Chapter 5. Working with Data

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:

  • Implementing data binding
  • Formatting data
  • Creating and consuming value converters
  • Implementing data validation

Implementing data binding

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.

Setting DataContext

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:

Setting DataContext

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();

Note

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:

Setting DataContext

Note

Self-binding refers to a state when an element binds to the DataContext property of the element above it in the hierarchy. In our preceding example, the ItemsSource property is bound to the DataContext object of the element above it in the hierarchy, that is, the page itself.

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.

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:

Binding data sets to controls

Tip

Setting ItemsSource in code behind

Setting the ItemsSource property from code behind is as simple as it gets. If we were to set ItemsSource to dataForm1 in the code behind, all we would have done is use the ItemsSource property as follows:

dataForm1.ItemsSource = entities

Binding elements to other elements

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:

Binding elements to other elements

Move the slider to the right, and you should see that the rectangle grows. Move the slider to the left and the rectangle shrinks:

Binding elements to other elements

The INotifyPropertyChanged interface

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 INotifyPropertyChanged interface

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:

The INotifyPropertyChanged interface

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:

The INotifyPropertyChanged interface

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.

Note

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.

Setting a fallback value

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:

Setting a fallback value

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.247.125