Error de WPF: no se puede encontrar el FrameworkElement gobernante para el elemento de destino
Tengo DataGrid
una fila que tiene una imagen. Esta imagen está vinculada con un disparador a un estado determinado. Cuando el estado cambia quiero cambiar la imagen.
La plantilla en sí está configurada en la HeaderStyle
base de un archivo DataGridTemplateColumn
. Esta plantilla tiene algunas vinculaciones. 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>
Plantilla de estilo de encabezado de cuadrícula de datos:
<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 System.Windows.Data: 2: No se puede encontrar el FrameworkElement o FrameworkContentElement que rige para el elemento de destino. BindingExpression:Path=HeaderItems[0]; Elemento de datos=nulo; el elemento de destino es 'DataGridTemplateColumn' (HashCode=26950454); la propiedad de destino es 'Encabezado' (escriba 'Objeto')
¿Por qué aparece este error?
Lamentablemente, cualquier DataGridColumn
alojamiento alojado DataGrid.Columns
en él no forma parte del Visual
árbol y, por lo tanto, no está conectado al contexto de datos de la cuadrícula de datos. Por lo tanto, los enlaces no funcionan con sus propiedades como Visibility
o Header
etc (¡aunque estas propiedades son propiedades de dependencia válidas!).
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 encuadernación realmente no funciona. En realidad, son las celdas de la cuadrícula de datos las que copian / clonan este objeto vinculante y lo utilizan para mostrar su propio contenido.
Ahora, volviendo a resolver su problema, supongo que HeaderItems
es una propiedad del objeto que está configurada como de DataContext
su Vista principal. Podemos conectar la DataContext
vista a cualquiera DataGridColumn
a través de algo que llamamos ProxyElement
.
El siguiente ejemplo ilustra cómo conectar un hijo lógico como ContextMenu
o DataGridColumn
a la vista principal.DataContext
<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 vinculante que encontró si no hubiera implementado el truco ProxyElement. ProxyElement es cualquier FrameworkElement que roba de DataContext
la Vista principal y se lo ofrece al hijo lógico como ContextMenu
o DataGridColumn
. Para eso, debe estar alojado como Content
un invisible ContentControl
que se encuentra bajo la misma Vista.
Espero que esto te guíe en la dirección correcta.
Una alternativa ligeramente más corta al uso de a StaticResource
como en la respuesta aceptada es 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>
La principal ventaja de esto es: si ya tiene un elemento que no es el ancestro de DataGrid (es decir, no el StackPanel
del ejemplo anterior), puede simplemente darle un nombre y usarlo como su x:Reference
lugar, por lo que no es necesario definir ningún elemento ficticio. FrameworkElement
en absoluto.
Si intenta hacer referencia a un antepasado, obtendrá un XamlParseException
error en tiempo de ejecución debido a una dependencia cíclica.