DarksideCookie

Come to the dark side...we have cookies!

Bindings, the glue that connects the view to the viewmodel part 2 (and apparently a bunch of data validation information)

Welcome back! Or possible Welcome! Sort of depends on if you saw my previous post… Anyhow, you have probably figured out that it is time to go on with even more binding information. In the previous we looked at the basic binding syntax and functionality. So, what’s left? Well, a bunch of small, medium and large details…

So lets start…the first part is to show you the syntax for something I mentioned in the previous post. Namely the Source parameter for the binding. What you do by setting the Source, you tell the binding what object to use as data context. Doing it from Xaml, means that you must start off by defining an object to use. This is done by adding an object to a Resource collection of any of the parent controls in the Xaml.

<UserControl
...
xmlns:local="MyNamspace;MyAssembly">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.Resources>
<local:MyObject x:Key="MyResourceObject" />
</Grid.Resources>
<TextBlock Text="{Binding Path=MyProperty, Source{StaticResource MyResourceObject}}" />
...

 

I personally do not like this. Why? Well, because that doing this couples our view to the model/viewmodel, making it less flexible. I like having a clean separation between the layers. But, Blend does it like this. When you use Blend to set the views bindings, it automatically adds an instance of the selected object to the usercontrol’s Resource collection and then uses Source to reference it. This is not my way of doing it, but it is nice to use Blend to simplify do the (not so) heavy lifting. Having said that, there are still times when this is useful…especially if you create your binding from code… More about that later…

The next binding feature to look at, would be the element-to-element binding. This is new in Silverlight 3. It makes it possible for us to bind one control’s property to another control’s property. This can be used in a LOT of ways. But let’s use a simple example

<Slider x:Name="MySlider" Margin="-120,0,0,0" Width="100" Minimum="0" Maximum="100" />
<TextBlock Text="{Binding Path=Value,ElementName=MySlider}" Margin="0,0,-100,0"
VerticalAlignment="Center" HorizontalAlignment="Center" />

This Xaml binds the TextBlock’s Text property to the Value property of a Slider. That is FAR from useful, but it shows the idea. The cool thing is that you can do it TwoWay as well. You can combine the Binding properties exactly how you want…

But this brings me to an important thing. You can only bind properties that are of type DependencyProperty. What does this mean? Well, it means that the properties that you want to bind, must be built for it. In most cases this is not a problem, but sometimes it causes problems… In those cases, it is sometimes possible to solve it by inverting the whole thing and using a TwoWay binding. There is a good example on this blog.

I’ll return to DependencyObject and DependencyProperty in the future, but for now, just accept that this is the way it is. And if you are having problems with a binding, make sure that the property you bound is a DependencyProperty…

By now, I have covered most of the things you can do with bindings in Xaml. Just remember that the secret to be a good “data binder”, is to find new ways to use the options you have. But, there are more ways to use bindings. Bindings are objects, like everything else in .NET. The only thing that makes them a little special is that they have their own textual syntax to define them. But since they are objects, they can be created in code as well. This can be very useful in a lot of cases…

Won’t binding from code behind break the whole "clean separation” idea? Well, a little, but then not at all. As long as you are binding to the DataContext, or to an internal object in a control, the binding is actually oblivious of the type of the viewmodel. It only breaks the separation if you decide to make it so…

So how do we create a binding from code? Well, as long as you know how to do it in Xaml, you just do the same in code. Let’s use the same example as previously, the slider. Lets remove the binding from the Xaml and give the TextBlock a name.

<Slider x:Name="MySlider" Margin="-120,0,0,0" Width="100" Minimum="0" Maximum="100" />
<TextBlock x:Name="tb" Margin="0,0,-100,0" VerticalAlignment="Center"
HorizontalAlignment="Center" />

And if we want to create the same binding in code, it looks like this

Binding binding = new Binding();
binding.Path = new PropertyPath("Value");
binding.ElementName = "MySlider";
binding.Mode = BindingMode.OneWay;
tb.SetBinding(TextBlock.TextProperty, binding);

As you can see, this is exactly the same as the Xaml based one. The only difference is that the properties are set individually and by using strongly typed objects. The code however, shows the previously mentioned DependencyProperty demand a bit more clearly. The SetBinding method takes two parameters, a DependencyProperty and a Binding. However, I would recommend doing it in Xaml unless you have a particular reason not to.

Silverlight 3 added another nice code based binding feature. It is now possible to get hold of the binding from code. Or rather, you can get hold of the BindingExpression. The object in turn makes it possible to get hold of the actual Binding. The BindingExpression has a nice little feature that can be good to know of. It has a UpdateSource() method.

Let’s revisit the TextBox. As you saw in the previous post, I used a couple TextBoxes to support data input. These boxes were bound with a TwoWay binding, updating the viewmodel when modified. However, you also saw that they only updated the binding when the TextBox lost it’s focus. Using the BindingExpression, you can modify this. Imagine the following Xaml

 

<TextBox x:Name="MyTextBox" Margin="-120,0,0,0" Height="25" Width="100" 
KeyUp="MyTextBox_KeyUp" />
<TextBlock x:Name="tb" Margin="0,0,-100,0" VerticalAlignment="Center"
HorizontalAlignment="Center"
Text="{Binding Text,ElementName=MyTextBox}" />

In the MyTextBox_KeyUp handler, I do the following

private void MyTextBox_KeyUp(object sender, KeyEventArgs e)
{
tb.GetBindingExpression(TextBlock.TextProperty).UpdateSource();
}

 

This gives us live updates of the binding. Not that this always is a good thing, but in certain situations it will help.

This brings me to the last thing I want to talk about. Data validation. So how do I validate what the user input. In ASP.NET we have Validators, that seems like a good solution. Or not… Getting data validation is a procedure with several steps. The first one is to implement the actual validation. Where would you choose to put the validation? Well, I would say, in the business object / viewmodel. This builds on the idea that we can have several clients using the same objects, and get the validation for free. So let’s put it there…

Remember the Person class? Well, if not, look at the previous post, cause it will come back here. However, I will simplify the form. I’ll limit it to a single TextBox. That isn’t even a simplification, that is a new demo…Anyhow, what I want to do is validate the Name property. I want to make sure that the user supplies a name. The first step is to give the usercontrol a DataContext

public Page()
{
InitializeComponent();
Person chris = new Person() { Name = "Chris Klug", Age = 29, IsMarried = false, Mood = Mood.Happy };
this.DataContext = chris;
}

 

Next I create the Xaml that I need. It is as I said a simple TextBox, with a bound Text property. There is nothing complicated here at all, except for one little thing, the ValidatesOnExceptions property

<TextBox Height="25" Width="100"
Text="{Binding Name, Mode=TwoWay,ValidatesOnExceptions=true}" />

By setting the ValidatesOnExceptions to true, the binding will notify the system when an exception is caused by the binding. There are a few places where this exception can come from. First of all, the binding context will cause an exception to be thrown if you get a type mismatch. So, that part is taken care of. The second place is by the model, which is what I will use. And the final place is by using a validation attribute. I will get back to this. But let’s start by validating the value in the model. Just modify the Person class in the following way

public string Name
{
get { return _name; }
set
{
if (_name == value)
return;

if (string.IsNullOrEmpty(value))
throw new ArgumentException("A person has to have a name");

_name = value;
OnPropertyChanged("Name");
}
}

 

As you can see, i is just a matter of throwing an Exception when needed. This causes the following when emptying the TextBox and tabbing out of it…

binding1

This layout can of course be templated and turn into some form of cool notification. THe way that this works, is that the input controls in Silverlight 3 has got a new ViewState. Or rather 3 of them – Valid, InvalidUnfocused and InvalidFocused. With these new states you can do what ever you want in the form of notification.

But I did mention a third type of validation, namely using validation attributes. If you have a look in the System.ComponentModel.DataAnnotations namespace, you will find a few attributes that you can use to validate the values inserted into a property. This makes it really easy to get error messages and data validation in controls that support this attributes (mainly the DataGrid). But you can use it yourself as well. In this case, all I need is a RequiredAttribute like this

[Required(ErrorMessage="A person must have a name")]
public string Name { get; set; }

 

Just remember that you need to add a reference to the System.ComponentModel.DataAnnotations assembly. But there is a little more that you have to do, unless you are only working with controls that validates on its own. The TextBox doesn’t, so we have to add a little bit of validation on our own

[Required(ErrorMessage="A person must have a name")]
public string Name
{
get { return _name; }
set
{
if (_name == value)
return;

Validator.ValidateProperty(value,
new ValidationContext(this, null, null) { MemberName="Name" });

_name = value;
OnPropertyChanged("Name");
}
}

 

So, what makes this so useful? Well, because we can add more than one attribute and validate according to several different business rules with the same code. Say that I want to limit the length of the string as well. Maybe the database has a limit…or something else. Making that change is just a matter of adding a new attribute

[StringLength(10,ErrorMessage="The name must be less than 10 characters")]
[Required(ErrorMessage="A person must have a name")]
public string Name
{
...

The cool thing is I don’t have to modify the validation code. And there are of course a bunch of validation attributes supplied by Microsoft, but we can also build our own. This can be done in two ways, either by inheriting the ValidationAttribute class, which makes it re-usable, or by adding a CustomValidationAttribute and define a method that does the validation…

But what if I just want to be notified if something has gone wrong and then handle this on my own. Maybe build up some cool, custom way of notifying the user. Well…that isn’t too hard. Just add another property to the binding. This time it is the NotifyOnValidationError. Just set that property to true and you are done…or…well..sort of…First you need a handler. The exception will bubble up the controls tree, and you can catch it wherever you want, as long as you are higher up in the tree. The simplest way, for demo purposes, is to catch it in the usercontrol or layout root

<UserControl x:Class="MyClass"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid x:Name="LayoutRoot" Background="White"
BindingValidationError="LayoutRoot_BindingValidationError">

<TextBox Height="25" Width="100"
Text="{Binding Name, Mode=TwoWay,ValidatesOnExceptions=true,
NotifyOnValidationError=true}"
/>

</Grid>
</UserControl>

The handler looks like this

private void LayoutRoot_BindingValidationError(object sender, ValidationErrorEventArgs e)
{
}

In the handler you get access to all the information you need about the exception. After having a look at the information, you need to make the decision if you wish to handle it yourself or just let it bubble on. If you handle it, set the e.Handled property to true and Bob’s your uncle. The bubbling stops and you have taken control…

But instead of me talking more about this, I recommend having a look here for some more complete examples... This was about bindings and not data validation. But I couldn’t help myself…

 

I think I have covered most of the binding information that you need to be able to build data bound applications. There are a few more things that I will leave up to you to figure out on your own. I have for example not shown you that you can change the Trigger that causes the data binding refresh and that you can set the binding to bind to a relative source (self or templated parent). So as you can see, there is more to learn and use, but I hope and think that this will take you pretty far.

Thank you for reading this, and I hope that you have got some good information that you can use in your future endeavors.

Cheers!

Posted: Jul 27 2009, 12:33 by ZeroKoll | Comments (0) |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: Silverlight
Manage post: :)
Comments are closed