36

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

我正在从后面的代码访问 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>

4

12 回答 12

31

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

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

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

于 2008-10-06T22:10:40.527 回答
23

在您的设置OverridesDefaultStyle中也将解决这些问题。TrueItemContainerStyle

<Style TargetType="ListBoxItem">
    <Setter Property="OverridesDefaultStyle" Value="True"/>
    <!-- set the rest of your setters, including Template, here -->
</Style>
于 2009-03-12T00:07:44.740 回答
8

这是此处其他答案的混合体,但对我来说,我必须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 在他的回答中提到的默认样式有关——我并没有真正在很大程度上自定义我的模板。

我认为其他答案还有更多的里程,但我想我会发布这个机会,它可以帮助同一条船上的人。

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

于 2014-05-04T11:39:11.023 回答
7

这是s 和其他临时容器的常见问题。它们是在加载/渲染时异步/动态创建的。在尝试访问它们之前,您必须附加到's事件并等待状态变为。ListBoxItem*ItemItemsControlListBox.ItemContainerGeneratorStatusChangedItemsGenerated

于 2008-10-20T13:23:05.033 回答
4

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

所以我的代码:

<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不存在时,不会出现此错误。

于 2014-07-26T11:23:01.110 回答
3

这对我有用。把它放在你的 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

于 2012-02-15T00:01:13.093 回答
2

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

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

这发生在执行这样的绑定时:

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

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

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

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

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

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

于 2011-08-16T13:08:26.577 回答
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值,则可以删除一些以提高性能。

于 2009-10-16T16:06:38.540 回答
1

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

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

我也忍不住认为,基于项目信息的项目自定义布局需要创建自定义Panel. 自定义Panel布局项目可能比使用 Rube Goldberg 分类的项目更好地布置项目IValueConverters

于 2008-10-02T18:04:04.863 回答
1

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

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

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

于 2011-11-30T12:34:43.950 回答
0

简单地为“ComboBoxItem”类型创建一个默认样式是行不通的,因为它被 ComboBox 的默认“ItemContainerStyle”覆盖。要真正摆脱这种情况,您需要更改 ComboBoxes 的默认“ItemContainerStyle”,如下所示:

<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>
于 2012-02-21T16:41:03.173 回答
0

我开始遇到这个问题,即使我的 ListBox 同时设置了 Style 和 ItemContainerStyle - 这些命名的样式已经定义了 Horizo​​ntalContentAlignment。我正在使用 CheckBox 控件来打开/关闭我的 ListBox 上的实时过滤,这似乎导致项目从默认样式而不是我分配的样式中拉出。大多数错误会在第一次启动实时过滤时发生,但此后它会继续在每次更改时抛出 2 个错误。我发现有趣的是,我的收藏中恰好有 2 条记录是空的,因此项目中没有任何内容可显示。所以这似乎有所贡献。我计划创建在记录为空时显示的默认数据。

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

于 2014-07-03T18:52:26.497 回答