tengo un DataGrid
con una fila que tiene una imagen. Esta imagen está vinculada con un disparador a un cierto estado. Cuando el estado cambia, quiero cambiar la imagen.
La propia plantilla se establece en el HeaderStyle
de a DataGridTemplateColumn
. Esta plantilla tiene algunos enlaces. El primer día vinculante muestra qué día es y el estado cambia la imagen con un disparador.
Estas propiedades se establecen en un ViewModel.
Propiedades:
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,
});
}
Cuadrícula de datos:
<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>
Ahora, cuando inicio el proyecto, las imágenes no se muestran y aparece este error:
Error de System.Windows.Data: 2: No se puede encontrar FrameworkElement o FrameworkContentElement para el elemento de destino. BindingExpression: Path = HeaderItems [0]; DataItem = null; el elemento de destino es 'DataGridTemplateColumn' (HashCode = 26950454); la propiedad de destino es 'Encabezado' (tipo 'Objeto')
¿Por qué se muestra este error?
Respuestas:
Lamentablemente, cualquier
DataGridColumn
alojado debajoDataGrid.Columns
no es parte delVisual
árbol y, por lo tanto, no está conectado al contexto de datos de la cuadrícula de datos. Así que las fijaciones no trabajan con sus propiedades, tales comoVisibility
oHeader
etc (aunque estas propiedades son las propiedades de dependencia válidos!).Ahora te preguntarás cómo es eso posible. ¿No se
Binding
supone que su propiedad está vinculada al contexto de datos? Bueno, simplemente es un truco. La unión no funciona realmente. En realidad, son las celdas de la cuadrícula de datos las que copian / clonan este objeto de enlace y lo usan para mostrar su propio contenido.Entonces, volviendo a resolver su problema, supongo que
HeaderItems
es una propiedad del objeto que se establece como laDataContext
Vista principal. Nos podemos conectar elDataContext
de la vista para cualquierDataGridColumn
medio de algo que llamamos unaProxyElement
.El siguiente ejemplo ilustra cómo conectar un hijo lógico como
ContextMenu
oDataGridColumn
con la vista principalDataContext
<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 vista anterior encontró el mismo error de enlace que ha encontrado si no implementé el truco ProxyElement. El ProxyElement es cualquier FrameworkElement que roba el
DataContext
de la Vista principal y lo ofrece al hijo lógico comoContextMenu
oDataGridColumn
. Para eso debe ser alojado como unContent
en un invisibleContentControl
que está bajo la misma Vista.Espero que esto le oriente en la dirección correcta.
fuente
Parent
mientras que elDataGridTextColumn
no expone suDataGridOwner
propiedad. Vea cómo se logra un enlace de elementos de contexto a través del enlace RelativeSource en mi respuesta Enlace del menú contextual al contexto de datos de la ventana principalUna alternativa un poco más corta a usar
StaticResource
como en la respuesta aceptada esx: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>
La principal ventaja de esto es: si ya tiene un elemento que no es un ancestro de DataGrid (es decir, no es el
StackPanel
del ejemplo anterior), puede simplemente darle un nombre y usarlo como elx:Reference
en su lugar, por lo tanto, no necesita definir ningún ficticioFrameworkElement
en absoluto.Si intenta hacer referencia a un antepasado, obtendrá un
XamlParseException
en tiempo de ejecución debido a una dependencia cíclica.fuente