.net 带有 Grid 作为 ItemsPanelTemplate 的 ListBox 会产生奇怪的绑定错误

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/160391/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 10:12:37  来源:igfitidea点击:

ListBox with Grid as ItemsPanelTemplate produces weird binding errors

.netwpfdata-bindinglistboxitemspaneltemplate

提问by Craig Shearer

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.

我有一个 ListBox 控件,并且在网格布局中呈现固定数量的 ListBoxItem 对象。所以我已经将我的 ItemsPanelTemplate 设置为一个网格。

I'm accessing the Grid from code behind to configure the RowDefinitions and ColumnDefinitions.

我正在从后面的代码访问网格以配置 RowDefinitions 和 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.

到目前为止,一切都按我的预期工作。我有一些自定义的 IValueConverter 实现,用于返回每个 ListBoxItem 应该出现的 Grid.Row 和 Grid.Column。

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')

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:

哦,还有,这是我的 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>

回答by ligaz

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 postBy Ian Griffiths for more details on this behavior.

绑定问题来自 ListBoxItem 的默认样式。默认情况下,将样式应用于元素时,WPF 会查找默认样式并应用默认样式中未在自定义样式中专门设置的每个属性。有关此行为的更多详细信息,请参阅Ian Griffiths 撰写的这篇很棒的博客文章

Back to our problem. Here is the default style for ListBoxItem:

回到我们的问题。下面是 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?

请注意,我已删除 ControlTemplate 以使其紧凑(我已使用StyleSnooper- 来检索样式)。您可以看到有一个绑定,其相对源设置为 ItemsControl 类型的祖先。因此,在您的情况下,绑定时创建的 ListBoxItems 没有找到它们的 ItemsControl。您能否提供有关 ListBox 的 ItemsSource 的更多信息?

P.S.: One way to remove the errors is to create new setters for HorizontalContentAlignment and VerticalContentAlignment in your custom Style.

PS:消除错误的一种方法是在自定义样式中为 Horizo​​ntalContentAlignment 和 VerticalContentAlignment 创建新的设置器。

回答by JTango18

Setting OverridesDefaultStyleto Truein your ItemContainerStylewill also fix these problems.

设置OverridesDefaultStyleTrue你的ItemContainerStyle意志也解决这些问题。

<Style TargetType="ListBoxItem">
    <Setter Property="OverridesDefaultStyle" Value="True"/>
    <!-- set the rest of your setters, including Template, here -->
</Style>

回答by Chris

This is an amalgam of the other answers here, but for me, I had to apply the Setterin two places to solve the error, although this was when using a custom VirtualizingWrapPanel

这是此处其他答案的合并,但对我而言,我不得不Setter在两个地方应用 来解决错误,尽管这是在使用自定义时VirtualizingWrapPanel

If I remove either one of the below Setterdeclarations, my errors reappear.

如果我删除以下任一Setter声明,我的错误会再次出现。

        <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.

目前我真的没有时间进一步调查,但我怀疑这与 JTango 在他的回答中提到的默认样式有关 - 我并没有真正在很大程度上自定义我的模板。

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.

大卫施密特的回答看起来可能描述了根本原因。

回答by David Schmitt

This is a common problemwith ListBoxItems and other ephemeral *Itemcontainers. They are created asynchronously/on the fly, while the ItemsControlis loaded/rendered. You have to attach to ListBox.ItemContainerGenerator's StatusChangedevent and wait for the Status to become ItemsGeneratedbefore trying to access them.

这是一个常见的问题ListBoxItemS和其他短暂的*Item容器。它们是在ItemsControl加载/渲染时异步/动态创建的。在尝试访问它们之前,您必须附加到ListBox.ItemContainerGeneratorStatusChanged事件并等待状态变为ItemsGenerated

回答by RedQueen87

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 ItemsPanelTemplatedo not exists.

我还发现自定义ItemsPanelTemplate不存在时不会出现此错误。

回答by SteffenSH

I just encountered the same type of error:

我刚刚遇到了相同类型的错误:

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')

System.Windows.Data 错误:4:无法找到引用“RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''的绑定源。BindingExpression:Path=Horizo​​ntalContentAlignment; 数据项=空;目标元素是 'ListBoxItem' (Name=''); 目标属性是“Horizo​​ntalContentAlignment”(类型“Horizo​​ntalAlignment”)

This happened while doing a binding like this:

这发生在做这样的绑定时:

<ListBox ItemsSource="{Binding Path=MyListProperty}"  />

To this property on my data context object:

对于我的数据上下文对象上的此属性:

public IList<ListBoxItem> MyListProperty{ get; set;}

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:

经过一些实验,我发现只有当项目数量超过我的 ListBox 的可见高度时才会触发错误(例如,当出现垂直滚动条时)。所以我立即想到了虚拟化并尝试了这个:

<ListBox ItemsSource="{Binding Path=MyListProperty}" VirtualizingStackPanel.IsVirtualizing="False" />

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.

这为我解决了这个问题。虽然我更愿意保持虚拟化开启,但我没有再花时间深入研究它。我的应用程序有点复杂,有多层网格、停靠面板等和一些异步方法调用。我无法在更简单的应用程序中重现该问题。

回答by akjoshi

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 -

另一个对我有用的解决方法/解决方案是通过在类的构造函数或顶级窗口中将数据绑定源开关级别设置为关键来抑制这些错误(实际上,将它们称为警告似乎更合适) -

#if DEBUG     
    System.Diagnostics.PresentationTraceSources.DataBindingSource.Switch.Level =
        System.Diagnostics.SourceLevels.Critical;
#endif

Ref.: How to suppress the System.Windows.Data Error warning message

参考:如何抑制 System.Windows.Data 错误警告消息

回答by Carter Medlin

This worked for me. Put this in your Application.xaml file.

这对我有用。把它放在你的 Application.xaml 文件中。

<Application.Resources>
    <Style TargetType="ListBoxItem">
        <Setter Property="HorizontalContentAlignment" Value="Left" />
        <Setter Property="VerticalContentAlignment" Value="Center" />
    </Style>
</Application.Resources>

from...

从...

http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/42cd1554-de7a

http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/42cd1554-de7a

回答by Joel B Fant

According to the Data Templating Overviewon MSDN, DataTemplatesshould be used as the ItemTemplateto define how the data is presented, while a Stylewould be used as the ItemContainerStyleto style just the generated container, such as ListBoxItem.

根据MSDN 上的数据模板概述DataTemplates应用作ItemTemplate定义数据的呈现方式,而 aStyle将用作ItemContainerStyle仅对生成的容器进行样式设置,例如ListBoxItem.

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 custom Panelto layout the items than for the items to lay themselves out with a Rube Goldberg assortment of IValueConverters.

我也忍不住认为基于项目信息的项目自定义布局需要创建自定义Panel. 定制Panel项目的布局可能比用 Rube Goldberg 的IValueConverters.

回答by Drew Noakes

If you want to completely replace the ListBoxItemtemplate such that no selection is visible (perhaps you want the look of ItemsControlwith the grouping/etc behaviour of ListBox) then you can use this style:

如果你想完全替换ListBoxItem模板,这样就没有可见的选择(也许你想要ItemsControl具有分组/等行为的外观ListBox),那么你可以使用这种风格:

<Style TargetType="ListBoxItem">
  <Setter Property="Margin" Value="2" />
  <Setter Property="FocusVisualStyle" Value="{x:Null}" />
  <Setter Property="OverridesDefaultStyle" Value="True" />
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type ListBoxItem}">
        <ContentPresenter Content="{TemplateBinding ContentControl.Content}" 
                          HorizontalAlignment="Stretch" 
                          VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}" 
                          SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

This template also excludes the standard Borderwrapper. If you need that, you can use replace the template with this:

此模板也不包括标准Border包装器。如果需要,您可以使用以下内容替换模板:

<Border BorderThickness="{TemplateBinding Border.BorderThickness}" 
        Padding="{TemplateBinding Control.Padding}" 
        BorderBrush="{TemplateBinding Border.BorderBrush}" 
        Background="{TemplateBinding Panel.Background}" 
        SnapsToDevicePixels="True">
  <ContentPresenter Content="{TemplateBinding ContentControl.Content}" 
                    ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" 
                    HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}" 
                    VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}" 
                    SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</Border>

If you don't need all these TemplateBindingvalues then you can remove some for performance.

如果您不需要所有这些TemplateBinding值,那么您可以删除一些以提高性能。