2011-10-20 12 views
8

Muszę stylować pierwszy i ostatni element widoku listy inaczej. Aby to osiągnąć, zacząłem pracować nad rozwiązaniem opartym na tej odpowiedzi: Use different template for last item in a WPF itemscontrolOdróżniający się szablon pozycji dla pierwszego i ostatniego elementu w ListView

Zasadniczo mam niestandardowy ItemsTemplateSelector, który decyduje o szablonie do zastosowania na podstawie indeksu pozycji w elementach widoku listy (kod poniżej).

Działa poprawnie, z wyjątkiem sytuacji, gdy lista jest aktualizowana (element jest dodawany lub usuwany), szablony nie są wybierane ponownie (na przykład początkowo wybierany jest obiekt SingleItemTemplate, ponieważ istnieje pojedynczy element. element do listy, szablon pierwszego elementu nie zostanie przełączony na FirstItemTemplate). Jak wymusić wybór szablonu dla wszystkich przedmiotów?

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; 
    } 
} 

Odpowiedz

14

Jako alternatywne podejście, chciałbym zaproponować wiązania AlternationCount twoich ItemsControl do liczby elementów w kolekcji (na przykład nieruchomości Count). To następnie przypisze każdemu kontenerowi w swoim ItemsControl unikalny AlternationIndex (0, 1, 2, ... Count-1). Zobacz tutaj po więcej informacji:

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

Raz każdy pojemnik posiada unikalny AlternationIndex można użyć DataTrigger w pojemniku Style ustawić ItemTemplate oparciu off indeksu. Można to zrobić, korzystając z MultiBinding z konwerterem, który zwraca True, jeśli indeks jest równy liczbie, w innym przypadku jest to False. Oczywiście można również zbudować selektor wokół tego podejścia. Z wyjątkiem konwertera, takie podejście jest dobre, ponieważ jest to rozwiązanie wyłącznie dla XAML.

Przykładem użyciem 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> 

Jeżeli przetwornica może wyglądać:

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

Zastanawiam się, dlaczego nie porównano za pomocą DataTrigger wiązanie do ItemsCount i Wartość do AlternationCount? Czy to dlatego, że ItemCount nie jest właściwością zależności i spowoduje błąd? Czy jest jakiś inny powód? Ponieważ widzę, że będzie to wartość boolowska porównywalna do porównania i dlaczego potrzebujemy konwertera – CarbineCoder

+0

@ ramb00 Spróbowałem i otrzymałem: "Powiązanie A" nie może być ustawione we właściwości "Wartość" typu "DataTrigger" " . Wygląda na to, że wartość nie jest zależnościąProp. –

Powiązane problemy