.net - databinding - wpf listbox itemtemplate




ListBox mit Grid als ItemsPanelTemplate erzeugt seltsame Bindungsfehler (8)

Das Bindungsproblem stammt von dem Standardstil für ListBoxItem. Beim Anwenden von Stilen auf Elemente sucht WPF standardmäßig nach den Standardstilen und wendet jede Eigenschaft, die nicht im benutzerdefinierten Stil festgelegt ist, auf den Standardstil an. Weitere Informationen zu diesem Verhalten finden Sie in diesem großartigen Blogpost von Ian Griffiths.

Zurück zu unserem Problem. Hier ist der Standardstil für 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>

Beachten Sie, dass ich das StyleSnooper entfernt habe, um es kompakt zu machen (Ich habe StyleSnooper - um den Style abzurufen). Sie können sehen, dass eine Bindung mit einer relativen Quelle vorhanden ist, die auf einen Vorgänger mit dem Typ ItemsControl festgelegt wurde. In Ihrem Fall finden die ListBoxItems, die beim Binden erstellt werden, ihr ItemsControl nicht. Können Sie weitere Informationen dazu bereitstellen, was die ItemsSource für Ihre ListBox ist?

PS: Eine Möglichkeit zum Entfernen der Fehler besteht darin, neue Setter für HorizontalContentAlignment und VerticalContentAlignment in Ihrem benutzerdefinierten Stil zu erstellen.

Ich habe ein ListBox-Steuerelement und ich präsentiere eine feste Anzahl von ListBoxItem-Objekten in einem Rasterlayout. Also habe ich meine ItemsPanelTemplate als Grid festgelegt.

Ich greife auf das Grid von Code zurück, um die RowDefinitions und ColumnDefinitions zu konfigurieren.

Bis jetzt funktioniert alles wie erwartet. Ich habe einige benutzerdefinierte IValueConverter-Implementierungen für die Rückgabe der Grid.Row und Grid.Column, die jedes ListBoxItem angezeigt werden sollte.

Allerdings bekomme ich manchmal seltsame Bindungsfehler, und ich kann nicht genau herausfinden, warum sie passieren, oder sogar, wenn sie in meinem Code sind.

Hier ist der Fehler, den ich bekomme:

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

Kann mir jemand erklären, was vor sich geht?

Oh, und hier ist mein 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>


Das Erstellen eines Standardstils für den Typ "ComboBoxItem" funktioniert nicht, da er vom Standard ItemContainerStyle der ComboBox überschrieben wird. Um das wirklich loszuwerden, müssen Sie den Standard "ItemContainerStyle" für ComboBoxes wie folgt ändern:

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


Dies ist ein Amalgam der anderen Antworten hier, aber für mich musste ich den Setter an zwei Stellen anwenden, um den Fehler zu beheben, obwohl dies mit einem benutzerdefinierten VirtualizingWrapPanel

Wenn ich eine der folgenden Setter Deklarationen Setter , werden meine Fehler erneut angezeigt.

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

Ich habe im Moment nicht die Zeit, weiter zu recherchieren, aber ich vermute, dass es mit dem Standard-Stil zusammenhängt, den JTango in seiner Antwort erwähnt - ich passe meine Vorlage nicht wirklich in hohem Maße an.

Ich denke, es gibt mehr Kilometer, die man von den anderen Antworten haben könnte, aber ich dachte, ich würde dies posten, wenn es jemandem im selben Boot hilft.

David Schmitts Antwort sieht so aus, als könnte sie die Ursache beschreiben.


Gemäß der Data Templating-Übersicht in MSDN sollten DataTemplates als ItemTemplate , um zu definieren, wie die Daten dargestellt werden, während ein Style als ItemContainerStyle , um nur den generierten Container wie ListBoxItem .

Es scheint jedoch, dass Sie versuchen, das Letztere zu benutzen, um die Arbeit des ersteren zu erledigen. Ich kann Ihre Situation nicht ohne viel mehr Code neu erstellen, aber ich vermute, dass die Datenbindung im Containerstil einen Haken in den angenommenen visuellen / logischen Baum werfen könnte.

Ich kann auch nicht anders, als zu denken, dass ein benutzerdefiniertes Layout von Elementen, das auf den Informationen des Elements basiert, das Erstellen eines benutzerdefinierten Panel . Es ist wahrscheinlich besser für das benutzerdefinierte Panel , die Elemente zu gestalten, als für die Elemente, die sich mit einem Rube Goldberg Sortiment von IValueConverters .


Ich fing an, in dieses Problem zu laufen, obwohl meine ListBox sowohl einen Stil als auch einen ItemContainerStyle hatte - und diese benannten Stile hatten bereits HorizontalContentAlignment definiert. Ich habe CheckBox-Steuerelemente verwendet, um die Live-Filterung auf meiner ListBox ein- und auszuschalten, und dies schien zu bewirken, dass die Elemente statt vom zugewiesenen Stil aus dem Standardstil gezogen wurden. Die meisten Fehler treten auf, wenn die Live-Filterung zum ersten Mal gestartet wird, danach werden jedoch bei jeder Änderung 2 Fehler ausgegeben. Ich finde es interessant, dass genau 2 Datensätze in meiner Sammlung leer waren und somit nichts im Objekt angezeigt werden konnte. Das scheint also beigetragen zu haben. Ich plane, Standarddaten zu erstellen, die angezeigt werden, wenn ein Datensatz leer ist.

Carters Vorschlag funktionierte für mich. Das Hinzufügen eines separaten "Standard" -Stils ohne Schlüssel und eines TargetType = "ListBoxItem", mit dem die Eigenschaft HorizontalContentAlignment definiert wurde, hat das Problem gelöst. Ich musste nicht einmal die OverridesDefaultStyle-Eigenschaft dafür festlegen.


Ich hatte das gleiche Problem wie du und ich wollte nur teilen, was meine Lösung war. Ich habe alle Möglichkeiten aus diesem Beitrag ausprobiert, aber der letzte war der beste für mich - thxxch.

Also mein Code:

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

Ich habe auch festgestellt, dass dieser Fehler nicht ItemsPanelTemplate , wenn benutzerdefinierte ItemsPanelTemplate nicht vorhanden ist.


Wenn Sie die ListBoxItem Vorlage vollständig ersetzen ListBoxItem , sodass keine Auswahl sichtbar ist (möglicherweise möchten Sie das Aussehen von ItemsControl mit dem Gruppierung / etc-Verhalten von ListBox ), können Sie diesen Stil verwenden:

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

Diese Vorlage schließt auch den Standard- Border Wrapper aus. Wenn Sie das brauchen, können Sie die Vorlage folgendermaßen ersetzen:

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

Wenn Sie nicht alle diese TemplateBinding Werte benötigen, können Sie einige für die Leistung entfernen.





itemspaneltemplate