DataTemplate bindings frozen in DataGridTemplateColumn when using cal:Bind.Model

Topics: Conventions
Jun 7, 2011 at 9:45 PM

I'm having a bit of trouble getting cal:Binding.Model working within a DataGrid Column's CellTemplate.  Specifically, I want to clean this up:

<sdk:DataGridTemplateColumn SortMemberPath="IsIncluded" Header="INCLUDE" CellStyle="{StaticResource CenteredCell}" IsReadOnly="True">
	<sdk:DataGridTemplateColumn.CellTemplate>
		<DataTemplate>
			<CheckBox IsChecked="{Binding IsIncluded, Mode=TwoWay, ValidatesOnDataErrors=True, ValidatesOnExceptions=True, ValidatesOnNotifyDataErrors=True}" />
		</DataTemplate>
	</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>

By allowing it to use conventions, like so:

<sdk:DataGridTemplateColumn SortMemberPath="IsIncluded" IsReadOnly="True">
	<sdk:DataGridTemplateColumn.CellTemplate>
		<DataTemplate>
			<CheckBox x:Name="IsIncluded" cal:Bind.Model="{Binding}" />
		</DataTemplate>
	</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>

This works very well for the binding, however when I try to sort the column, none of the checkbox controls get sorted, whereas all the other columns in the grid get rearranged as expected.  In fact, every CellTemplate that uses this technique gets frozen in it's original order as the other columns sort fine.  The scary part is that the databinding does get updated to the new row, so any changes apply to the record that lines up with the frozen checkbox.

At first I thought it had to do with row virtualization, but I'm pretty sure that is disabled since I'm using a PagedCollectionView with grouping.

Any idea what's happening here?

Jun 7, 2011 at 9:54 PM
Edited Jun 7, 2011 at 9:54 PM

A little more information; just the inclusion of cal:Bind.Model="{Binding}" will trigger this behavior, even if I've explicitly set my own bindings.

Jun 12, 2011 at 9:59 PM

Is cal.Bind.Model the recommended approach for utilizing caliburn conventions within a DataGrid's DataTemplate?  I used the ideas brought forth in this discussion: http://caliburnmicro.codeplex.com/discussions/243369.

Coordinator
Jun 12, 2011 at 10:50 PM

Personally, I wouldn't use conventions inside a datagrid template :) Usually, with the datagrid, I just revert to explicit bindings since there generally isn't too much advantage to conventions in that case. If you had some consistent biding issues with grids you wanted to improve, you might investigate creating a custom behavior.

Jun 20, 2011 at 7:20 PM

Conventions make markup much cleaner, and the more complex a DataTemplate becomes, the more useful it is to have conventions.  At some point when I have time, I'll step through the source to try and find the bug, and post my findings back here.

Jan 3, 2012 at 3:44 AM
Edited Jan 3, 2012 at 3:46 AM

I'll bet the problem is attributable to virtualization of the cells or list items. I know you said you disabled virtualization ... but I'll bet you didn't succeed.

I've determined that Bind.Model won't work if the templated item is reused as it is in a virtuallized control such as a VirtualizingStackPanel; that's the control inside a ListBox. I suspect there is some virtualizing going on in the DataGrid and the problem I'm about to describe is manifested upon sorting.

A virtualized items control doesn't build a visual templated item for every item in your data source. It only builds enough of them to display. When an item scrolls off the screen, the visual templated item that goes with it is re-used for displaying the next data item, typically displayed at the bottom. SL sets the re-cycled templated item's DataContext to the new data source. But CM doesn't know that it should clear the previous bindings and establish new ones to the new data source. Therefore, the re-cycled visual item shows the values from the original data item (whose bindings are still alive) even though the DataContext has changed to the new data item.

It isn't going to be easy to fix this, I think, because it would be tricky to distinguish between bindings that CM wrote (these should be cleared) vs. explicit bindings that you wrote (which should be preservered and would be reapplied by SL/WPF automatically). I peeked at the source and walked quickly away. I think it could be fixed ... but it wouldn't be trivial.

Rob probably didn't encounter this problem because he followed his own advice and used explicit bindings in DataTemplates ... at least the DataTemplates in DataGrids which, I suspect, are virtualized.

In sum, it seems you will have to go back to explicit binding for templates used in a DataGrid.

I share your regret as I like to use conventions wherever they seem natural ... as they do inside a DataGrid's item template.

Coordinator
May 2, 2012 at 2:55 AM
Edited May 2, 2012 at 12:31 PM

In this scenario, use the Bind.ModelWithoutContext property instead. Though, I still usually recommend using explicit bindings for this scenario.