web-dev-qa-db-fra.com

Erreur WPF: impossible de trouver FrameworkElement de gouvernance pour l'élément cible

J'ai une grille de données avec une ligne qui a une image. Cette image est liée avec un déclencheur à un certain état. Lorsque l'état change, je veux changer l'image.

Le modèle lui-même est défini sur le HeaderStyle d'un DataGridTemplateColumn. Ce modèle a quelques liaisons. Le premier Jour de reliure montre de quel jour il s'agit et l'État change l'image avec un déclencheur.

Ces propriétés sont définies dans un ViewModel.

Propriétés:

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,
            });
        }

Grille de données:

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

Maintenant, lorsque je démarre le projet, les images ne s'affichent pas et j'obtiens cette erreur:

Erreur System.Windows.Data: 2: impossible de trouver FrameworkElement ou FrameworkContentElement pour l'élément cible. BindingExpression: Path = HeaderItems [0]; DataItem = null; l'élément cible est "DataGridTemplateColumn" (HashCode = 26950454); la propriété cible est 'En-tête' (type 'Objet')

Pourquoi cette erreur s'affiche-t-elle?

74
KDP

Malheureusement, tout DataGridColumn hébergé sous DataGrid.Columns ne fait pas partie de l'arborescence Visual et n'est donc pas connecté au contexte de données de la grille de données. Les liaisons ne fonctionnent donc pas avec leurs propriétés telles que Visibility ou Header etc (bien que ces propriétés soient des propriétés de dépendance valides!).

Maintenant, vous vous demandez peut-être comment est-ce possible? Leur propriété Binding n'est-elle pas censée être liée au contexte de données? Eh bien, c'est tout simplement un hack. La reliure ne fonctionne pas vraiment. Ce sont en fait les cellules de la grille de données qui copie/clone cet objet de liaison et l'utilisent pour afficher leur propre contenu!

Alors maintenant, pour résoudre votre problème, je suppose que HeaderItems est une propriété de l'objet définie comme DataContext de votre vue parent. Nous pouvons connecter le DataContext de la vue à tout DataGridColumn via quelque chose que nous appelons un ProxyElement.

L'exemple ci-dessous montre comment connecter un enfant logique tel que ContextMenu ou DataGridColumn au DataContext de la vue parent

 <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 vue ci-dessus a rencontré la même erreur de liaison que vous avez trouvée si je n'avais pas implémenté le hack ProxyElement. Le ProxyElement est tout FrameworkElement qui vole le DataContext de la vue principale et le propose à l'enfant logique tel que ContextMenu ou DataGridColumn. Pour cela, il doit être hébergé en tant que Content dans un ContentControl invisible qui est sous la même vue.

J'espère que cela vous guide dans la bonne direction.

142
WPF-it