使用{RelativeSource PreviousData}绑定在特定情况下会导致绑定中断。

4

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

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

即使只使用单个DataTrigger,问题也会重现,但是当ListBox.Height大于178时,不会重现。

示例GIF - 绿色线条缺失!:

Regular scroll works, but fast scroll cause it binding to 'loose-it'


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 https://dev59.com/HH7aa4cB1Zd3GeqPmBwV#22705507
                                            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>

MainWindow代码后台:

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个回答

3

由于虚拟化会影响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>

谢谢,确实非常有趣。您能否详细说明一下“使用虚拟化时,PreviousData绑定不可靠”的这句话的含义? - itsho
1
@itsho 抱歉,这更多是一种实践探索(关闭虚拟化-它可以工作,打开它,相对绑定具有不适当的更新触发器并产生错误值)。我没有找到任何权威来源以有用的方式解决这个问题。 - grek40

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接