views:

138

answers:

2

Or "how do you make sure all your bindings stay correct?"
(this is kinda lengthy, but bear with me, I tried to make it as short as I could)

Consider the following example:

    <TextBox Name="tb" />
    <TextBlock Text="{Binding Text.TheProp, ElementName=tb}" />

It is perfectly known at compile time that the binding is incorrect (i.e. the parser knows the type of element tb, and therefore, it knows the type of it's Text property, and therefore, it knows that TheProp doesn't exist).
Yet, this code will compile and run (although with a binding error message in debug output).

This behavior may come in very handy in some situations: no matter what exact type my data is, as long as it has appropriately named properties, I'm ok. Thus, we get sort of "declarative duck typing".

However, duck typing is not always a good thing.
Specifically, while using the MVVM pattern, I know (most of the time) the exact types of all my ViewModel objects. On the other hand, the models become more and more complex over time, which gets me worried about future refactoring: what if I decide to rename some properties, or, God forbid, put them in a separate aggregated object? What's going to happen with all my bindings then? Will I have to rake all XAML files by hand? And even without refactoring - what if I simply make a typo?

A similar problem is already solved in other places of XAML. If, for instance, you put an incorrect property name in Style/Setter/@Property, you get a compile time error.
TemplateBinding also provides such verification. Which is very handy.

So, ideally, I would love to see something like this:

ProductViewModel.cs:

    public class ProductViewModel
    {
        public Name { get; set; }
        public Price { get; set; }
    }

ProductView.XAML:

    <UserControl x:Class="Shopping.View.ProductView"
                 x:DataContextType="vm:ProductViewModel"
                 xmlns:vm="clr-namespace:Shopping.ViewModel"
                 ... >
        <TextBox Text="{Binding Name}" />  <!-- OK -->
        <TextBox Text="{Binding Price}" /> <!-- OK -->
        <TextBox Text="{Binding ABC}" />   <!-- Compile time error: there is no property ABC in ProductViewModel -->
    </UserControl>

ShoppingCart.XAML:

    <UserControl x:Class="Shopping.View.ShoppingCartView"
                 x:DataContextType="vm:ShoppingCartViewModel"
                 xmlns:vm="clr-namespace:Shopping.ViewModel"
                 ... >
        <ItemsControl ItemsSource="{Binding Products}"
                      ItemType="vm:ProductViewModel" >  <!-- Static check happens here 
                                                             ShoppingCartViewModel.Products must 
                                                             implement IEnumerable<ProductViewModel> -->
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="vm:ProductViewModel">
                    <view:ProductView /> <!-- DataContext is known to be of correct type
                                              because of DataTemplate.DataType property -->
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </UserControl>

But let's get back to reality. In reality, all that dreaming is just not going to happen in the near future.

However, I am sure I'm not the first person to have this problem.
So, finally, the question is: How do you make sure your bindings are correct? And that they stay that way?

+2  A: 

As a practical matter, I've never found this to be a problem, at least when using the MVVM pattern. The view model only exists in support of the view. I'm not going to change one without changing the other. Refactoring the view model isn't going to break the bindings in the view because it makes no sense to refactor the view model for its own sake. You'll only refactor the view model when (and because) you're changing the design of the view.

The other reason that I don't have this problem is that I'm not developing the view model independently of Expression Blend. For all but the most trivial UIs, I build my view models using some kind of dependency injection so that I can create a test data source that's usable in Expression Blend. When I create bindings in Blend, I know right away whether or not I've done it right.

As with MVVM in general, doing this is an unbelieveable pain in the ass until you understand what you're doing and why. (This long blog post by Jonas Follesø gives a pretty good overview of how to use Ninject for this purpose, though there are no end of other frameworks you can use.) I'm sure that there are problems that I haven't uncovered yet with this methodology - above and beyond the problem that I've added DI frameworks and Expression Blend onto the heap of things that I need to understand to develop WPF applications.

Pablo Casals said that constant experimentation keeps the artist young. I don't feel young.

Robert Rossney
Well, it seems like we understand MVVM differently. As I came to understand it, one should think of ViewModel as of the actual UI of the application, while View is just sort of a "skin" for that UI. I can cite, for example, this article: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx This approach enabled a few nice things. For instance: one can have more than one "skin" for the same UI. Another example: "skin" can be designed [relatively] independently from the UI itself, and by a different person, too (usually referred to as a "designer" :-). [continued in the next comment]
Fyodor Soikin
Yet another example: in case of several "skins", some of them can be designed independently, by a different person, or by a different company, or even by the customer! And, of course, what about unit testing? If you see ViewModel as "support" for View, you should always keep View in mind while designing unit tests. And even from your point of view, it still doesn't work: if the View is being designed by a different person ("designer"), how are you going to change it together with the ViewModel?
Fyodor Soikin
And lastly, regarding the need for refactoring. I beg to differ on this part, too. Refactoring is regularly utilized when the system becomes overly complex, so that it needs breaking down into simpler systems, or rearranging its components in a different manner. A simple example: suppose one screen should show some information about some product. Say, name and price. So my ViewModel has those properties, and the view binds to them. Then a requirement comes in: add a "manufacturer" field. Just a simple string. Ok, done. Now my ViewModel has another property named "Manufacturer".[see next comnt]
Fyodor Soikin
A day later, another requirement comes in: add manufacturer address. Ok, now it becomes apparent that the next requirement will probably be "add manufacturer logo" or something. Now it probably makes sense to create another data structure for manufacturer and have product reference it. Depending on other circumstances (like complexity of the ViewModel), I may also decide that it makes sense to create a separate ViewModel for manufacturer, and have ProductViewModel reference it, too. So that's your refactoring, right here. Now I need to go and hunt down all related XAML bindings.
Fyodor Soikin
See, I'd never permit any of the things you're describing. My view models are tightly coupled to my views. Period. If I want to have three similar views of the same data, I don't create one view model that's a superset of the three views, I subclass the view model. The only reason I'd create a separate view model for manufacturer is if I were also creating a separate view. If you decide that the view model is overly complex without extending that to the view, you're introducing a category of complexity that's worse than what you're trying to fix.
Robert Rossney
And as far as your point about independently developing skins goes: that's the point of making the whole thing usable in Blend. It gives you a dynamic platform for functional testing views as they're being built. If you do that, the binding problems you're concerned about show up at the moment the designer introduces them. The view and view model can get out of sync if the designer removes things from the view without telling the developer, and this is certainly an issue. But it's a comparatively small one.
Robert Rossney
Yes, the very last thing is exactly the issue I'm trying to solve. But I absolutely do not agree that is's a comparatively small one. An issue is small when it can be easily caught and fixed as part of the routine process. And if the designer accidentally puts the view out of sync with the ViewModel and doesn't immediately notice it, then it probably is so subtle that it may never be noticed in manual testing - until some customer does exactly that thing that causes the the issue to manifest.
Fyodor Soikin
And as far as "several views for same viewmodels". When I wrote "several skins", I meant exactly that: skins. As in, the ability to skin your UI in different themes. How about that?
Fyodor Soikin
I don't see how the view designer removing things from the view without telling the view model programmer that he's done so is one that static verification of bindings is going to help with. I'm also not seeing how a designer working in Blend with blendable components is not going to see that something's not working immediately, at least not in my experience with using Blend as part of the development process. But my experience is pretty limited. Have you built blendable UI components and encountered these kinds of problems?
Robert Rossney
No, I haven't encountered these problems with WPF yet. I have had similar problems in the past, but that was with other technologies. And that experience shows that when I do encounter these problems, it is already too late. Usually it turns out that I had to design the system in a different manner from the start, and now it's too late, there is no resources and time to rebuild everything, and we just have to solve these problems by painstaking manual labor or simply live with them, weeding them out as customers discover them. That's hardly good work :-(
Fyodor Soikin
Oh, and to answer the other question: "how is static verification going to help in the situation with designer changing things?" As I see it, the static verification would be part of the regular build process, so that when the designer changes something and checks in the changes into the source control, the next build would fail on static bindings verification, and will clearly indicate which binding is incorrect. From there, I can look what the last change was and who made it, and go kick that person's ass.
Fyodor Soikin
+5  A: 

How about static analysis of your Xaml performed as a post-build step?

As part of .Net 4, Microsoft released a new System.Xaml library to provide robust Xaml parsing and serialization support independent of WPF. They are now beginning to build all kinds of interesting things on top of it, some of which may help you out.

In the XamlToolkit, for example, you'll find the XamlDOM that enables you to do easy static analysis of Xaml files. And taking that a bit further, there's FxCop rules for XAML.

Of most interest is Rob Relyea's BindingFinder that has the explicit goal of type checking Bindings in Xaml. This requires that you have type hints in your Xaml, like the DataType attribute in a DataTemplate, or the the new d:DataContext attribute on your Views (which Blend uses to provide design-time data). It then uses the XamlDOM to check that everything matches up.

Samuel Jack
...Aaaaaaaand 200 reputation goes to Samuel Jack! Thank you, sir, this is almost exactly what I was looking for. I was actually on the verge of implementing something like that myself, but having somewhere to start is always better. :-)
Fyodor Soikin
@Fyodor - glad I could be of service!
Samuel Jack