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>