Friday, June 05, 2009

GridViewColumn CellTemplate does not work?

Well, today I came across this interesting question on WPF regarding setting the DataTemplate of a GridViewColumn not working properly. Everything looks fine in the code (assuming his DataTemplate generating method was fine) which got me interested to figure out what the issue is.

And then I made up this sample application whose XAML is shown below.

<Window x:Class="Window1" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" Loaded="Window_Loaded">
<StackPanel>
<ListView Name="lv" ItemsSource="{Binding Items}">
<ListView.View>
<GridView></GridView>
</ListView.View>
</ListView>
</StackPanel>
</Window>


The code-behind is simple. It sets the DataContext to itself and then fetches sample data using GetData() call. Then the GridView for the ListView is obtained and a new gridviewcolumn is added programatically. The CellTemplate is set on the GridViewColumn using SampleTemplate() method. The SampleTemplate() method constructs a DataTemplate programatically using the FrameworkElementFactory class and returns the same.





 
Private Sub Window_Loaded(ByVal sender As System.Object, _
ByVal e As System.Windows.RoutedEventArgs)
Me.DataContext = Me 'set the datacontext
Items = GetData()
Dim gv As GridView = CType(lv.View, GridView)
Dim gvc As New GridViewColumn
gvc.Header = "Test"
'Display member binding
gvc.DisplayMemberBinding = New Binding(".")
gvc.CellTemplate = SampleTemplate()
gv.Columns.Add(gvc)
End Sub

Private Function SampleTemplate() As DataTemplate
Dim dt As New DataTemplate
dt.DataType = GetType(String)
Dim fef As New FrameworkElementFactory(GetType(TextBox))
Dim bd As New Binding(".")
fef.SetBinding(TextBox.TextProperty, bd)
dt.VisualTree = fef
Return dt
End Function

Public Property Items() As IEnumerable(Of String)
Get
Return GetValue(ItemsProperty)
End Get

Set(ByVal value As IEnumerable(Of String))
SetValue(ItemsProperty, value)
End Set
End Property

Public Shared ReadOnly ItemsProperty As DependencyProperty = _
DependencyProperty.Register("Items", _
GetType(IEnumerable(Of String)), GetType(Window1), _
New FrameworkPropertyMetadata(Nothing))

Function GetData() As IEnumerable(Of String)
Dim x As New ObservableCollection(Of String)
x.Add("Item 1")
x.Add("Item 2")
x.Add("Item 3")
Return x
End Function




And when I run the application all I see is …



image



Ok the binding works and I see my data but somehow the celltemplate is not being reflected. So, I was able to reproduce the issue the question presented.



So I dig around and looked in the documentation for CellTemplate where I found the following.



image



So it basically means that if DisplayMemberBinding is used, then CellTemplate would be ignored and so on with the CellTemplateSelector. So I comment the line that sets DisplayMemberBinding and run again.



image







Yes! its working as expected.



So whats the moral of the story? Actually there are three morals:



1. First of all, when using CellTemplate, do not set DisplayMemberBinding.

2. Refer to the documentation, especially the remarks and code samples section for a method or property. MSDN folks did an amazing job (ofcourse the WPF guys as well).


3. MSDN WPF Forums is amazing and overall Microsoft.NET folks rocks big time.

3 comments:

Jew Paltz said...

Ok, so "when using CellTemplate, do not set DisplayMemberBinding" is your conclusion.
How about when you have two columns in a list view and they both use the same template but for different fields of the object being used for the row? How can you set the same template to both columns and then assign a different relative path to each one?

There are many examples of this.
Home address and business address (in a simple form for a simple user) should be listed next to each other. Each one contains {address, city, state, zip}, they are both formatted the same so you should be able to reuse the template. However in one column you need the template to display the homeAddress and in the next you need it to display the businessAddress.

Bhargav-------------------------------------------- said...

Jew,

Thanks for visiting.

It wasnt my conclusion. It is what I got to know having researched the documentation.

I will answer your question soon as I am currently working on something.

Anonymous said...

Thank you very much for this information. It seems I just overlooked it on the MSDN pages.