wpf - 绑定(bind) {RelativeSource PreviousData} 在特定情况下会中断绑定(bind)

标签 wpf data-binding relativesource

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

但是,当使用下面提供的特定代码时,绑定(bind)会在向上向下滚动几次和 时停止工作。一些Rectangle s 不见了。

即使使用单个 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 http://stackoverflow.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"
            };
        }
    }
}

最佳答案

由于PreviousData绑定(bind)与虚拟化不可靠,您可以通过设置 VirtualizingPanel.IsVirtualizing="False" 禁用虚拟化在 ListBox ,或者让您的绑定(bind)虚拟化准备就绪。

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

关于wpf - 绑定(bind) {RelativeSource PreviousData} 在特定情况下会中断绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44005571/

相关文章:

c# - 如何将单个 LINQ 对象绑定(bind)到 ASP.NET Repeater Control

ios - 如何使用 MvvmCross 和 Xamarin.iOS 将 MvxTableViewSource 绑定(bind)到动态创建的 ViewModel

silverlight - 在 Silverlight 中绑定(bind)到 RelativeSource Self

WPF ContextMenu 问题 : How do I set the DataContext of the ContextMenu?

c# - 如何在运行时更改数据模板?

android - lateinit 属性绑定(bind)尚未初始化

c# - 无法将可检查的 MenuItem 绑定(bind)到父窗口的 Topmost (始终位于顶部)属性

WPF : Binding Order

wpf - 如何处理 WPF 中的巨大 tif?

WPF - 风格中的相对源