绑定 {RelativeSource PreviousData} 在特定情况下会破坏绑定

Binding {RelativeSource PreviousData} breaks binding in specific case

我尝试在 ListBox.ItemTemplate 中使用 {RelativeSource PreviousData},它工作正常。

但是,当使用下面提供的特定代码时,向上和向下滚动几次时绑定会停止工作,并且 Rectangle 中的 一些 会丢失。

即使使用单个 DataTrigger,问题也会重现,但当 ListBox.Height 大于 178[ 时,它 不会 重建=39=].

示例 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 
                                            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"
            };
        }
    }
}

由于 PreviousData 绑定对于虚拟化不可靠,您可以通过在 ListBox 上设置 VirtualizingPanel.IsVirtualizing="False" 来禁用虚拟化,或者让您的绑定虚拟化就绪。

处理此类问题的一种方法是创建一个自定义列表框(在我的示例代码中为 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>