Errore WPF: impossibile trovare FrameworkElement governante per l'elemento di destinazione


88

Ho una DataGridcon una riga che ha un'immagine. Questa immagine è associata a un trigger a un determinato stato. Quando lo stato cambia, voglio cambiare l'immagine.

Il modello stesso è impostato su HeaderStyleun file DataGridTemplateColumn. Questo modello ha alcune associazioni. Il primo giorno vincolante mostra che giorno è e lo Stato cambia l'immagine con un trigger.

Queste proprietà sono impostate in un ViewModel.

Proprietà:

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>

Ora quando avvio il progetto le immagini non vengono visualizzate e ottengo questo errore:

System.Windows.Data Errore: 2: impossibile trovare FrameworkElement o FrameworkContentElement governante per l'elemento di destinazione. BindingExpression: Path = HeaderItems [0]; DataItem = null; l'elemento di destinazione è "DataGridTemplateColumn" (HashCode = 26950454); la proprietà di destinazione è 'Header' (tipo 'Object')

Perché viene visualizzato questo errore?


4
Ho controllato la soluzione con risposta sopra, ma nel mio caso non funziona. Quando passo a un'altra soluzione come nel link thomaslevesque.com/2011/03/21/… . L'idea è la stessa della soluzione, invece di usare FrameworkElement, hanno creato un'altra classe. Allora funziona per me.
leo

Per altri che finiscono qui cercando il messaggio di errore: La risposta a questa domanda simile mi ha aiutato a risolvere il problema abbastanza facilmente stackoverflow.com/a/18657986/4961688
Tim Pohlmann

Risposte:


166

Purtroppo qualsiasi DataGridColumnospitato sotto DataGrid.Columnsnon fa parte Visualdell'albero e quindi non è connesso al contesto dati del datagrid. Quindi le associazioni non funzionano con le loro proprietà come Visibilityo Headerecc (sebbene queste proprietà siano proprietà di dipendenza valide!).

Ora potresti chiederti come è possibile? La loro Bindingproprietà non dovrebbe essere vincolata al contesto dei dati? Beh, è ​​semplicemente un trucco. La rilegatura non funziona davvero. In realtà sono le celle datagrid che copiano / clonano questo oggetto di associazione e lo usano per visualizzare i propri contenuti!

Quindi, ora tornando a risolvere il tuo problema, presumo che HeaderItemssia una proprietà dell'oggetto che è impostata come DataContextdella vista genitore. Siamo in grado di collegare la DataContextdella vista a qualsiasi DataGridColumnvia qualcosa che chiamiamo ProxyElement.

L'esempio seguente illustra come connettere un figlio logico come ContextMenuo DataGridColumnalla vista padreDataContext

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

La visualizzazione precedente ha riscontrato lo stesso errore di associazione che hai trovato se non avessi implementato l'hack ProxyElement. ProxyElement è qualsiasi FrameworkElement che ruba il DataContextdalla vista principale e lo offre al figlio logico come ContextMenuo DataGridColumn. Per questo deve essere ospitato come Contentin un invisibile ContentControlche si trova sotto la stessa vista.

Spero che questo ti guidi nella giusta direzione.


26
Trovo che dover usare questa roba hacky proxy davvero deludente ma non riesco a trovare un altro modo per ottenere la stessa funzionalità altrimenti ... Grazie.
Alex Hope O'Connor

2
Questo non ha funzionato per me, ma dopo aver letto l'articolo di Josh Smith sui rami virtuali ho provato ad aggiungere l'associazione OneWayToSource al mio controllo root per impostare il DataContext "ProxyElement" e ha funzionato.
jpierson

1
No. La soluzione di cui sopra si adatta molto bene a .NET 3.5.
WPF-it

1
Questa risposta è vecchia, ma ancora utile contro .NET 4.0. Molte delle risposte relative alla copia di DataContext nella colonna non sembrano funzionare. Avevo bisogno di mostrare / nascondere una colonna a seconda di una proprietà del modello di visualizzazione e questa soluzione ha funzionato bene. E senza codice dietro non causerà un incidente diplomatico nella revisione del codice.
James_UK_DEV

3
Cordiali saluti, il menu contestuale non è lo stesso e ha una soluzione non proxy. Il menu contestuale ha una proprietà esposta Parentmentre il DataGridTextColumnnon espone la sua DataGridOwnerproprietà. Guarda come viene eseguita l'associazione di elementi di contesto tramite l'associazione RelativeSource nella mia risposta Associazione del menu
ΩmegaMan

8

Un'alternativa leggermente più breve all'utilizzo di un StaticResourceas nella risposta accettata è 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>

Il vantaggio principale di questo è: se si dispone già di un elemento che è non antenato di un DataGrid (cioè non l' StackPanelnell'esempio precedente), si può solo dare un nome e usarlo come x:Referenceinvece, quindi non necessità di definire qualsiasi manichino FrameworkElementaffatto.

Se provi a fare riferimento a un antenato, otterrai un XamlParseExceptionin fase di esecuzione a causa di una dipendenza ciclica.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.