Data Binding in Silverlight
April 24th, 2010 § 5 Comments
Data Binding is a simple, yet powerful way to connect (bind) your business model and user interface (UI). When the data in your business model changes the UI reflects changes automatically and vice versa (when a user changes the data in the UI, the data in the business model is changed).
Binding establishes a connection between two dependant items. Basically, it is an automatic way of transferring information from one item to another.
Content
- Data Context, Dependency Property, Dependency Object
- Types of Binding
- Converters
- Validation in Binding
- Consuming Services
- Sample
Data Context, Dependency Property, Dependency Object
Data Context
In Data Binding we have two objects: target – the UI element and source – the data (business model, entity, etc). While the source property can be a normal .NET property, the target property must be a DependencyProperty. You can read more about that later in the tutorial.
The DataContext is actually the source of data bound to the target. The default source of a binding is the data context. DataContext is inherited to child elements and this makes it ideal for scenarios where you want to bind multiple elements to a single data source. DataContext is a property of FrameworkElement. Let’s see this in an example:
<local:ContactCard x:Name="JohnsBusinessCard"
FullName="John Smith"
Position="Silverlight Developer"
Email="john@smith.com" />
<StackPanel DataContext="{StaticResource JohnsBusinessCard}">
<TextBlock Text="{Binding FullName}"
FontWeight="Bold" />
<TextBlock Text="{Binding Position}" />
<TextBlock Text="{Binding Email}" />
</StackPanel>
ContactCard is a custom defined class with three properties – FullName, Position and Email.
All bindings set to the TextBlock elements inside the StackPanel have source the DataContext of the StackPanel - {StaticResource JohnsBusinessCard} in this case.
In case of items control we can think of the DataContext as the particular data item that we bind to at run time. So let’s say we have a list of ContactCard objects set as a DataContext to an ItemsControl. The data context of each of the items in the ItemsControl is the corresponding list element in the list (set as DataContext).
Dependency Property and Dependency Object
DependencyObject and DependencyProperty (DP) are fundamental concepts in Silverlight/WPF that provide the base for the underlying property system.
Dependency Properties are designed to support data binding, animations, default values, property validation, property metadata. Dependency Properties are properties that automatically notify interested parties each time their value changes.
Most of the properties in the framework are implemented as Dependency Properties thus allowing us to bind or use them in animations. Dependency Properties are widely used in custom control development.
Let’s see how to register a DP:
public static readonly DependencyProperty SizeProperty =
DependencyProperty.Register(
"Size",
typeof( double ),
typeof( MyControl ),
new PropertyMetadata(
0,
new PropertyChangedCallback( OnSizeChanged ) ) );
Wow! Looks a bit different than a normal property, isn’t it? Let’s explain. First, the DP must be static and readonly and the return type must be DependencyProperty. Then it comes the actual registration using the static Register method of the DependencyProperty class. The first parameter is the name of the DP you register. By convention, the name should be the same as the DP without the “Property” word. The next parameter is the return type of the property (double in this case). The third parameter defines the type of the class for which you register this DP. The last parameter defines the default value and the callback method to be called once the property value changes. Currently in Silverlight this is the only metadata you can use.
DP values are set/get in a different way than the traditional properties. To set/get values you should use the SetValue and GetValue methods inherited from DependencyObject. To make the use of a DP more transparent, a property wrapper is defined:
public double Size
{
get
{
return ( double ) GetValue( SizeProperty );
} set
{
SetValue( SizeProperty, value );
}
}
Types of Binding
With each binding you can set Mode which defines the direction of the data flow as well as when the binding occurs. There are three available binding modes in Silverlight:
- OneTime – set the target and forget the binding. This mode is useful when you display data and you know that this data will never change or you don’t need to change it. Changes in the source are not propagated to the target.
- OneWay – set the target and update it every time the source changes. This is the default mode. Use it in cases where the user can’t change the target, but you still want to keep the target updated once the source is changed from somewhere (say the code).
- TwoWay – keep both the target and the source updated. Once the source is changed its new value is propagated to the target and vice versa. This is useful in cases where the user can change the target and you want the changes to be written back to the source.
Let’s take a look at the syntax:
<TextBlock Text="{Binding Email, Mode=TwoWay}" />
Depending on your case you can select one of the three binding modes. To choose which one is most appropriate for your case you can try to answer yourself the following questions in this order:
- Do I need to update the target when the source changes? (yes - OneWay or TwoWay; no - OneTime)
- Do I need to reflect the changes done on the target to the source? (yes – TwoWay; no – OneWay)
When you use OneWay or TwoWay binding you want to keep the target (and the source in TwoWay mode) always updated. This is accomplished with implementing the INotifyPropertyChanged interface. It is used to notify clients that a property value has been changed. It is easy to implement interface (has just one event), but sometimes might be annoying.
class ContactCard : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged( string propertyName )
{
if ( this.PropertyChanged != null )
{
this.PropertyChanged(
this,
new PropertyChangedEventArgs( propertyName ) );
}
} private string fullName;
public string FullName
{
get
{
return this.fullName;
} set
{
if ( this.fullName != value )
{
this.fullName = value;
this.NotifyPropertyChanged( "FullName" );
}
}
}
}
Converters
A common scenario in data binding is that you need to format the data displayed to the user. Intuitive examples for that are formatting of DateTime and number values. In Silverlight and WPF this is achieved by applying a ValueConverter (or just Converter) to the data you are binding to.
Converters are classes that derive from the IValueConverter interface. This interface has two methods that should be implemented – Convert and ConvertBack. The Convert method is used when the data flows from the source to the target, while the ConvertBack is used when the data flows in the opposite direction – from the target to the source. In case your binding uses OneTime or OneWay mode you can go only with implementing the Convert method, but that is not considered a good practice as the converters are reusable chunks of code and you never know who and in what situation will use it.
Let’s see what a converter looks like:
x Here is implemented only the Convert method. It is important to note that you should always validate the value parameter as it could be from a type you don’t expect.
This is probably the simplest converter you can ever write. To make it more flexible you can use a ConverterParameter. In different scenarios you may want to format the DateTime value in a different way. The specific format can be passed through the ConverterParameter.
<UserControl.Resources>
<cnv:DateTimeConverter x:Key=”DateTimeConverter” />
</UserControl.Resources>
<TextBlock Text="{Binding Birthdate,
Converter={StaticResource DateTimeConverter},
ConverterParameter=YYYY}" />
Validation in Binding
In Silverlight 3 data validation is far easier than in previous versions. You simply need to throw an exception with a meaningful message in the property setter. Most of the controls in the framework have built in support for showing validation errors. Exception thrown in the property setter is propagated to the UI. Following the example from above, let’s make the property FullName required.
public string FullName
{
get
{
return this.fullName;
}
set
{
if ( this.fullName != value )
{
if ( string.IsNullOrEmpty( value ) )
{
throw new Exception ( "Please enter your name" );
}
this.fullName = value;
this.NotifyPropertyChanged( "FullName" );
}
}
}
Once you try to set the value of FullName to an empty string an exception will be thrown.
Let’s see what we need in XAML:
<TextBox Text="{Binding FullName, Mode=TwoWay,
ValidatesOnExceptions=True}" />
In the binding expression we set a new property ValidatesOnExceptions. Setting that property to True, checks for exceptions that are thrown during the update of the source property. If an exception is thrown, the binding engine creates a ValidationError with the exception and adds it to the Validation.Errors collection of the bound element. If you enter an invalid data in the FullName field (i.e. no data in this case), a red border appears in the TextBox and the error message is reported.
Another option you can set is the UpdateSourceTrigger property. It controls the timing of binding source updates. Its default value is Default which returns the default UpdateSourceTrigger value of the target dependency property. For most controls this trigger is fired on PropertyChanged event, however for the TextBox control it is fired on LostFocus.
You can also set UpdateSourceTrigger to Explicit. That means you have to control to manually update the source. This is useful when doing some time consuming data processing on the background.
Consuming Services
While you are working with Silverlight it is most common to use web services to deal with data. Consuming web services is easy as Visual Studio provides appropriate templates and tools for both creating and using a service. However there are certain things like performance, security, debugging and error handling that need special attention.
Create a WCF service
Visual Studio provides template for creating a WCF service that can be consumed from Silverlight. Inside a Web Application/Web Site project add a new item and select the Silverlight-enabled WCF Service template.
That creates for you a WCF service configured with custom binding with binary message encoding. What is binary message encoding? Follow the tutorial and you will understand.
Add Service Reference
To use this service from Silverlight open your Silverlight project in VIsual Studio, right click and select Add Service Reference. If your service is in the same solution you can click Discover to list all available services. Otherwise just type the service address. Choose a namespace and click OK.
That generates a service proxy class with implemented asynchronous methods for accessing your service. For more control over creating this service reference I recommend you to use the SlSvcUtil.exe available in the Silverlight SDK folder. This is a simplified version of the original SvcUtil and allows you more flexibility than the Add Service Reference command. If the project that you are working on uses automated build processes you can use the SlSvcUtil to automate the proxy class build. There are various options that can be used with the SlSvcUtil so just type SlSvcUtil /? in the command prompt (make sure you are in Silverlight SDK/Tools folder – by default found in %ProgramFiles%\Microsoft SDKs\Silverlight\v3.0\Tools) at play with them. At the end of the Help Topics you can find examples on how to use it.
Create an Instance
Now that you have the proxy class generated in your Silverlight project you are ready to access it. First create an instance.
Handle Completed Event
Web service calls are done in asynchronous fashion so for every method in your service there is a Completed event with the appropriate EventArgs automatically generated. In my service (you can get the full source code for here) I have a method LoadClients and while we were adding the service reference a method LoadClientsAsync, an event LoadClientsCompleted and a LoadClientsCompletedEventArgs class were generated. Before you actually call the service method you need to handle the LoadClientsCompleted event so you can get the returned result.
EventHandler<LoadClientsCompletedEventArgs> loadClientsEventHandler = null;
loadClientsEventHandler += ( sender, e ) =>
{
// Handle LoadClientsCompleted event appropriately.
// Get the returned result through e.Result.
this.proxy.LoadClientsCompleted -= loadClientsEventHandler;
};
this.proxy.LoadClientsCompleted += loadClientsEventHandler;
Notice how we detach from the event on line 4. Remember that if you don’t detach, the event handler will be called as many times as you attach for it in a method.
Call a Method
Now you are ready to call a method and get the returned result.
private void LoadClients()
{
EventHandler<LoadClientsCompletedEventArgs> loadClientsEventHandler = null;
loadClientsEventHandler = ( sender, e ) =>
{
if ( e.Error != null )
{
// TODO: Handle exception.
}
else
{
this.Clients = e.Result;
}
this.proxy.LoadClientsCompleted -= loadClientsEventHandler;
};
this.proxy.LoadClientsCompleted += loadClientsEventHandler;
this.proxy.LoadClientsAsync();
}
Bind the Results in the UI
Finally, you can display the results in the UI through binding to the filled collection.
<ListBox x:Name="ClientsListBox"
ItemsSource="{Binding Clients}"
DisplayMemberPath="Name" />
<StackPanel>
<TextBlock Text="Name" />
<TextBox Text="{Binding SelectedItem.Name,
ElementName=ClientsListBox, Mode=TwoWay}" />
<TextBlock Text="Birth Date" />
<TextBox Text="{Binding SelectedItem.BirthDate,
ElementName=ClientsListBox, Mode=TwoWay,
Converter={StaticResource DateTimeConverter},
ConverterParameter=yyyy-MM-dd}" />
</StackPanel>
Here we bind the Clients collection to a ListBox and use two TextBox controls to bind the selected item from the ListBox so we can edit it. With TwoWay binding mode all changes done in the TextBox controls are populated to the data source.
So these are the basics of consuming web services from Silverlight. Take a look at the sample below and download the source code to get a better idea of what we just achieved. Now lets go on with couple of tips.
Performance
Consuming a web service means that data is transmitted over the wire from the server through the client. Optimizing the size and the speed of the transmitted data is crucial to have a good performance. One recommendation from Microsoft to optimize the speed is to always use binary message encoding in the service binding. That’s why when you create a Silverlight-enabled WCF service by default it is configured to use binary message encoding. With it you get increased server throughput and smaller message size. Remember that binary encoding optimizes speed, not just size. Using it with arrays, numbers, complex type graphs you get the best of it, while using it with small messages, strings it doesn’t get you much benefits.
Error Handling and Debugging
In default configuration if an error is thrown on the web service you get a message “Not Found” on the client nevertheless what was the actual error. That’s pretty annoying and you can’t understand what was the exception. With Silverlight 3 things changed in good direction – now you can use typed exceptions and pass them to the Silverlight client.
What is wrong and why “Not Found” is thrown instead of the real exception?
It is all about security. When an exception is thrown on the server, the service sends Error 500 (SOAP Standard) which cannot be read from browser plug-ins. With Silverlight 3 you can use ExceptionDetail and FaultException<T> to get a strongly typed faults to the client. I won’t get into this as there is a MSDN article which describes how to create and handle faults in SIlverlight – read it here.
Another tip for debugging is to enable the includeExceptionDetailInFaults in the service behavior defined in the service configuration. That will allow you to see the actual exception through use of HTTP debugger tools like Fiddler. See the web.config file in the web project found in the source code connected to this tutorial for more information where exactly to set this attribute.
Security
If you deal with web services then you should be aware of security, review the different option for authentication you have and choose the one that fit well for your scenario.
In general there are two options for authentication:
Browser-based
With browser-based authentication you rely solely on the browser and let it control the identity of each request. You have two options in the browser-based scenario:
- Windows Authentication – by configuring windows authentication for you application you let the browser ensures that every request is associated with a windows identity.
- Forms Authentication – you can use cookies and the provided ASP.NET Authentication Service to authenticate users.
Browser-based security is appropriate in no cross-domain access scenarios or limited to a few trusted domains. In other scenarios you should consider using a message-based security.
Message-based
With message-based security the identity is managed by Silverlight not by the browser. It relies on standard SOAP headers with passing username and password. Requires HTTPS connection because the username and password are sent as clear text.
To use a message-based security you need to configure the service binding to use authenticationMode = UserNameOverTransport – this is the only one mode supported by Silverlight currently.
To pass username and password to you service you need to use proxy.ClientCredentials.Username.Username and proxy.ClientCredentials.Username.Password.
Sample
Download Source Code
WOW, a Silverlight 2 app don in 2010?
You might want to update that dino…
Hi Fallon,
This tutorial was written a long time ago, but I never publish it on my blog. The demo might be a bit old, but still the content is valuable.
Emil
Yeah, I should have stated that the content is still valid and useful.
It’s a shame that they publish a link to your post like it’s new, so I thought the sample would be up to date.
Thanks for the info.
[...] http://emil.silverlightshow.net/2010/04/24/data-binding-in-silverlight/ [...]
[...] http://emil.silverlightshow.net/2010/04/24/data-binding-in-silverlight/ [...]