Before you answer: This question is about WPF's ListView
control (with GridView
). It is not about WPF's DataGrid
control, ASP.NET's DataGrid
or ListView
controls, or WinForm's DataGridView
or ListView
controls. They all sound very similar and are often confused, but an answer for the wrong type of control is both unhelpful to me and more importantly a waste of the time you took to write the answer, which I believe should not be squandered.
I have a ListView
control with GridView
that has several columns bound to my view-model's properties. I can easily customize the visual appearance of the GridViewColumn
's cells by specifying a CellTemplate
(either inline or via a resource).
Now I have a particular property on my view-model; its type is an abstract base class and its value can be one of several derived types. Each derived type should have a different DataTemplate
in the cell. Luckily, GridViewColumn
has a CellTemplateSelector
which does exactly what I want, but requires writing some plumbing code. But looking at the page for DataTemplateSelector
it says:
Note that if you have objects of different types you can set the DataType property on the DataTemplate. If you do that then there is no need to create a DataTemplateSelector. [...] For more information, see Data Templating Overview.
Hurray! No need to write plumbing code. My types are different so this seems like a perfect fit. But alas, even after I defined a DataTemplate
with a DataType
that matches a specific derived type of one of the databound columns (bound using GridViewColumn
's DisplayMemberBinding
), it had no effect.
I simply want to display a different DataTemplate
according to the runtime type of a certain column in my GridView
. Are DataType
-targeted DataTemplates
simply incompatible with GridView
? Is there a way to use them, or must I resort to specifying a CellTemplateSelector
? Perhaps there is a way to specify multiple DataTemplate
s inside GridViewColumn
's CellTemplate
, so that the DataType
property will have an effect?
WPF allows you to insert objects such as ViewModels
in the Logical Tree, while a DataTemplate
can be used to tell WPF how to draw the specified object when drawing the Visual Tree.
An implicit DataTemplate
is a DataTemplate
that only has a DataType
defined (no x:Key
), and it will be used automatically whenever WPF tries to render an object of the specified type in the VisualTree.
So, you can use
<Window.Resources>
<DataTemplate DataType="{x:Type local:ViewModelA}">
<local:ViewA />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModelB}">
<local:ViewB />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModelC}">
<local:ViewC />
</DataTemplate>
</Window.Resources>
to tell WPF to draw ViewModelA with ViewA, ViewModelB with ViewB, and ViewModelC with ViewC.
If you only want this applied to your GridView instead of to the entire Window, you can specify <GridView.Resources>
(or <ListView.Resources>
, I can't remember which one)
It should be noted that if you are binding your column using the DisplayMemberBinding
, it will render as a TextBox
with the Text
value bound to your property, which means it will render YourViewModel.ToString()
instead of trying to draw the ViewModel in the VisualTree using your DataTemplate
.
To avoid that, simply set the CellTemplate
to something like a ContentPresenter
with the Content
property bound to your ViewModel
, and it will render your ViewModel using your implicit DataTemplates
<GridViewColumn Header="Some Header">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding YourViewModelProperty}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
The implementation of DisplayMember*
properties is crap, i have no idea why they thought that binding a TextBlock
instead of a ContentPresenter
would be a good idea.
I would recommend an attached property or a subclass with a respective property to override this. You just need to make it create a DataTemplate
containing a ContentPresenter
whose Content
is bound to the targeted property, that will allow for implicit DataTemplating
. This deferring DataTemplate
then should be assigned as the CellTemplate
of the column.