Mam DataGrid
z wierszem, który ma obraz. Ten obraz jest powiązany z wyzwalaczem do pewnego stanu. Kiedy stan się zmieni, chcę zmienić obraz.
Sam szablon jest ustawiony na HeaderStyle
a DataGridTemplateColumn
. Ten szablon ma pewne powiązania. Pierwszy wiążący dzień pokazuje, jaki jest dzień, a stan zmienia obraz za pomocą wyzwalacza.
Te właściwości są ustawiane w ViewModel.
Nieruchomości:
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,
});
}
Siatka danych:
<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>
Teraz, kiedy uruchamiam projekt, obrazy się nie wyświetlają i pojawia się ten błąd:
Błąd System.Windows.Data: 2: nie można znaleźć regulującego elementu FrameworkElement lub FrameworkContentElement dla elementu docelowego. BindingExpression: Path = HeaderItems [0]; DataItem = null; element docelowy to „DataGridTemplateColumn” (HashCode = 26950454); Właściwość docelowa to „Nagłówek” (typ „Obiekt”)
Dlaczego ten błąd się wyświetla?
Odpowiedzi:
Niestety żaden
DataGridColumn
hostowany podDataGrid.Columns
nie jest częściąVisual
drzewa i dlatego nie jest połączony z kontekstem danych datagrid. Dlatego powiązania nie działają z ich właściwościami, takimi jakVisibility
lubHeader
itp. (Chociaż te właściwości są prawidłowymi właściwościami zależności!).Teraz możesz się zastanawiać, jak to możliwe? Czy ich
Binding
własność nie powinna być powiązana z kontekstem danych? Cóż, to po prostu hack. Wiązanie tak naprawdę nie działa. W rzeczywistości to komórki datagrid kopiują / klonują ten obiekt wiążący i używają go do wyświetlania własnej zawartości!A teraz wracając do rozwiązania twojego problemu, zakładam, że
HeaderItems
jest to właściwość obiektu, który jest ustawiony jakoDataContext
widok nadrzędny. My może połączyćDataContext
z widokiem na dowolnyDataGridColumn
poprzez coś nazywamyProxyElement
.Poniższy przykład ilustruje sposób łączenia logicznego elementu podrzędnego, takiego jak
ContextMenu
lubDataGridColumn
z widokiem nadrzędnymDataContext
<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>
Powyższy widok napotkał ten sam błąd wiązania, który znalazłeś, jeśli nie zaimplementowałem hackowania ProxyElement. ProxyElement to dowolny element FrameworkElement, który kradnie element
DataContext
z widoku głównego i oferuje go logicznemu elementowi podrzędnemu, na przykładContextMenu
lubDataGridColumn
. W tym celu musi być hostowany jako aContent
w niewidzialnym,ContentControl
który jest pod tym samym Widokiem.Mam nadzieję, że poprowadzi cię to we właściwym kierunku.
źródło
Parent
podczas gdyDataGridTextColumn
nie ujawnia swojejDataGridOwner
właściwości. Zobacz, jak odbywa się wiązanie elementów kontekstu za pośrednictwem powiązania RelativeSource w mojej odpowiedzi Menu kontekstowe Powiązanie z kontekstem danych okna nadrzędnegoNieco krótszą alternatywą dla użycia
StaticResource
jak w akceptowanej odpowiedzi jestx: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>
Główną zaletą tego jest to, że jeśli masz już element, który nie jest przodkiem DataGrid (tj. Nie jest to
StackPanel
w powyższym przykładzie), możesz po prostu nadać mu nazwę i użyć go jakox:Reference
zamiast niej, stąd nie musisz definiować żadnego atrapyFrameworkElement
w ogóle.Jeśli spróbujesz odwołać się do przodka, otrzymasz
XamlParseException
w czasie wykonywania z powodu cyklicznej zależności.źródło