使用Grid作为ItemsPanelTemplate的ListBox会产生奇怪的绑定错误

时间:2008-10-02 00:36:58

标签: .net wpf data-binding listbox itemspaneltemplate

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

我从代码后面访问Grid以配置RowDefinitions和ColumnDefinitions。

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

然而,我有时会遇到奇怪的绑定错误,我无法确切地知道它们为什么会发生,或者即使它们出现在我的代码中。

这是我得到的错误:

  

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

有人可以解释发生了什么吗?

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

12 个答案:

答案 0 :(得分:29)

绑定问题来自ListBoxItem的默认样式。默认情况下,将样式应用于元素时,WPF会查找默认样式,并应用默认样式中未在自定义样式中专门设置的每个属性。有关此行为的详细信息,请参阅Ian Griffiths的this great blog post

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

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

P.S。:删除错误的一种方法是在自定义样式中为Horizo​​ntalContentAlignment和VerticalContentAlignment创建新的setter。

答案 1 :(得分:21)

OverridesDefaultStyle中设置TrueItemContainerStyle也可以解决这些问题。

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

答案 2 :(得分:7)

这是common problemListBoxItem和其他短暂的*Item容器。它们是异步/动态创建的,同时加载/呈现ItemsControl。您必须附加到ListBox.ItemContainerGeneratorStatusChanged事件,并等待状态变为ItemsGenerated,然后再尝试访问它们。

答案 3 :(得分:6)

这是其他答案的混合,但对我来说,我必须在两个地方应用Setter来解决错误,尽管这是在使用自定义VirtualizingWrapPanel

如果我删除以下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>

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

我认为在其他答案中有更多的里程数,但我想我会发布这个有机会帮助同一条船的人。

David Schmitt的回答看起来可能描述了根本原因。

答案 4 :(得分:3)

我和你有同样的问题,我只想分享我的解决方案。 我已尝试过这篇文章中的所有选项,但最后一个对我来说是最好的 - 谢克斯。

所以我的代码:

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

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

答案 5 :(得分:2)

我刚刚遇到了同样的错误:

  

System.Windows.Data错误:4:       无法找到带参考的绑定源       'RelativeSource FindAncestor,AncestorType ='System.Windows.Controls.ItemsControl',AncestorLevel ='1''。       BindingExpression:路径= Horizo​​ntalContentAlignment;       的DataItem = NULL; target元素是'ListBoxItem'(Name ='');       target属性是'Horizo​​ntalContentAlignment'(类型'Horizo​​ntalAlignment')

在执行这样的绑定时发生了这种情况:

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

在我的数据上下文对象上的这个属性:

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

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

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

这解决了我的问题。 虽然我更愿意继续虚拟化,但我没有再花时间去深入了解它。 我的应用程序有点复杂,有多个级别的网格,停靠面板等以及一些异步方法调用。 我无法在更简单的应用程序中重现该问题。

答案 6 :(得分:2)

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

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

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

答案 7 :(得分:1)

根据MSDN上的Data Templating OverviewDataTemplates应该用作ItemTemplate来定义数据的呈现方式,而Style将用作ItemContainerStyle {1}}仅为生成的容器设置样式,例如ListBoxItem

但是,您似乎正在尝试使用后者来完成前者的工作。我无法在没有更多代码的情况下重新创建您的情况,但我怀疑在容器样式中进行数据绑定可能会在假定的可视/逻辑树中抛出一个扳手。

我也不禁认为根据商品信息自定义商品布局需要创建自定义Panel。自定义Panel布局项目可能比使用Rube Goldberg分类IValueConverters的项目更好。

答案 8 :(得分:1)

如果您想完全替换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>

此模板还排除了标准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>

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

答案 9 :(得分:1)

这对我有用。将它放在Application.xaml文件中。

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

...从

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

答案 10 :(得分:0)

只需为类型&#34; ComboBoxItem&#34;创建默认样式不起作用,因为它被ComboBox的默认&#34; ItemContainerStyle&#34;覆盖。要真正摆脱这个,你需要改变默认的&#34; ItemContainerStyle&#34;对于ComboBoxes,像这样:

<Style TargetType="ComboBox">
    <Setter Property="ItemContainerStyle">
        <Setter.Value>                
            <Style TargetType="ComboBoxItem">
                <Setter Property="HorizontalContentAlignment" Value="Left" />
                <Setter Property="VerticalContentAlignment" Value="Center" />
            </Style>
        </Setter.Value>
    </Setter>
</Style>

答案 11 :(得分:0)

我开始遇到这个问题,即使我的ListBox同时具有Style和ItemContainerStyle集 - 并且这些命名样式已经定义了Horizo​​ntalContentAlignment。我正在使用CheckBox控件打开/关闭我的ListBox上的实时过滤,这似乎导致项目从默认样式而不是我指定的样式中拉出。大多数错误将在第一次启动实时过滤时发生,但此后它将继续在每次更改时抛出2个错误。我觉得有趣的是,我的收藏品中只有2条记录是空的,因此没有任何东西可以显示在该项目中。所以这似乎已经结束了。我打算创建一个记录为空时显示的默认数据。

卡特的建议对我有用。添加一个没有键的单独“默认”样式和定义Horizo​​ntalContentAlignment属性的TargetType =“ListBoxItem”解决了这个问题。我甚至不需要为它设置OverridesDefaultStyle属性。