Eu tenho um DataGrid
com uma linha que tem uma imagem. Esta imagem é ligada por um gatilho a um determinado estado. Quando o estado mudar, quero mudar a imagem.
O próprio modelo é definido em HeaderStyle
de a DataGridTemplateColumn
. Este modelo possui algumas ligações. O primeiro dia de ligação mostra que dia é e o estado muda a imagem com um gatilho.
Essas propriedades são definidas em um ViewModel.
Propriedades:
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,
});
}
Grade de dados:
<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>
Agora, quando eu inicio o projeto, as imagens não aparecem e recebo este erro:
Erro System.Windows.Data: 2: Não é possível localizar FrameworkElement ou FrameworkContentElement para o elemento de destino. BindingExpression: Path = HeaderItems [0]; DataItem = null; o elemento de destino é 'DataGridTemplateColumn' (HashCode = 26950454); a propriedade de destino é 'Cabeçalho' (tipo 'Objeto')
Por que este erro está sendo mostrado?
Respostas:
Infelizmente, qualquer um
DataGridColumn
hospedado emDataGrid.Columns
não faz parte daVisual
árvore e, portanto, não está conectado ao contexto de dados do datagrid. Portanto, as ligações não funcionam com suas propriedades comoVisibility
ouHeader
etc (embora essas propriedades sejam propriedades de dependência válidas!).Agora você pode se perguntar como isso é possível? Sua
Binding
propriedade não deveria estar vinculada ao contexto de dados? Bem, é simplesmente um hack. A ligação realmente não funciona. Na verdade, são as células de datagrid que copiam / clonam esse objeto de ligação e o usam para exibir seus próprios conteúdos!Portanto, agora, de volta à solução de seu problema, presumo que
HeaderItems
seja uma propriedade do objeto definido como oDataContext
de sua Visualização pai. Nós podemos conectar oDataContext
do fim de qualquerDataGridColumn
via algo que chamamos de umProxyElement
.O exemplo abaixo ilustra como conectar um filho lógico, como
ContextMenu
ouDataGridColumn
ao View paiDataContext
<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>
A exibição acima encontrou o mesmo erro de ligação que você encontrou se eu não tivesse implementado o hack ProxyElement. O ProxyElement é qualquer FrameworkElement que rouba o
DataContext
da View principal e o oferece ao filho lógico, comoContextMenu
ouDataGridColumn
. Para isso, ele deve ser hospedado como umContent
invisívelContentControl
que está sob a mesma visualização.Espero que isso o oriente na direção correta.
fonte
Parent
enquanto oDataGridTextColumn
não expõe suaDataGridOwner
propriedade. Veja como uma vinculação de itens de contexto é realizada por meio de vinculação RelativeSource em minha resposta Vinculação do menu de contexto ao Datacontext da janela paiUma alternativa um pouco mais curta para usar um
StaticResource
como na resposta aceita é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>
A principal vantagem disso é: se você já tiver um elemento que não seja um ancestral do DataGrid (ou seja, não o
StackPanel
no exemplo acima), você pode simplesmente dar um nome a ele e usá-lo como ox:Reference
, portanto, não precisa definir nenhum modeloFrameworkElement
em absoluto.Se você tentar fazer referência a um ancestral, obterá um
XamlParseException
em tempo de execução devido a uma dependência cíclica.fonte