WPF-Fehler: Das maßgebliche FrameworkElement für das Zielelement wurde nicht gefunden

85

Ich habe eine DataGridmit einer Zeile, die ein Bild hat. Dieses Bild ist mit einem Auslöser an einen bestimmten Zustand gebunden. Wenn sich der Status ändert, möchte ich das Bild ändern.

Die Vorlage selbst wird auf die HeaderStylevon a gesetzt DataGridTemplateColumn. Diese Vorlage hat einige Bindungen. Der erste Bindungstag zeigt an, welcher Tag heute ist, und der Status ändert das Bild mit einem Auslöser.

Diese Eigenschaften werden in einem ViewModel festgelegt.

Eigenschaften:

public class HeaderItem
{
    public string Day { get; set; }
    public ValidationStatus State { get; set; }
}

this.HeaderItems = new ObservableCollection<HeaderItem>();
for (int i = 1; i < 15; i++)
{
    this.HeaderItems.Add(new HeaderItem()
    {
        Day = i.ToString(),
        State = ValidationStatus.Nieuw,
    });
}

Datagrid:

<DataGrid x:Name="PersoneelsPrestatiesDataGrid" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
              AutoGenerateColumns="False" SelectionMode="Single" ItemsSource="{Binding CaregiverPerformances}" FrozenColumnCount="1" >

    <DataGridTemplateColumn HeaderStyle="{StaticResource headerCenterAlignment}" Header="{Binding HeaderItems[1]}" Width="50">
        <DataGridTemplateColumn.CellEditingTemplate>
            <DataTemplate>
                <TextBox Text="{ Binding Performances[1].Duration,Converter={StaticResource timeSpanConverter},Mode=TwoWay}"/>
            </DataTemplate>
        </DataGridTemplateColumn.CellEditingTemplate>

        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <TextBlock TextAlignment="Center" Text="{ Binding Performances[1].Duration,Converter={StaticResource timeSpanConverter}}"/>
            </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn> 
</DataGrid>

Datagrid HeaderStyleTemplate:

<Style x:Key="headerCenterAlignment" TargetType="{x:Type DataGridColumnHeader}">
    <Setter Property="HorizontalContentAlignment" Value="Center"/>

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition />
                        <RowDefinition />
                    </Grid.RowDefinitions>

                    <TextBlock Grid.Row="0" Text="{Binding Day}" />
                    <Image x:Name="imageValidation" Grid.Row="1" Width="16" Height="16" Source="{StaticResource imgBevestigd}" />
                </Grid>

                <ControlTemplate.Triggers>
                    <MultiDataTrigger >
                        <MultiDataTrigger.Conditions>
                            <Condition Binding="{Binding State}" Value="Nieuw"/>                                 
                        </MultiDataTrigger.Conditions>
                        <Setter TargetName="imageValidation" Property="Source" Value="{StaticResource imgGeenStatus}"/>
                    </MultiDataTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Wenn ich jetzt das Projekt starte, werden die Bilder nicht angezeigt und ich erhalte folgende Fehlermeldung:

System.Windows.Data-Fehler: 2: FrameworkElement oder FrameworkContentElement für das Zielelement können nicht gefunden werden. BindingExpression: Path = HeaderItems [0]; DataItem = null; Zielelement ist 'DataGridTemplateColumn' (HashCode = 26950454); Die Zieleigenschaft ist 'Header' (Typ 'Objekt').

Warum wird dieser Fehler angezeigt?

KDP
quelle
4
Ich habe die oben beantwortete Lösung überprüft, aber sie funktioniert in meinem Fall nicht. Wenn ich zu einer anderen Lösung wechsle wie im Link thomaslevesque.com/2011/03/21/… . Die Idee ist die gleiche wie bei der Lösung. Statt FrameworkElement zu verwenden, haben sie eine andere Klasse erstellt. Dann funktioniert es bei mir.
Leo5.
Für andere, die hier nach der Fehlermeldung gesucht haben: Die Antwort auf diese ähnliche Frage hat mir geholfen, das Problem ziemlich einfach zu lösen. Stackoverflow.com/a/18657986/4961688
Tim Pohlmann

Antworten:

163

Leider ist jedes DataGridColumngehostete unter DataGrid.Columnsnicht Teil des VisualBaums und daher nicht mit dem Datenkontext des Datagrids verbunden. Bindungen funktionieren also nicht mit ihren Eigenschaften wie Visibilityoder Headerusw. (obwohl diese Eigenschaften gültige Abhängigkeitseigenschaften sind!).

Jetzt fragen Sie sich vielleicht, wie das möglich ist? Soll ihr BindingEigentum nicht an den Datenkontext gebunden sein? Nun, es ist einfach ein Hack. Die Bindung funktioniert nicht wirklich. Es sind tatsächlich die Datagrid-Zellen, die dieses Bindungsobjekt kopieren / klonen und es zur Anzeige ihres eigenen Inhalts verwenden!

Zurück zur Lösung Ihres Problems gehe ich davon aus, dass dies HeaderItemseine Eigenschaft des Objekts ist, die als die DataContextIhrer übergeordneten Ansicht festgelegt ist. Wir können die Verbindung DataContextder Ansicht zu jeder DataGridColumnüber etwas , das wir einen Anruf ProxyElement.

Das folgende Beispiel zeigt, wie ein logisches untergeordnetes Element wie ContextMenuoder DataGridColumnmit den übergeordneten Ansichten verbunden wirdDataContext

 <Window x:Class="WpfApplicationMultiThreading.Window5"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
         xmlns:vb="http://schemas.microsoft.com/wpf/2008/toolkit"
         Title="Window5" Height="300" Width="300" >
  <Grid x:Name="MyGrid">
    <Grid.Resources>
        <FrameworkElement x:Key="ProxyElement" DataContext="{Binding}"/>
    </Grid.Resources>
    <Grid.DataContext>
         <TextBlock Text="Text Column Header" Tag="Tag Columne Header"/>
    </Grid.DataContext>
    <ContentControl Visibility="Collapsed"
             Content="{StaticResource ProxyElement}"/>
    <vb:DataGrid AutoGenerateColumns="False" x:Name="MyDataGrid">
        <vb:DataGrid.ItemsSource>
            <x:Array Type="{x:Type TextBlock}">
                <TextBlock Text="1" Tag="1.1"/>
                <TextBlock Text="2" Tag="1.2"/>
                <TextBlock Text="3" Tag="2.1"/>
                <TextBlock Text="4" Tag="2.2"/>
            </x:Array>
        </vb:DataGrid.ItemsSource>
        <vb:DataGrid.Columns>
            <vb:DataGridTextColumn
                       Header="{Binding DataContext.Text,
                                     Source={StaticResource ProxyElement}}"
                       Binding="{Binding Text}"/>
            <vb:DataGridTextColumn
                       Header="{Binding DataContext.Tag,
                                     Source={StaticResource ProxyElement}}"
                       Binding="{Binding Tag}"/>
        </vb:DataGrid.Columns>
    </vb:DataGrid>
  </Grid>
</Window>

In der obigen Ansicht ist derselbe Bindungsfehler aufgetreten, den Sie gefunden haben, wenn ich den ProxyElement-Hack nicht implementiert habe. Das ProxyElement ist ein FrameworkElement, das das aus der Hauptansicht stiehltDataContext und es dem logischen untergeordneten Element wie ContextMenuoder anbietet DataGridColumn. Dafür muss es als ContentUnsichtbares gehostet werden, ContentControldas sich unter derselben Ansicht befindet.

Ich hoffe, das führt Sie in die richtige Richtung.

WPF-it
quelle
25
Ich finde es wirklich enttäuschend, dieses hackige Proxy-Zeug verwenden zu müssen, aber ich kann sonst keinen anderen Weg finden, um die gleiche Funktionalität zu erreichen ... Danke.
Alex Hope O'Connor
2
Dies funktionierte bei mir nicht, aber nachdem ich Josh Smiths Artikel über virtuelle Zweige gelesen hatte, versuchte ich, die OneWayToSource-Bindung auf meinem Root-Steuerelement hinzuzufügen, um den "ProxyElement" -Datenkontext festzulegen, und das funktionierte.
Jpierson
1
Nee. Die obige Lösung passt sehr gut zu .NET 3.5.
WPF-it
1
Diese Antwort ist alt, aber für .NET 4.0 immer noch nützlich. Viele der Antworten zum Kopieren des DataContext in die Spalte scheinen nicht zu funktionieren. Ich musste eine Spalte abhängig von einer Ansichtsmodelleigenschaft ein- / ausblenden, und diese Lösung funktionierte gut. Und ohne Code wird kein diplomatischer Vorfall in der Codeüberprüfung verursacht.
James_UK_DEV
3
Das FYI-Kontextmenü ist nicht dasselbe und hat eine Nicht-Proxy-Problemumgehung. Das Kontextmenü hat eine exponierte Eigenschaft, Parentwährend das DataGridTextColumnseine DataGridOwnerEigenschaft nicht verfügbar macht . Sehen Sie in meiner Antwort, wie eine Kontextelementbindung über die RelativeSource-Bindung durchgeführt wird. Kontextmenü-Bindung an den Datenkontext des übergeordneten Fensters
ΩmegaMan
5

Eine etwas kürzere Alternative zur Verwendung von a StaticResourcewie in der akzeptierten Antwort ist x:Reference:

<StackPanel>

    <!--Set the DataContext here if you do not want to inherit the parent one-->
    <FrameworkElement x:Name="ProxyElement" Visibility="Collapsed"/>

    <DataGrid>
        <DataGrid.Columns>
            <DataGridTextColumn
                Header="{Binding DataContext.Whatever, Source={x:Reference ProxyElement}}"
                Binding="{Binding ...}" />
        </DataGrid.Columns>
    </DataGrid>

</StackPanel>

Der Hauptvorteil davon ist: Wenn Sie bereits ein Element haben, das nicht der Vorfahr eines DataGrid ist (dh nicht das StackPanelim obigen Beispiel), können Sie ihm einfach einen Namen geben und ihn x:Referencestattdessen verwenden, sodass Sie keinen Dummy definieren müssen FrameworkElementüberhaupt.

Wenn Sie versuchen, einen Vorfahren zu referenzieren, erhalten Sie XamlParseExceptionzur Laufzeit aufgrund einer zyklischen Abhängigkeit eine.

FernAndr
quelle
Ich ziehe dies der akzeptierten Antwort vor!
AyCe