2011-10-20 6 views
8

Devo modellare in modo diverso il primo e l'ultimo elemento di una visualizzazione elenco. Per raggiungere questo obiettivo, ho iniziato a lavorare su una soluzione basata su tale risposta: Use different template for last item in a WPF itemscontrolModello oggetto distinto per il primo e l'ultimo elemento in un ListView

Fondamentalmente, ho un oggetto ItemsTemplateSelector che decide il modello da applicare in base all'indice dell'oggetto negli elementi della vista elenco (codice di seguito).

Funziona correttamente se non che quando l'elenco viene aggiornato (un elemento viene aggiunto o rimosso), i modelli non vengano nuovamente selezionati (per esempio, inizialmente, il SingleItemTemplate viene scelto perché v'è un singolo elemento. Quando aggiungo un elemento all'elenco, il modello del primo elemento non viene passato a FirstItemTemplate). Come forzare la selezione del modello per tutti gli articoli?

public class FirstLastTemplateSelector : DataTemplateSelector 
{ 
    public DataTemplate DefaultTemplate { get; set; } 
    public DataTemplate FirstItemTemplate { get; set; } 
    public DataTemplate LastItemTemplate { get; set; } 
    public DataTemplate SingleItemTemplate { get; set; } 

    public override DataTemplate SelectTemplate(object item, DependencyObject container) 
    { 
     ListView lv = VisualTreeHelperEx.FindParentOfType<ListView>(container); 
     if (lv != null) 
     { 
      if (lv.Items.Count == 1) 
      { 
       return SingleItemTemplate; 
      } 

      int i = lv.Items.IndexOf(item); 
      if (i == 0) 
      { 
       return FirstItemTemplate; 
      } 
      else if (i == lv.Items.Count - 1) 
      { 
       return LastItemTemplate; 
      } 
     } 
     return DefaultTemplate; 
    } 
} 

risposta

14

Come approccio alternativo, vorrei suggerire vincolante la AlternationCount del vostro ItemsControl per il numero di elementi nella vostra collezione (ad esempio la proprietà Count). Questo assegnerà quindi a ciascun contenitore nel tuo ItemsControl un AlternationIndex univoco (0, 1, 2, ... Count-1). Vedi qui per maggiori informazioni:

http://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.alternationcount.aspx

Una volta ogni contenitore ha un unico AlternationIndex è possibile utilizzare un DataTrigger nel vostro contenitore Style per impostare il ItemTemplate in base al largo dell'indice. Questo potrebbe essere fatto utilizzando un MultiBinding con un convertitore che restituisce True se l'indice è uguale al conteggio, False in caso contrario. Ovviamente si potrebbe anche costruire un selezionatore attorno a questo approccio. Ad eccezione del convertitore, questo approccio è buono poiché è una soluzione solo XAML.

Un esempio utilizzando un ListBox:

<Window x:Class="WpfApplication4.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:Collections="clr-namespace:System.Collections;assembly=mscorlib" 
     xmlns:System="clr-namespace:System;assembly=mscorlib" 
     xmlns:l="clr-namespace:WpfApplication4" 
     Title="MainWindow" Height="350" Width="525"> 
    <Grid> 
     <Grid.Resources> 
      <Collections:ArrayList x:Key="MyCollection"> 
       <System:String>Item One</System:String> 
       <System:String>Item Two</System:String> 
       <System:String>Item Three</System:String> 
      </Collections:ArrayList> 

      <l:MyAlternationEqualityConverter x:Key="MyAlternationEqualityConverter" /> 

      <Style x:Key="MyListBoxItemStyle" TargetType="{x:Type ListBoxItem}"> 
       <Style.Triggers> 
        <DataTrigger Value="True"> 
         <DataTrigger.Binding> 
          <MultiBinding Converter="{StaticResource MyAlternationEqualityConverter}"> 
           <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ListBox}}" Path="Items.Count" /> 
           <Binding RelativeSource="{RelativeSource Self}" Path="(ItemsControl.AlternationIndex)" /> 
          </MultiBinding> 
         </DataTrigger.Binding> 
         <!-- Could set the ItemTemplate instead --> 
         <Setter Property="Background" Value="Red"/> 
        </DataTrigger> 
       </Style.Triggers> 
      </Style> 
     </Grid.Resources> 

     <ListBox ItemsSource="{Binding Source={StaticResource MyCollection}}" 
       AlternationCount="{Binding RelativeSource={RelativeSource Self}, Path=Items.Count}" 
       ItemContainerStyle="{StaticResource MyListBoxItemStyle}" /> 
    </Grid> 

Qualora il convertitore potrebbe essere simile:

class MyAlternationEqualityConverter : IMultiValueConverter 
{ 
    #region Implementation of IMultiValueConverter 

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 
     if (values != null && values.Length == 2 && 
      values[0] is int && values[1] is int) 
     { 
      return Equals((int) values[0], (int) values[1] + 1); 
     } 

     return DependencyProperty.UnsetValue; 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    { 
     throw new NotSupportedException(); 
    } 

    #endregion 
} 
+0

Mi chiedo il motivo per cui non si è confrontato con DataTrigger di Binding per ItemsCount e Valore su AlternationCount? È perché ItemCount non è una proprietà di dipendenza e genererà un errore? O c'è qualche altra ragione? Perché vedo che sarà un booleano uguale al confronto e perché abbiamo bisogno di un convertitore – CarbineCoder

+0

@ ramb00 Ho dato una prova e ottenuto: "A 'Binding' non può essere impostato sulla proprietà 'Valore' di tipo 'DataTrigger'" . Sembra che Value non sia una dependencyProp. –

Problemi correlati