Подтвердить что ты не робот

ListBox с сеткой как ItemsPanelTemplate создает странные ошибки привязки

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

Я получаю доступ к 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>

4b9b3361

Ответ 1

Проблема привязки происходит от стиля по умолчанию для 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?

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

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

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

Ответ 5

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

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

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

Ответ 6

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

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

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

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

К этому свойству в объекте контекста данных:

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

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

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

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

Ответ 7

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

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

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

Ответ 8

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

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

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

Ответ 9

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

Ответ 10

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

Ответ 11

Просто создание стиля по умолчанию для типа "ComboBoxItem" не работает, потому что он перезаписывается по умолчанию для ComboBox "ItemContainerStyle". Чтобы действительно избавиться от этого, вам необходимо изменить значение "ItemContainerStyle" по умолчанию для 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>

Ответ 12

Я начал сталкиваться с этой проблемой, хотя у моего ListBox был и стиль, и набор ItemContainerStyle - и эти названные стили уже определили HorizontalContentAlignment. Я использовал элементы управления CheckBox, чтобы включить/выключить прямую фильтрацию в моем ListBox, и это, похоже, заставляет элементы вместо этого использовать вместо стиля по умолчанию вместо назначенных стилей. Большинство ошибок будут возникать при первом включении фильтрации в реальном времени, но после этого он будет продолжать бросать 2 ошибки при каждом изменении. Мне интересно, что ровно 2 записи в моей коллекции были пустыми и поэтому не отображали ничего в элементе. Таким образом, это, похоже, встретило. Я планирую создавать данные по умолчанию, которые будут отображаться, когда запись пуста.

Предложение Картера сработало для меня. Добавление отдельного стиля "по умолчанию" без ключа и TargetType = "ListBoxItem", который определил свойство HorizontalContentAlignment, решил проблему. Мне даже не нужно было устанавливать для него свойство OverridesDefaultStyle.

Ответ 13

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

И СЛЕДУЮЩАЯ ОШИБКА

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

Они все еще идут.