Gotcha with order of View.Model and View.Context

Topics: Bugs
Oct 7, 2011 at 3:26 PM

Hi folks. I've just been bitten by this and thought I'd share.

I have a ViewModel and Caliburn is happily finding and displaying the appropriate View. I wanted to add a new View so added this:


<ContentControl vm:View.Model="{Binding}"
                vm:View.Context="{Binding SubSectionViewMode, Mode=TwoWay}" />


So I was trying to insert a new view based on the value of the string property SubSectionViewMode on my ViewModel, and I wanted this new View to share the top-level ViewModel.

When running, though, it causes the whole screen to disappear - no exceptions, no logs that look suspicious, nothing. Looking in Silverlight Spy, I see an empty element tree, no views or anything. However, the logging shows that Caliburn successfully finds and binds my child view.

If I change the snippet to this:

<ContentControl vm:View.Model="{Binding}"
                vm:View.Context="RequiredViewMode" />

Where the big difference is that the Context is now a string rather than a binding, then everything works as it should - Caliburn still finds and binds my child view, but no blank screen and it all gets displayed properly.

The only difference in the logging is that it appears the first snippet is first trying binding this ContentControl against the ViewModel, and resolving the original, top-level (cached!) View, to be put into the ContentControl. I assume trying to set the content of the content control to an ancestor in the element tree is what's causing the blow up.

Fixing it is simple, but unintuitive and kinda brittle:

<ContentControl vm:View.Context="{Binding SubSectionViewMode, Mode=TwoWay}" 
                vm:View.Model="{Binding}" />

If the Context attribute is first, then the Model property handler has a context to use and resolves the correct view and everything is good.

It's funny (and annoying) that when Context is a plain string, then it doesn't matter that it's specified second in the order of attributes in the XAML - the View.Context dependency property is set first (I checked in the debugger). Then the Model dependency property is set last. When they're both bindings, they are set in the order they appear in the XAML.

So. That's my gotcha. I couldn't find anything in the docs that state this order is required - perhaps it should be called out? And I noticed that there's a View.ExecuteOnLoad method - could this be used to make sure Caliburn only tries to set the content property once the whole element (and all of its attributes) has loaded?