Data makes the world go round. I'm sure you have heard this phrase before, and Silverlight is no stranger to that saying. Silverlight offers a rich platform for working with data, and in this chapter, we are going to cover it thoroughly. We will discuss data binding, a major concept for working with data in Silverlight. We will then move on to discussing data formatting and value converters; and finish off with data validation, which is something that no data-driven application is complete without.
In this chapter, we will cover the following topics:
Data binding, at its core, provides a simple way for a Silverlight developer to work with data by separating the display module from the data management module. By establishing a connection between data objects from the backend and the UI layer, you get a mechanism that lets you update UI elements when data changes, or the other way around without the use of code.
The bound data can flow in three distinctive modes as follows:
OneTime:
The target of the bind operation will update with the source only when it's created. For example, if we have a TextBlock
element bound to a property, the TextBlock
element will only display the value the property had when TextBlock
was rendered. TextBlock
won't change its value even if the backend property changes later on.OneWay:
The target of the bind operation will update with the source every time the source changes. Using the same example as before, if the TextBlock
element is bound to a property using the one-way binding mode, it will update its value every time the source property updates as well. This is the default data binding mode in Silverlight.TwoWay:
The bind operation will update both the source and the target when either of them changes. For two-way binding, there is no logic in using a TextBlock
element, because this mode allows the UI element to update its source property, as well as the other way around. (TextBlock, as opposed to TextBox
only displays text and doesn't allow editing). This mode is used when we wish to get data from the user and a very common use for it is in forms.In order for the automatic update to occur, the source of the binding must implement an interface known as INotifyPropertyChanged
. This interface, in a nutshell, contains the PropertyChanged
event, which is in charge of telling the binding engine that the source has been changed, and it's time to update its bound UI element. We will discuss the INotifyPropertyChanged
interface in more details in just a bit. To have a better understanding of the whole binding process we have discussed so far, imagine that you need to create a form for adding a new user to your site. The form consists of several text areas the user needs to fill in, including a username field. You want the username field to light up in green if the chosen username is available and red if the username is already taken. By using two-way data binding, the form will update a backend property with the text the user has typed. The property in its turn performs some logic to check for the availability of the username, and then by having a property bound to the border property of the text box, it sets the new color.
At the heart of the binding engine is DataContext
, and before we go on and dig deeper into binding, let's discuss this important concept.
DataContext
, in a nutshell, allows us to define a common source for all the UI binding in XAML under a certain level. You can set the DataContext
property at either the page level or an element level depending on your needs. Consider the following code snippet:
<toolkit:DataForm HorizontalAlignment="Stretch" Margin="12" Name="dataForm1" VerticalAlignment="Stretch"> <toolkit:DataForm.NewItemTemplate> <DataTemplate> <StackPanel> <StackPanel Orientation="Horizontal"> <toolkit:DataField> <TextBox Text="{Binding FirstName, Mode=TwoWay}" MinWidth="200"/> </toolkit:DataField> <toolkit:DataField> <TextBox Text="{Binding LastName, Mode=TwoWay}" MinWidth="200"/> </toolkit:DataField> </StackPanel> <toolkit:DataField> <TextBox Text="{Binding EmailAddress, Mode=TwoWay}" /> </toolkit:DataField> </StackPanel> </DataTemplate> </toolkit:DataForm.NewItemTemplate> </toolkit:DataForm>
The preceding code snippet creates a DataForm
element with three fields—FirstName, LastName, and EmailAddress. These fields are bound to three different properties. How does the DataForm
control know to bind these properties? How does the DataForm
control know where the EmailAddress property is, and how does it update it when the TextBox
element's text changes? (Remember, two-way binding can update both the source property and the target UI element of the bind operation). The answer to these questions is the DataContext
object. Open the Chapter5-DataContext project in Visual Studio 2010. The project can be found in the downloadable content of the book and can be downloaded from http://www.packtpub.com.
If I run this code right now, I will get the result, as shown in the following screenshot:
Nothing gets rendered, but the nice default background of the DataForm
element. The reason for this is that DataForm
has no context. As stated previously, it has no idea where to take the bound properties from and, thus, doesn't render itself at all.
Let's fix this problem. First, open the MainPage.xaml.cs
file, and add the following code snippet in the constructor method, below the call to InitializeComponent:
ObservableCollection<FormEntity> entities = new ObservableCollection<FormEntity>(); dataForm1.DataContext = entities; dataForm1.AddNewItem();
The DataContext
property can also be set directly from XAML. This method requires a bit more work, as it needs a class that holds a collection of items and a resource that references to that class. Have a look at a post by Emil Stoychev at http://estoychev.wordpress.com/2010/04/24/data-binding-in-silverlight/ for more information on this topic.
The code we added creates a collection of the FormEntity
class named entities
. Once the collection is created, it is set as DataContext
for the DataForm
control we have in the UI (XAML) layer. Finally, we are telling the DataForm
control that we wish to add a new item to the collection by using the AddNewItem
method. If we were to run the application now, we would have got the same result as before—nothing but a nice background. The reason for that is pretty simple; we declared the source of the data that we wish to bind to DataForm
(If you take a look at the FormEntity
class in the project files, you will see that it contains the three properties we are binding to—FirstName, LastName, and EmailAddress), but we didn't tell the DataForm
control itself who is holding its items!
Switch back to the MainPage.xaml
file, and add the ItemsSource
property to the opening DataForm
element, so it will look as follows:
<toolkit:DataForm HorizontalAlignment="Stretch" Margin="12" Name="dataForm1" VerticalAlignment="Stretch" ItemsSource="{Binding}">
By setting the ItemsSource
property to self-binding (you will learn more on that in a moment), DataForm
now has a source for the binding (the entities
observable collection), and it knows who holds its items. Run the application now, and you should get the result, as shown in the following screenshot:
DataContext
can be bound to any FrameworkElement
control, and it's not uncommon to see several different data contexts assigned to several different controls within a single page or container control. Now that we understand what the DataContext
property is and how it works, it's time to move on to binding data sets to controls.
Sometimes, you need to bind a collection of items to your control. Think about a ListBox
control, or a ComboBox
control. Both display a collection of items to the user. For this exact reason, Silverlight binding engine supports the ItemsSource
property. Let's see an example of this property. For this example, we will continue from where we left off in the previous example. Open the MainPage.xaml
file, and add the following XAML code snippet below the last DataField
closing element:
<toolkit:DataField Label="Marital status"> <ComboBox ItemsSource="{Binding Statuses}" SelectedItem="{Binding SelectedStatus,Mode=TwoWay}"/> </toolkit:DataField>
Most of the code is pretty straightforward. We are adding a new DataField
element to the DataForm
control with a ComboBox
control as its input control. The less trivial part here is the binding. We are basically binding two properties—ItemsSource
and SelectedItem
.
The ItemsSource
property is what the ComboBox
control is going to show to the user. This is the data set of items that the ComboBox
control uses as its source. In the following code snippet, Statuses
is the name of the property that holds the collection of items. Add Statuses
to the FormEntity
class in your project as follows:
private List<string> statuses = new List<string>() { "Single", "Married", "Divorced" }; public List<string> Statuses { get { return statuses; } set { statuses = value; } }
We first create a hardcoded list of strings called statuses
. Then, we create the Statuses
property, which will set and return the statuses
variable.
The SelectedItem
property is the value of the item the user chose from ComboBox
. As we are binding a list of string
items as the ComboBox
control's items source, the selected item will also be a string
. In addition, as SelectedStatus
is a property that the user sets from the UI layer, we set its binding mode to TwoWay. Let's add the SelectedStatus
property to the FormEntity
class now. Add the following code snippet to the Statuses
property:
private string selectedStatus; public string SelectedStatus { get { return selectedStatus; } set { selectedStatus = value; } }
That's it! Build and run your application, and you should get the result, as shown in the following screenshot:
While building line of business (LOB) applications in Silverlight, there may come a time when you wish to bind a property of a control to another control. Think about a page with a slider control and a rectangle. The value of the slider control determines the size of the rectangle. The more to the right the slider is, the bigger the rectangle is. While this can be done using code behind, with events, it is much easier to do so in the XAML layer. Let's create this example now to have a better understanding of elements to elements binding.
Create a new Silverlight project in Visual Studio 2010 and name it Chapter5-ElementBinding. Add the following code snippet to your MainPage.xaml
file under the LayoutRoot Grid
control:
<Rectangle HorizontalAlignment="Center" Margin="0,15,0,0" Name="rectangle1" Stroke="#FFFF9800" StrokeThickness="1" VerticalAlignment="Top" Width="{Binding ElementName=slider1,Path=Value}" Height="{Binding ElementName=slider1,Path=Value}" Fill="#FFFFD200" /> <Slider Height="23" HorizontalAlignment="Center" Name="slider1" VerticalAlignment="Center" Width="100" Minimum="100" Maximum="150" Value="100" />
Can you spot the bindings in the preceding code example?
Elements binding have a constant syntax; we first declare the Binding
keyword, then we use the ElementName
property to set the source element for the binding, and finally we set the Path
property, which is the value of the source element we wish to bind. In our example, we are binding the Value
property of the slider1
control as both the width and height of our rectangle. Build and run your application, and you should get the result, as shown in the following screenshot:
Move the slider to the right, and you should see that the rectangle grows. Move the slider to the left and the rectangle shrinks:
We've already mentioned the INotifyPropertyChanged
interface earlier when we talked about binding modes. When we are binding a UI element to a source property using either a OneWay
or TwoWay
binding mode, the binding engine needs to know that the source property has changed so it can in turn update the UI element the source is bound to.
The only way the engine can know that it is time to perform the update is if the source of the binding implements the INotifyPropertyChanged
interface. When implementing the interface, a new event will get added to the class named PropertyChanged
. This is the event the binding engine listens to in order to know when to update. When the event is raised, Silverlight will update the bound UI element with the new value of the source.
Let's implement the INotifyPropertyChanged
interface in our project. Just like before, we are going to use the Chapter5-DataContext project and add the interface to its properties class. Open the FormEntity.cs
file, and change the class declaration as follows:
public class FormEntity:INotifyPropertyChanged
To implement the interface, mark INotifyPropertyChanged, then click on Implement interface 'INotifyPropertyChanged', as shown in the following screenshot:
The interface adds a single event to the class called PropertyChanged:
public event PropertyChangedEventHandler PropertyChanged;
To actually raise the event, we add the OnPropertyChanged
method to the class. Add the following code snippet below the PropertyChanged
event declaration:
public void OnPropertyChanged(string propertyName) { var handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } }
Now, whenever a property wishes to raise the PropertyChanged
event to let the binding engine know it's time to update its bounded UI element, all it has to do is call the OnPropertyChanged
event and pass its name as an argument to it. Change the SelectedStatus
property so that it uses the OnPropertyChanged
method as follows:
public string SelectedStatus { get { return selectedStatus; } set { selectedStatus = value; OnPropertyChanged("SelectedStatus"); } }
To actually see it in action, add the following DataField
control to your DataForm
control on MainPage.xaml:
<toolkit:DataField Label="Selected status:"> <TextBlock Text="{Binding SelectedStatus}"/> </toolkit:DataField>
Build and run your application, and you should get the result, as shown in the following screenshot:
Change the Marital status combobox, and you should notice that whenever the value in the combobox changes, the newly added Selected status field is updated with the new value, as shown in the following screenshot:
Try to remove the call to the OnPropertyChanged
method from the FormEntity.cs
class, and you'll see that the Selected status field no longer gets updated.
If you are binding to a collection of items, and you want the UI to stay in sync with the source, you should use the ObservableCollection
collection type. This collection already implements the INotifyPropertyChanged
interface and, thus, whenever an item in ObservableCollection
updates, the binding engine updates the UI as well.
So far, we've dealt with binding and showing the result of that bind operation to the user. What happens if your binding fails? In such a case, you would probably want to present some kind of message to the user. This message is exactly what the fallback value is all about. This is the value that gets displayed to the user when a binding fails or has no value. Setting the fallback value is done using the TargetNullValue
property. In the last example we created, you might have noticed that when the application starts for the first time, nothing is shown for the Selected status field. This is completely logical as we haven't selected any marital status yet. If we change the binding of the field and add the TargetNullValue
property to it, we can show a nice friendly message to the user specifying that he/she hasn't selected a Marital status yet. Change the TextBlock
for the Selected Status field as follows:
<TextBlock Text="{Binding SelectedStatus, TargetNullValue='No status selected!'}"/>
The TargetNullValue
property is set to a string, which acts as the fallback value for this binding. Run the application now and you should get the result, as shown in the following screenshot:
Another property that we have for fallback values is the FallBackValue
property. Not surprisingly, this property can also be used to set a default fallback value for our binding.
3.143.247.125