ListBox with Grid as ItemsPanelTemplate produces weird binding errors
Solution 1
The binding problem comes from the default style for ListBoxItem. By default when applying styles to elements WPF looks for the default styles and applies each property that is not specifically set in the custom style from the default style. Refer to this great blog post By Ian Griffiths for more details on this behavior.
Back to our problem. Here is the default style for ListBoxItem:
<Style
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
TargetType="{x:Type ListBoxItem}">
<Style.Resources>
<ResourceDictionary/>
</Style.Resources>
<Setter Property="Panel.Background">
<Setter.Value>
<SolidColorBrush>
#00FFFFFF
</SolidColorBrush>
</Setter.Value>
</Setter>
<Setter Property="Control.HorizontalContentAlignment">
<Setter.Value>
<Binding Path="HorizontalContentAlignment" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ItemsControl, AncestorLevel=1}"/>
</Setter.Value>
</Setter>
<Setter Property="Control.VerticalContentAlignment">
<Setter.Value>
<Binding Path="VerticalContentAlignment" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ItemsControl, AncestorLevel=1}"/>
</Setter.Value>
</Setter>
<Setter Property="Control.Padding">
<Setter.Value>
<Thickness>
2,0,0,0
</Thickness>
</Setter.Value>
</Setter>
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
...
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Note that I have removed the ControlTemplate to make it compact (I have used StyleSnooper - to retrieve the style). You can see that there is a binding with a relative source set to ancestor with type ItemsControl. So in your case the ListBoxItems that are created when binding did not find their ItemsControl. Can you provide more info with what is the ItemsSource for your ListBox?
P.S.: One way to remove the errors is to create new setters for HorizontalContentAlignment and VerticalContentAlignment in your custom Style.
Solution 2
Setting OverridesDefaultStyle
to True
in your ItemContainerStyle
will also fix these problems.
<Style TargetType="ListBoxItem">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<!-- set the rest of your setters, including Template, here -->
</Style>
Solution 3
This is an amalgam of the other answers here, but for me, I had to apply the Setter
in two places to solve the error, although this was when using a custom VirtualizingWrapPanel
If I remove either one of the below Setter
declarations, my errors reappear.
<ListView>
<ListView.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Top" />
</Style>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Top" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<controls:VirtualizingWrapPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
I don't really have the time to investigate further at the moment, but I suspect it's related to the default style that JTango mentions in his answer - I'm not really customising my template to a huge degree.
I think there's more mileage to be had out of the other answers, but I thought I'd post this on the off chance it helps someone in the same boat.
David Schmitt's answer looks like it might describe the root cause.
Solution 4
This is a common problem with ListBoxItem
s and other ephemeral *Item
containers. They are created asynchronously/on the fly, while the ItemsControl
is loaded/rendered. You have to attach to ListBox.ItemContainerGenerator
's StatusChanged
event and wait for the Status to become ItemsGenerated
before trying to access them.
Solution 5
I had the same problem as you and I just wanted to share what was my solution. I have tried all options from this post but the last one was the best for me - thx Chris.
So my code:
<ListBox.Resources>
<Style x:Key="listBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="MinWidth" Value="24"/>
<Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
</Style>
<Style TargetType="ListBoxItem" BasedOn="{StaticResource listBoxItemStyle}"/>
</ListBox.Resources>
<ListBox.ItemContainerStyle>
<Binding Source="{StaticResource listBoxItemStyle}"/>
</ListBox.ItemContainerStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" IsItemsHost="True" MaxWidth="170"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
I have also discovered that this bug do not appear when custom ItemsPanelTemplate
do not exists.
Tushee
Full-stack Software Architect and Developer in C#, .NET, Javascript, Typescript, AngularJS, Angular .NET, JADE, related web technologies. SOreadytohelp
Updated on December 18, 2020Comments
-
Tushee over 3 years
I've got a ListBox control and I'm presenting a fixed number of ListBoxItem objects in a grid layout. So I've set my ItemsPanelTemplate to be a Grid.
I'm accessing the Grid from code behind to configure the RowDefinitions and ColumnDefinitions.
So far it's all working as I expect. I've got some custom IValueConverter implementations for returning the Grid.Row and Grid.Column that each ListBoxItem should appear in.
However I get weird binding errors sometimes, and I can't figure out exactly why they're happening, or even if they're in my code.
Here's the error I get:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''. BindingExpression:Path=HorizontalContentAlignment; DataItem=null; target element is 'ListBoxItem' (Name=''); target property is 'HorizontalContentAlignment' (type 'HorizontalAlignment')
Can anybody explain what's going on?
Oh, and, here's my XAML:
<UserControl.Resources> <!-- Value Converters --> <v:GridRowConverter x:Key="GridRowConverter" /> <v:GridColumnConverter x:Key="GridColumnConverter" /> <v:DevicePositionConverter x:Key="DevicePositionConverter" /> <v:DeviceBackgroundConverter x:Key="DeviceBackgroundConverter" /> <Style x:Key="DeviceContainerStyle" TargetType="{x:Type ListBoxItem}"> <Setter Property="FocusVisualStyle" Value="{x:Null}" /> <Setter Property="Background" Value="Transparent" /> <Setter Property="Grid.Row" Value="{Binding Path=DeviceId, Converter={StaticResource GridRowConverter}}" /> <Setter Property="Grid.Column" Value="{Binding Path=DeviceId, Converter={StaticResource GridColumnConverter}}" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBoxItem}"> <Border CornerRadius="2" BorderThickness="1" BorderBrush="White" Margin="2" Name="Bd" Background="{Binding Converter={StaticResource DeviceBackgroundConverter}}"> <TextBlock FontSize="12" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Path=DeviceId, Converter={StaticResource DevicePositionConverter}}" > <TextBlock.LayoutTransform> <RotateTransform Angle="270" /> </TextBlock.LayoutTransform> </TextBlock> </Border> <ControlTemplate.Triggers> <Trigger Property="IsSelected" Value="true"> <Setter TargetName="Bd" Property="BorderThickness" Value="2" /> <Setter TargetName="Bd" Property="Margin" Value="1" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </UserControl.Resources> <Border CornerRadius="3" BorderThickness="3" Background="#FF333333" BorderBrush="#FF333333" > <Grid ShowGridLines="False"> <Grid.RowDefinitions> <RowDefinition Height="15" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <StackPanel Grid.Row="0" Orientation="Horizontal"> <Image Margin="20,3,3,3" Source="Barcode.GIF" Width="60" Stretch="Fill" /> </StackPanel> <ListBox ItemsSource="{Binding}" x:Name="lstDevices" Grid.Row="1" ItemContainerStyle="{StaticResource DeviceContainerStyle}" Background="#FF333333" SelectedItem="{Binding SelectedDeviceResult, ElementName=root, Mode=TwoWay}" > <ListBox.ItemsPanel> <ItemsPanelTemplate> <Grid> <Grid.LayoutTransform> <RotateTransform Angle="90" /> </Grid.LayoutTransform> </Grid> </ItemsPanelTemplate> </ListBox.ItemsPanel> </ListBox> </Grid> </Border>
-
Drew Noakes almost 15 yearsTrue, but it also caused my item to not present itself properly. Make sure that if you do this, you have enough of the style specified that you don't need anything from the default (see @ligaz's answer above)
-
paranoidduck about 14 years@JTango it helps; I did it for my CustromTreeViewItem object and I have no more exceptions like those described
-
cplotts almost 14 years+1 for the pointer to Ian Griffith's post. That is hands-down, one of the best descriptions on how elements get styled ... that I've ever read.
-
cplotts almost 14 yearsAlso, having a setter for HorizontalContentAlignment in my custom Style, did NOT seem to make a difference for me (this is for a ComboBoxItem though).
-
David over 13 years+1 for the whole answer, should be marked as the right answer, worked perfectly for me
-
Esben von Buchwald about 13 yearsThis solved an issue I had as well. In our controls we are completely re-styling them so we didn't want the default style used anyway. Didn't even know about this property of Style. Thank you!
-
akjoshi over 12 yearsblogged about this here - weblogs.asp.net/akjoshi/archive/2011/11/30/…
-
William over 11 yearsI tried setting HorizontalContentAlignment, VerticalContentAlignment, HorizontalAlignment, and VerticalAlignment for my ListBoxItem style and it failed. I think it has to do with the interaction between the ListBoxItem and the ControlTemplate's scrollviewer.
-
William over 11 yearsCan you elaborate on this? I think this is my issue. I'm trying to bind IsSelected property to my ListBoxItem via styling, but because of this exception thrown group selections are not toggled as expected when ListBox.SelectionMode=Extended. How would I go about intercepting the communication between the IsSelected Item and have it wait for the StatusChanged event to finish firing?
-
William over 11 yearsThis solves my problem too. I guess the bindings fire before the item is completly loaded. In my opinion, this is a bug, and as such I have filed it with MSDN (though I'm sure I'm not the first) and posted this as a workaround.
-
Meow Cat 2012 over 4 yearsThis hides my list items. To solve, add
BasedOn="{StaticResource {x:Type ListBoxItem}}"
to yourstyle
, right afterTargetType="ListBoxItem"
. I was using a customVirtualizingWrapPanel
. -
Meow Cat 2012 over 4 yearsWe should have been using the same
VirtualizingWrapPanel
and it is "different" somehow and makes the problem slightly variated. AddingBasedOn="{StaticResource {x:Type ListBoxItem}}"
forStyle
and<Setter Property="OverridesDefaultStyle" Value="True"/>
underStyle
, ofListView.ItemContainerStyle
also helps. -
Chris over 4 yearsGood tip, seems like there are some subtleties to understand.
-
Mike Williams over 2 yearsThanks this worked for us at Chem4Word