Câu hỏi Mẫu mục riêng biệt cho mục đầu tiên và cuối cùng trong một ListView


Tôi cần phải tạo kiểu cho các mục đầu tiên và cuối cùng của chế độ xem danh sách khác nhau. Để đạt được điều đó, tôi bắt đầu làm việc trên một giải pháp dựa trên câu trả lời đó: Sử dụng mẫu khác nhau cho mục cuối cùng trong một mục WPF itemscontrol

Về cơ bản, tôi có một ItemsTemplateSelector tùy chỉnh quyết định mẫu để áp dụng dựa trên chỉ mục của mục trong các mục danh sách xem (mã bên dưới).

Nó hoạt động đúng cách ngoại trừ khi danh sách được cập nhật (một mục được thêm vào hoặc loại bỏ), các mẫu không được chọn lại (ví dụ, ban đầu, SingleItemTemplate được chọn vì có một mục duy nhất. Khi tôi thêm một mục vào danh sách, mẫu của mục đầu tiên không được chuyển sang FirstItemTemplate). Làm cách nào để buộc chọn mẫu cho tất cả các mục?

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

8
2017-10-20 11:04


gốc




Các câu trả lời:


Là một cách tiếp cận thay thế, tôi sẽ đề xuất ràng buộc AlternationCount của bạn ItemsControl số lượng mục trong bộ sưu tập của bạn (ví dụ: Count bất động sản). Điều này sau đó sẽ gán cho mỗi vùng chứa trong ItemsControl duy nhất AlternationIndex (0, 1, 2, ... Đếm-1). Xem ở đây để biết thêm thông tin:

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

Khi mỗi vùng chứa có duy nhất AlternationIndex bạn có thể sử dụng DataTrigger trong thùng chứa của bạn Style  để đặt ItemTemplate dựa trên chỉ mục. Điều này có thể được thực hiện bằng cách sử dụng MultiBinding với công cụ chuyển đổi trả về True nếu chỉ số bằng số, False nếu không thì. Tất nhiên bạn cũng có thể xây dựng một bộ chọn xung quanh phương pháp này. Ngoại trừ bộ chuyển đổi, cách tiếp cận này là tốt đẹp vì nó là một giải pháp duy nhất XAML.

Ví dụ sử dụng 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>

Nơi bộ chuyển đổi có thể trông giống như sau:

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
}

14
2017-10-20 11:28



Tôi tự hỏi tại sao bạn không so sánh bằng cách sử dụng Datatrigger's Binding để ItemsCount và Value to AlternationCount? Có phải vì ItemCount không phải là một thuộc tính phụ thuộc và nó sẽ ném lỗi? Hoặc có lý do nào khác không? Bởi vì tôi thấy nó sẽ là một boolean bằng so sánh và tại sao chúng ta cần một công cụ chuyển đổi - Carbine
@ ramb00 Tôi đã thử và nhận: "A 'Binding" không thể được đặt trên thuộc tính' Giá trị 'của loại' DataTrigger '". Có vẻ như Value không phải là một dependencyProp. - Bill Tarbell