Отличительный шаблон элемента для первого и последнего элемента в ListView - PullRequest
10 голосов
/ 20 октября 2011

Мне нужно по-разному стилизовать первый и последний элементы представления списка. Чтобы добиться этого, я начал работать над решением на основе этого ответа: Использовать другой шаблон для последнего элемента в элементе управления WPF

По сути, у меня есть пользовательский ItemsTemplateSelector, который выбирает шаблон для применения на основе индекса элемента в элементах представления списка (код ниже).

Он работает правильно, за исключением того, что когда список обновляется (элемент добавляется или удаляется), шаблоны не выбираются снова (например, первоначально, SingleItemTemplate выбирается, потому что есть один элемент. элемент списка, шаблон первого элемента не переключается на FirstItemTemplate). Как заставить выбор шаблона для всех элементов?

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

1 Ответ

16 голосов
/ 20 октября 2011

В качестве альтернативного подхода я бы предложил связать AlternationCount вашего ItemsControl с количеством предметов в вашей коллекции (например, свойство Count).Затем каждый контейнер в вашем ItemsControl назначит уникальный AlternationIndex (0, 1, 2, ... Count-1).Смотрите здесь для получения дополнительной информации:

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

Как только каждый контейнер имеет уникальный AlternationIndex, вы можете использовать DataTrigger в своем контейнере Style, чтобы установить ItemTemplateвне индекса.Это можно сделать с помощью MultiBinding с преобразователем, который возвращает True, если индекс равен счетчику, False в противном случае.Конечно, вы также можете создать селектор вокруг этого подхода.За исключением конвертера, этот подход хорош, поскольку это решение только для XAML.

Пример использования 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>

Где конвертер может выглядеть примерно так:

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