ListBox с Grid as ItemsPanelTemplate вызывает странные ошибки привязки - PullRequest
33 голосов
/ 02 октября 2008

У меня есть элемент управления ListBox, и я представляю фиксированное количество объектов ListBoxItem в сетке. Поэтому я установил свой ItemsPanelTemplate в качестве сетки.

Я обращаюсь к Grid из кода позади, чтобы настроить RowDefinitions и ColumnDefinitions.

Пока все работает так, как я ожидаю. У меня есть несколько пользовательских реализаций IValueConverter для возврата Grid.Row и Grid.Column, в которых должен отображаться каждый ListBoxItem.

Однако иногда я получаю странные ошибки привязки и не могу точно понять, почему они происходят, или даже если они есть в моем коде.

Вот ошибка, которую я получаю:

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 ]

29 голосов
/ 07 октября 2008

Проблема привязки возникает из-за стиля по умолчанию для ListBoxItem. По умолчанию при применении стилей к элементам WPF ищет стили по умолчанию и применяет каждое свойство, которое специально не установлено в пользовательском стиле из стиля по умолчанию. Обратитесь к этому великолепному сообщению в блоге Иана Гриффитса для получения более подробной информации об этом поведении.

Вернемся к нашей проблеме. Вот стиль по умолчанию для 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. Можете ли вы предоставить больше информации о том, что представляет собой ItemsSource для вашего ListBox?

P.S .: Одним из способов устранения ошибок является создание новых сеттеров для HorizontalContentAlignment и VerticalContentAlignment в собственном стиле.

21 голосов
/ 12 марта 2009

Установка OverridesDefaultStyle на True в вашем ItemContainerStyle также исправит эти проблемы.

<Style TargetType="ListBoxItem">
    <Setter Property="OverridesDefaultStyle" Value="True"/>
    <!-- set the rest of your setters, including Template, here -->
</Style>
7 голосов
/ 20 октября 2008

Это общая проблема с ListBoxItem s и другими эфемерными *Item контейнерами. Они создаются асинхронно / на лету, в то время как ItemsControl загружается / отображается. Вы должны присоединиться к событию ListBox.ItemContainerGenerator StatusChanged и дождаться, пока Статус станет ItemsGenerated, прежде чем пытаться получить к ним доступ.

6 голосов
/ 04 мая 2014

Это смесь других ответов здесь, но для меня мне пришлось применить 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 упоминает в своем ответе - я не очень настраиваю свой шаблон в огромной степени.

Я думаю, что из других ответов будет больше пробега, но я подумал, что опубликую это на случай, если это поможет кому-то в той же лодке.

Ответ Дэвида Шмитта выглядит так, как будто он может описать основную причину.

3 голосов
/ 26 июля 2014

У меня была та же проблема, что и у вас, и я просто хотел поделиться своим решением. Я перепробовал все варианты из этого поста, но последний был лучшим для меня - спасибо Крис.

Итак, мой код:

<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 не существуют.

2 голосов
/ 30 ноября 2011

Другим обходным решением / решением, которое мне помогло, было подавление этих ошибок (на самом деле, кажется, более уместно называть их предупреждениями) путем установки уровня переключения источника привязки данных как критического в конструкторе класса или в окне верхнего уровня 1001 *

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

Ссылка: Как отключить предупреждение об ошибке System.Windows.Data

2 голосов
/ 16 августа 2011

Я только что столкнулся с тем же типом ошибки:

System.Windows.Data Ошибка: 4: Не удается найти источник для привязки со ссылкой 'RelativeSource FindAncestor, AncestorType =' System.Windows.Controls.ItemsControl ', AncestorLevel =' 1 ''. BindingExpression: Path = HorizontalContentAlignment; DataItem = NULL; целевым элементом является ListBoxItem (Name = ''); Целевым свойством является «HorizontalContentAlignment» (тип «HorizontalAlignment»)

Это произошло при выполнении привязки:

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

Для этого свойства в моем объекте контекста данных:

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

После некоторых экспериментов я обнаружил, что ошибка возникает только тогда, когда количество элементов превышает видимую высоту моего ListBox (например, когда появляются вертикальные полосы прокрутки). Поэтому я сразу подумал о виртуализации и попробовал это:

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

Это решило проблему для меня. Хотя я бы предпочел оставить виртуализацию включенной, я больше не использовал время, чтобы погрузиться в нее. Мое приложение немного сложное, с несколькими уровнями сетки, панелями док-станции и т. Д. И некоторыми вызовами асинхронных методов. Я не смог воспроизвести проблему в более простом приложении.

1 голос
/ 15 февраля 2012

Это сработало для меня. Поместите это в файл 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

1 голос
/ 16 октября 2009

Если вы хотите полностью заменить шаблон ListBoxItem таким образом, чтобы выделение не было видно (возможно, вам нужен внешний вид ItemsControl с поведением группировки / etc 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 значения, вы можете удалить некоторые из них для производительности.

1 голос
/ 02 октября 2008

В соответствии с Обзор шаблонов данных в MSDN, DataTemplates следует использовать как ItemTemplate для определения способа представления данных, тогда как Style будет использоваться как ItemContainerStyle стилизовать только сгенерированный контейнер, например ListBoxItem.

Однако, похоже, что вы пытаетесь использовать последнее для выполнения работы первого. Я не могу воссоздать вашу ситуацию без гораздо большего количества кода, но я подозреваю, что выполнение привязки данных в стиле контейнера может привести к созданию гаечного ключа в предполагаемом визуальном / логическом дереве.

Я также не могу не думать, что пользовательский макет элементов, основанный на информации об элементе, требует создания пользовательского Panel. Вероятно, для пользовательского Panel лучше расположить элементы по порядку, чем для элементов, представленных в ассортименте Рубе Голдберга IValueConverters.

...