You should see DataTemplate
in WPF as a Factory. Thus I think that you don't really need a new instance of the DataTemplate
, you just want it to be applied differently based on your context.
If I understand correctly your issue, the problem is that the DataContext
of your DataGrid
Cells is not correct : it's the Row ViewModel whereas you want it to be the Cell ViewModel (which makes perfect sense). This is however the basic behavior of the DataGrid and is probably tied to the fact that Cells in each rows are hold by a DataGridCellsPresenter (which is basically an ItemsControl
) whose ItemsSource
dependency property has not been set (thus explaining the bad DataContext
).
I've run into this problem and found two way to fix this (but I only managed to make one work).
First one is to subclass DataGridCellsPresenter and override OnItemChanged
method to set the ItemsSource manually.
protected override void OnItemChanged(object oldItem, object newItem)
{
var rowViewModel = newItem as ViewModel;
if (rowViewModel != null)
{
ItemsSource = rowViewModel.Items;
}
else
{
ItemsSource = null;
}
}
where rowViewModel.Items should point to something like data[x] in your case. However I ran into some troubles using this fix and couldnt make it work correctly.
Second solution is to subclass DataGridCell
and update the dataContext on change of the ColumnProperty
. You also have to subclass DataGridCellsPresenter
to make it create the right cell controls
public class MyDataGridCell : DataGridCell
{
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if (e.Property == ColumnProperty)
{
var viewModel = DataContext as YourViewModelType;
if (viewModel != null)
{
var column = (e.NewValue as DataGridTemplateColumn);
if (column != null)
{
var cellViewModel = viewModel[column.DisplayIndex];
DataContext = cellViewModel;
}
}
}
base.OnPropertyChanged(e);
}
}
public class MyDataGridCellsPresenterControl : DataGridCellsPresenter
{
protected override System.Windows.DependencyObject GetContainerForItemOverride()
{
return new MyDataGridCell();
}
}
Finally you will also have to override the DataGridRow default ControlTemplate to make it use your custom DataGridCellsPresenter
in place of the original DataGridCellsPresenter
.
<ControlTemplate x:Key="DataGridRowControlTemplate" TargetType="{x:Type DataGridRow}">
<Border x:Name="DGR_Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<SelectiveScrollingGrid>
<SelectiveScrollingGrid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</SelectiveScrollingGrid.ColumnDefinitions>
<SelectiveScrollingGrid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</SelectiveScrollingGrid.RowDefinitions>
<local:MyDataGridCellsPresenter Grid.Column="1" ItemsPanel="{TemplateBinding ItemsPanel}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<DataGridDetailsPresenter Grid.Column="1" Grid.Row="1" Visibility="{TemplateBinding DetailsVisibility}">
<SelectiveScrollingGrid.SelectiveScrollingOrientation>
<Binding Path="AreRowDetailsFrozen" RelativeSource="{RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type DataGrid}}">
<Binding.ConverterParameter>
<SelectiveScrollingOrientation>Vertical</SelectiveScrollingOrientation>
</Binding.ConverterParameter>
</Binding>
</SelectiveScrollingGrid.SelectiveScrollingOrientation>
</DataGridDetailsPresenter>
<DataGridRowHeader Grid.RowSpan="2" SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical">
<DataGridRowHeader.Visibility>
<Binding Path="HeadersVisibility" RelativeSource="{RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type DataGrid}}">
<Binding.ConverterParameter>
<DataGridHeadersVisibility>Row</DataGridHeadersVisibility>
</Binding.ConverterParameter>
</Binding>
</DataGridRowHeader.Visibility>
</DataGridRowHeader>
</SelectiveScrollingGrid>
</Border>
</ControlTemplate>