J'ai un DataGrid
avec une ligne qui a une image. Cette image est liée à 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
fichier DataGridTemplateColumn
. Ce modèle a quelques liaisons. Le premier jour de liaison montre quel jour il est 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 'Header' (type 'Object')
Pourquoi cette erreur s'affiche-t-elle?
Réponses:
Malheureusement, tout
DataGridColumn
hébergé sousDataGrid.Columns
ne fait pas partie de l'Visual
arborescence 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 queVisibility
ouHeader
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
Binding
propriété 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 datagrid qui copient / clonent cet objet de liaison et l'utilisent pour afficher leur propre contenu!Revenons maintenant à la résolution de votre problème, je suppose que
HeaderItems
c'est une propriété de l'objet qui est définie comme la vueDataContext
de votre parent. Nous pouvons connecter laDataContext
vue à n'importe quelDataGridColumn
via quelque chose que nous appelons unProxyElement
.L'exemple ci-dessous illustre comment connecter un enfant logique tel que
ContextMenu
ouDataGridColumn
à la vue parentDataContext
<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 n'importe quel FrameworkElement qui vole le
DataContext
de la vue principale et l'offre à l'enfant logique tel queContextMenu
ouDataGridColumn
. Pour cela, il doit être hébergé en tant queContent
dans un invisibleContentControl
qui se trouve sous la même vue.J'espère que cela vous guide dans la bonne direction.
la source
Parent
tandis que leDataGridTextColumn
n'expose pas saDataGridOwner
propriété. Voyez comment une liaison d'éléments de contexte est accomplie via la liaison RelativeSource dans ma réponse Liaison du menu contextuel au contexte de données de la fenêtre parenteUne alternative légèrement plus courte à l'utilisation de a
StaticResource
comme dans la réponse acceptée estx: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>
Le principal avantage de ceci est: si vous avez déjà un élément qui n'est pas l'ancêtre d'un DataGrid (c'est-à-dire pas le
StackPanel
dans l'exemple ci-dessus), vous pouvez simplement lui donner un nom et l'utiliser à lax:Reference
place, donc pas besoin de définir de mannequinFrameworkElement
du tout.Si vous essayez de référencer un ancêtre, vous obtiendrez un
XamlParseException
à l'exécution en raison d'une dépendance cyclique.la source