Static verification of bindings
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 foll开发者_StackOverflow社区owing 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).
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?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.
Update: Resharper 6 now provides intellisense for data bindings, and warnings if you get your property paths wrong.
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.
精彩评论