I tried to use {RelativeSource PreviousData}
in a ListBox.ItemTemplate
and it worked correctly.
But, when using the specific code provided below, binding stops working when scrolling up an down few times and some of the Rectangle
s are missing.
The issue reproduce even when using a single DataTrigger
, but it does not reconstruct when ListBox.Height
is more than 178.
<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();
}
}
}
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"
};
}
}
}
Since the PreviousData
binding is not reliable with virtualization, you can either disable virtualization by setting VirtualizingPanel.IsVirtualizing="False"
on ListBox
, or make your bindings virtualization ready.
One way to deal with such an issue is to create a custom listbox (ListBox2
in my sample code), override PrepareContainerForItemOverride
and set some property that can be used for further operations. I create an attached property ItemIndex
for this purpose.
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;
}
Then change the xaml to use ListBox2
and rely on the ItemIndex
instead of 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>