使用 Grid 作为 ItemsPanelTemplate 的 ListBox 会产生奇怪的绑定错误
我有一个 ListBox 控件,并且在网格布局中呈现固定数量的 ListBoxItem 对象。 所以我将 ItemsPanelTemplate 设置为网格。
我从后面的代码访问网格以配置 RowDefinitions 和 ColumnDefinitions。
到目前为止,一切都按我的预期进行。 我有一些自定义 IValueConverter 实现,用于返回每个 ListBoxItem 应出现在其中的 Grid.Row 和 Grid.Column。
但是有时我会遇到奇怪的绑定错误,并且我无法确切地弄清楚它们发生的原因,或者即使它们在我的代码中。
这是我得到的错误:
System.Windows.Data 错误:4:无法找到与引用“RelativeSource FindAncestor、AncestorType='System.Windows.Controls.ItemsControl”、AncestorLevel='1'' 进行绑定的源。 BindingExpression:Path=HorizontalContentAlignment; 数据项=空; 目标元素是“ListBoxItem”(名称=“”); 目标属性是“HorizontalContentAlignment”(类型“HorizontalAlignment”)
任何人都可以解释发生了什么吗?
哦,还有,这是我的 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>
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>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
根据 MSDN 上的数据模板概述,
DataTemplates< /code> 应该用作
ItemTemplate
来定义数据的呈现方式,而Style
将用作ItemContainerStyle
来设置样式生成的容器,例如ListBoxItem
。但是,您似乎正在尝试使用后者来完成前者的工作。 如果没有更多代码,我无法重新创建您的情况,但我怀疑以容器样式进行数据绑定可能会破坏假定的视觉/逻辑树。
我也忍不住认为基于项目信息的自定义项目布局需要创建自定义
Panel
。 使用自定义Panel
来布局项目可能比使用 Rube Goldberg 分类的IValueConverters
来布局项目更好。According to the Data Templating Overview on MSDN,
DataTemplates
should be used as theItemTemplate
to define how the data is presented, while aStyle
would be used as theItemContainerStyle
to style just the generated container, such asListBoxItem
.However, it appears that you are trying to use the latter to do the job of the former. I can't recreate your situation without much more code, but I suspect that doing databinding in the container style could be throwing a wrench in the assumed visual/logical tree.
I also can't help but think that a custom layout of items based on the item's information calls for creating a custom
Panel
. It's probably better for the customPanel
to layout the items than for the items to lay themselves out with a Rube Goldberg assortment ofIValueConverters
.对我有用的另一个解决方法/解决方案是通过在类或顶级窗口的构造函数中将数据绑定源开关级别设置为关键来抑制这些错误(实际上,将它们称为警告似乎更合适) -
参考:< a href="http://www.codeproject.com/Tips/124556/How-to-suppress-the-System-Windows-Data-Error-warn" rel="nofollow noreferrer">如何抑制 System.Windows .数据错误警告消息
Another workaround/solution that worked for me was to suppress these errors (actually, it seems more appropriate to call them warnings) by setting the data binding source switch level as critical in constructor of the class or a top level window -
Ref.: How to suppress the System.Windows.Data Error warning message
绑定问题来自于ListBoxItem的默认样式。 默认情况下,当将样式应用于元素时,WPF 会查找默认样式并应用默认样式中未在自定义样式中专门设置的每个属性。 请参阅 Ian Griffiths 撰写的这篇精彩的博客文章了解更多信息有关此行为的详细信息。
回到我们的问题。 这是 ListBoxItem 的默认样式:
请注意,我已删除 ControlTemplate 以使其紧凑(我使用了 StyleSnooper< /a> - 检索样式)。 您可以看到有一个绑定,其相对源设置为 ItemsControl 类型的祖先。 因此,在您的情况下,绑定时创建的 ListBoxItems 找不到其 ItemsControl。 您能否提供有关列表框的 ItemsSource 的更多信息?
PS:消除错误的一种方法是在自定义样式中为 HorizontalContentAlignment 和 VerticalContentAlignment 创建新的设置器。
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:
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.
在
ItemContainerStyle
中将OverridesDefaultStyle
设置为True
也可以解决这些问题。Setting
OverridesDefaultStyle
toTrue
in yourItemContainerStyle
will also fix these problems.这是 ListBoxItem 的常见问题 code> 和其他临时
*Item
容器。 它们是在加载/渲染ItemsControl
时异步/动态创建的。 您必须附加到ListBox。 ItemContainerGenerator
的StatusChanged
事件并等待 Status 变为ItemsGenerated
,然后再尝试访问它们。This is a common problem with
ListBoxItem
s and other ephemeral*Item
containers. They are created asynchronously/on the fly, while theItemsControl
is loaded/rendered. You have to attach toListBox.ItemContainerGenerator
'sStatusChanged
event and wait for the Status to becomeItemsGenerated
before trying to access them.这是这里其他答案的混合体,但对我来说,我必须在两个地方应用
Setter
来解决错误,尽管这是在使用自定义VirtualizingWrapPanel
时如果我删除以下任一
Setter
声明,我的错误就会再次出现。我目前确实没有时间进一步调查,但我怀疑这与 JTango 在他的答案中提到的默认样式有关 - 我并没有真正在很大程度上自定义我的模板。
我认为其他答案有更多的意义,但我想我会发布这个,以防它对有同样想法的人有帮助。
大卫·施密特的回答看起来可能描述了根本原因。
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 customVirtualizingWrapPanel
If I remove either one of the below
Setter
declarations, my errors reappear.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.
我刚刚遇到了同样类型的错误:
在进行如下绑定时发生这种情况:
对于我的数据上下文对象上的此属性:
经过一些实验,我发现仅当项目数量超过可见高度时才会触发错误我的列表框(例如,当出现垂直滚动条时)。
所以我立即想到了虚拟化并尝试了这个:
这为我解决了问题。
尽管我更愿意保持虚拟化处于开启状态,但我没有再花时间去深入研究它。
我的应用程序有点复杂,有多层网格、停靠面板等以及一些异步方法调用。
我无法在更简单的应用程序中重现该问题。
I just encountered the same type of error:
This happened while doing a binding like this:
To this property on my data context object:
After some experimenting I discovered that the error was only triggered when the number of items exceeded the visible height of my ListBox (e.g. when vertical scrollbars appear).
So I immediately thought about virtualization and tried this:
This solved the problem for me.
Although I would prefer to keep virtualization turned on I did not use any more time to dive into it.
My application is a bit on the complex side with mulitiple levels of grids, dock panels etc. and some asynch method calls.
I was not able to reproduce the problem in a simpler application.
这对我有用。 将其放入您的 Application.xaml 文件中。
来自...
http://social.msdn.microsoft .com/Forums/en-US/wpf/thread/42cd1554-de7a
This worked for me. Put this in your Application.xaml file.
from...
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/42cd1554-de7a
我和你遇到了同样的问题,我只是想分享我的解决方案。
我已经尝试了这篇文章中的所有选项,但最后一个对我来说是最好的 - 谢谢克里斯。
所以我的代码:
我还发现,当自定义
ItemsPanelTemplate
不存在时,不会出现此错误。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:
I have also discovered that this bug do not appear when custom
ItemsPanelTemplate
do not exists.如果您想完全替换
ListBoxItem
模板,以便没有任何选择可见(也许您希望ItemsControl
的外观具有ListBox
的分组/等行为code>) 那么您可以使用此样式:此模板还排除了标准
Border
包装器。 如果需要,可以使用以下内容替换模板:如果不需要所有这些
TemplateBinding
值,则可以删除一些值以提高性能。If you want to completely replace the
ListBoxItem
template such that no selection is visible (perhaps you want the look ofItemsControl
with the grouping/etc behaviour ofListBox
) then you can use this style:This template also excludes the standard
Border
wrapper. If you need that, you can use replace the template with this:If you don't need all these
TemplateBinding
values then you can remove some for performance.简单地为“ComboBoxItem”类型创建默认样式是行不通的,因为它会被 ComboBox 的默认“ItemContainerStyle”覆盖。 要真正摆脱这个问题,您需要更改 ComboBox 的默认“ItemContainerStyle”,如下所示:
Simply creating a default style for the type "ComboBoxItem" doesn't work, because it it overwritten by the ComboBox's default "ItemContainerStyle". To really get rid of this, you need to change the default "ItemContainerStyle" for ComboBoxes, like this:
我开始遇到这个问题,即使我的 ListBox 同时设置了 Style 和 ItemContainerStyle - 并且这些命名样式已经定义了 HorizontalContentAlignment。 我使用 CheckBox 控件来打开/关闭 ListBox 上的实时过滤,这似乎导致项目从默认样式而不是我指定的样式中提取。 大多数错误会在实时过滤第一次启动时发生,但此后每次更改都会继续抛出 2 个错误。 我发现有趣的是,我的收藏中恰好有 2 条记录是空的,因此项目中没有任何内容可显示。 所以这似乎有所贡献。 我计划创建当记录为空时显示的默认数据。
卡特的建议对我有用。 添加一个没有键的单独“默认”样式和定义 HorizontalContentAlignment 属性的 TargetType="ListBoxItem" 解决了该问题。 我什至不需要为其设置 OverridesDefaultStyle 属性。
I started running into this problem, even though my ListBox had both a Style and an ItemContainerStyle set - and these named styles had already defined HorizontalContentAlignment. I was using CheckBox controls to turn on/off live filtering on my ListBox and this seemed to be causing the items to pull instead from the default style instead of my assigned styles. Most errors would occur the first time the live filtering kicked in, but thereafter it would continue to throw 2 errors on each change. I find it interesting that exactly 2 records in my collection were empty and thus had nothing to display in the item. So this seems to have contibuted. I plan to create default data to be displayed when a record is empty.
Carter's suggestion worked for me. Adding a separate "default" style with no key and a TargetType="ListBoxItem" that defined the HorizontalContentAlignment property solved the problem. I didn't even need to set the OverridesDefaultStyle property for it.