Binding {RelativeSource PreviousData} нарушает привязку в конкретном случае

Я пытался использовать {RelativeSource PreviousData} в ListBox.ItemTemplate и это сработало правильно.

Но при использовании приведенного ниже кода привязка перестает работать при прокрутке вниз несколько раз, и некоторые из Rectangleс не хватает.

Проблема воспроизводится даже при использовании одного DataTrigger, но это не реконструирует когда ListBox.Height больше 178

Пример GIF - Зеленые линии отсутствуют!:

Обычная прокрутка работает, но быстрая прокрутка приводит к тому, что она становится привязанной


Источник MainWindow.Xaml:

<Window
    x:Class="PreviousDataBindingWheelIssue.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:PreviousDataBindingWheelIssue"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="PreviousData Issue"
    d:DataContext="{d:DesignInstance Type=local:MyModel}"
    SizeToContent="WidthAndHeight"
    mc:Ignorable="d">
    <StackPanel>

        <!--  Height must be less or equal to 178  -->
        <ListBox
            Width="300"
            Height="178"
            HorizontalContentAlignment="Stretch"
            ItemsSource="{Binding MyData}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <DockPanel Background="#FFFFFFED">
                        <Rectangle
                            Height="2"
                            Margin="0"
                            DockPanel.Dock="Top">
                            <Rectangle.Style>
                                <Style TargetType="Rectangle">
                                    <Setter Property="Fill" Value="#FF63605C" />
                                    <Style.Triggers>

                                        <!--
                                            Hide our magnificent separator if this is the first item on the list
                                            see http://stackru.com/a/22705507/426315
                                            but, it seems to have some issues when using mouse wheel
                                            some of the rows does NOT have the rectangle even when PreviousData SHOULD not be null
                                        -->
                                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource PreviousData}}" Value="{x:Null}">
                                            <Setter Property="Visibility" Value="Collapsed" />
                                        </DataTrigger>

                                        <DataTrigger Binding="{Binding}" Value="Fun Item">
                                            <Setter Property="Fill" Value="SpringGreen" />
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </Rectangle.Style>
                        </Rectangle>

                        <TextBlock
                            Margin="5,7,5,7"
                            VerticalAlignment="Center"
                            FontSize="12"
                            Text="{Binding}" />
                    </DockPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>
</Window>

Код основного окна позади:

using System.Windows;
namespace PreviousDataBindingWheelIssue
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MyModel();
        }
    }
}

MyModel.cs источник:

using System.Collections.ObjectModel;

namespace PreviousDataBindingWheelIssue
{
    public class MyModel
    {
        public ObservableCollection<string> MyData { get; set; }

        public MyModel()
        {
            MyData = new ObservableCollection<string>()
            {
                "Lorem ipsum dolor", "sit amet, consectetur", "adipiscing elit. Sed",
                "Fun Item",
                "rhoncus leo convallis", "pulvinar tellus at",
                "Fun Item",
                "porta metus. Mauris", "sed mauris quis", "neque congue semper",
                "Fun Item",
                "vitae non leo", "Donec aliquet feugiat", "massa vitae luctus",
                "Fun Item",
                "Duis pharetra velit", "et lorem blandit"
            };
        }
    }
}

1 ответ

Решение

Так как PreviousData привязка не надежна с виртуализацией, вы можете отключить виртуализацию, установив VirtualizingPanel.IsVirtualizing="False" на ListBoxили подготовьте виртуализацию привязок.

Один из способов справиться с такой проблемой - создать собственный список (ListBox2 в моем примере кода), переопределить PrepareContainerForItemOverride и установить некоторые свойства, которые могут быть использованы для дальнейших операций. Я создаю прикрепленное свойство ItemIndex для этого.

public class ListBox2 : ListBox
{
    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
    {
        base.PrepareContainerForItemOverride(element, item);
        SetItemIndex(element, ItemContainerGenerator.IndexFromContainer(element));
    }


    // helper attached property to indicate the index of listbox items

    public static int GetItemIndex(DependencyObject obj)
    {
        return (int)obj.GetValue(ItemIndexProperty);
    }

    protected static void SetItemIndex(DependencyObject obj, int value)
    {
        obj.SetValue(ItemIndexPropertyKey, value);
    }

    private static readonly DependencyPropertyKey ItemIndexPropertyKey =
        DependencyProperty.RegisterAttachedReadOnly("ItemIndex", typeof(int), typeof(ListBox2), new PropertyMetadata(-1));

    public static readonly DependencyProperty ItemIndexProperty = ItemIndexPropertyKey.DependencyProperty;
}

Затем измените XAML для использования ListBox2 и полагаться на ItemIndex вместо PreviousData:

<local:ListBox2
    Width="300"
    Height="178"
    HorizontalContentAlignment="Stretch"
    ItemsSource="{Binding MyData}">
    <local:ListBox2.ItemTemplate>
        <DataTemplate>
            <DockPanel Background="#FFFFFFED">
                <Rectangle
                    Height="2"
                    Margin="0"
                    DockPanel.Dock="Top">
                    <Rectangle.Style>
                        <Style TargetType="Rectangle">
                            <Setter Property="Fill" Value="#FF63605C" />
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding Path=(local:ListBox2.ItemIndex),RelativeSource={RelativeSource AncestorType=ListBoxItem}}" Value="0">
                                    <Setter Property="Visibility" Value="Collapsed" />
                                </DataTrigger>

                                <DataTrigger Binding="{Binding}" Value="Fun Item">
                                    <Setter Property="Fill" Value="SpringGreen" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </Rectangle.Style>
                </Rectangle>

                <TextBlock
                    Margin="5,7,5,7"
                    VerticalAlignment="Center"
                    FontSize="12"
                    Text="{Binding}" />
            </DockPanel>
        </DataTemplate>
    </local:ListBox2.ItemTemplate>
</local:ListBox2>
Другие вопросы по тегам