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

Cra*_*rer 35 .net data-binding wpf 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>
Run Code Online (Sandbox Code Playgroud)

lig*_*gaz 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>
Run Code Online (Sandbox Code Playgroud)

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

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

  • 另外,在我的自定义样式中使用Horizo​​ntalContentAlignment的setter,似乎对我没什么影响(这是为了ComboBoxItem). (4认同)
  • +1指向Ian Griffith的帖子.这是关于如何构造元素的最佳描述之一......这是我读过的. (2认同)

小智 22

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

<Style TargetType="ListBoxItem">
    <Setter Property="OverridesDefaultStyle" Value="True"/>
    <!-- set the rest of your setters, including Template, here -->
</Style>
Run Code Online (Sandbox Code Playgroud)


Dav*_*itt 7

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


Chr*_*ris 7

这是其他答案的混合,但对我来说,我必须应用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>
Run Code Online (Sandbox Code Playgroud)

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

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

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


Joe*_*ant 1

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

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

我也忍不住认为基于项目信息的项目自定义布局需要创建自定义Panel. 定制布局物品可能Panel比让物品用鲁布·戈德堡 (Rube Goldberg) 分类来布局更好IValueConverters