Data binding provides a simple way for Silverlight-based applications to display and interact with data. The way data is displayed is separated from the management of the data. A connection, or binding, between the UI and a data object allows data to flow between the two. When a binding is established and the data changes, the UI elements that are bound to the data can reflect changes automatically. Similarly, changes made by the user in a UI element can be reflected in the data object. For example, if the user edits the value in a TextBox, the underlying data value is automatically updated to reflect that change.

Some common binding scenarios include binding a ListBox to a list of headlines, an input form's TextBox to a customer data object, or an Image to the current user's photo.

Connecting User Interface Elements with Data

Every binding must specify a source and a target. The following illustration shows the basic concepts of a binding.

DtBigSil1.gif

Figure 1

The binding engine gets information from the Binding object about the following:

<ComboBox x:Name="cmbEmployeeName"
VerticalAlignment="Top"
Margin="261,72,279,0"
Height="24" Width="200">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding EmployeeName}"

Foreground="{Binding EmployeeStatus}"/>

</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

Listing

DtBigSil2.gif

Figure 2

As you see in the above listing and figure, the Foreground property of a TextBlock can be bound to a SolidColorBrush so that the color of the text can change based on the data. In this scenario, the Foreground property is the target, and the SolidColorBrush object is the source for the binding.
The binding is created in XAML using the {Binding ...} syntax. The source is set in code by setting the DataContext property for the TextBlock.
Data context is inherited. If you set the data context on a parent element, all it's children will use the same data context. A child element can override this behavior by setting the Source property on it's binding object or by setting it's DataContext, which will then apply to all it's children.

Setting the data context is useful when you want to have multiple bindings that all use the same source. To set the source for a single binding, set the Source property on the Binding object.

You can also use the ElementName property or the RelativeSource property to specify the binding source. The ElementName property is useful when you are binding to other elements in your application, such as when you are using a slider to adjust the width of a button. The RelativeSource property is useful when the binding is specified in a ControlTemplate or a Style.

Altogether, the preceding example causes the binding engine to create a binding, which is OneWay by default, connecting the Foreground property of the TextBlock to the EmployeeStatus property of the Employee object.

Direction of the Data Flow

Each binding has a Mode property, which determines how and when the data flows. Silverlight enables three types of bindings:

In order for automatic target updates to occur, the source object must implement the INotifyPropertyChanged interface.

public class Employee: INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion

public int EmployeeID { get; set; }

private string _EmployeeName;
public string EmployeeName
{
get
{
return _EmployeeName;
}
set
{
_EmployeeName = value;
NotifyPropertyChanged("EmployeeName");
}
}
public SolidColorBrush EmployeeStatus { get; set; }

public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
}

Listing

To get change notification for collections bound to an ItemsControl, implement INotifyCollectionChanged in addition to INotifyPropertyChanged. If you implement INotifyCollectionChanged, changes to the collection, such as adding or removing an object, will propagate to the target. To get property change notification for objects in the collection, the objects must implement INotifyPropertyChanged.

Before implementing your own collection, consider using the ObservableCollection<(Of <(<'T>)>)> class, which has a built-in implementation of INotifyCollectionChanged and INotifyPropertyChanged.

Updating the Data Source

In TwoWay bindings, changes to the target automatically update the source, except when binding to the Text property of a TextBox. In this case, the update occurs when the TextBox loses focus.

You can disable automatic source updates and update the source at times of your choosing. For example, you can do this to validate user input from multiple controls before you update the bound data sources.

To disable automatic source updates, set the UpdateSourceTrigger property to Explicit. This setting affects all bindings that use the same Binding object (for example, when using an inherited data context). You must update the source for each binding individually, however. To update a binding, first call the FrameworkElement.GetBindingExpression method of a target element, passing in the target DependencyProperty. You can then use the return value to call the BindingExpression.UpdateSource method.

<Button x:Name="btnSubmit"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="100" Content="Submit"
Click="btnSubmit_Click"
Margin="82,191,0,0" Height="24"/>
<TextBox x:Name="txtEmployeeName"
HorizontalAlignment="Left"
Text="{Binding EmployeeName, Mode=TwoWay, UpdateSourceTrigger=Explicit}"
VerticalAlignment="Top"
TextWrapping="Wrap"
Margin="82,100,0,0" Width="207"/>
<TextBlock x:Name="txtStatus1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
TextWrapping="Wrap" Margin="82,134,0,0"/>
<TextBlock x:Name="txtStatus2"
HorizontalAlignment="Left"
Margin="82,157,0,0"
VerticalAlignment="Top"
TextWrapping="Wrap"/>

Listing

public MainPage()
{
InitializeComponent();

emp = new Employee
{
EmployeeID = 1,
EmployeeName = "Mahesh",
EmployeeStatus = new SolidColorBrush(Colors.Green)
};

txtEmployeeName.DataContext = emp;
}

private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
BindingExpression expression =

txtEmployeeName.GetBindingExpression(TextBox.TextProperty);
txtStatus1.Text = "Before UpdateSource, Employee Name = " +

emp.EmployeeName;
expression.UpdateSource();

txtStatus2.Text = "After UpdateSource, Employee Name = " +

emp.EmployeeName;

}

Listing

DtBigSil3.gif

Figure 3

DtBigSil4.gif

Figure 4

Binding to Collections

A binding source object can be treated either as a single object whose properties contain data or as a collection of objects. For example, you might want to display a list of items, such as monthly credit card bills. To do this, use an ItemsControl and repeat over a DataTemplate to display each item in a collection.

You can enumerate over any collection that implements IEnumerable. If you want the target to update the ItemsSource when the collection changes, implement INotifyCollectionChanged. For more information on change notification, see the Change Notification section earlier in this topic.
We have already seen binding a collection of data to a ComboBox, and it would display a specific property out of the particular entity.

Data Validation

Silverlight supports simple data validation in TwoWay bindings for target-to-source updates. Additionally, Silverlight supports validation with IDataErrorInfo and INotifyDataErrorInfo regardless of the binding mode.

Silverlight reports a validation error whenever the Validation.Errors attached property of a binding contains errors. Errors are added to this collection in the following cases:

Silverlight provides visual feedback for validation errors in the following cases:


<TextBox x:Name="txtEmployeeName"
HorizontalAlignment="Left"
Text="{Binding EmployeeName, Mode=TwoWay,

UpdateSourceTrigger=Explicit,

NotifyOnValidationError=True,

ValidatesOnExceptions=True}"

VerticalAlignment="Top"
TextWrapping="Wrap"
Margin="82,100,0,0" Width="207"/>

Listing

private string _EmployeeName;
public string EmployeeName
{
get
{
return _EmployeeName;
}
set
{
if (string.IsNullOrEmpty(value))
{
throw new Exception("Employee Name is Required");
}
_EmployeeName = value;
NotifyPropertyChanged("EmployeeName");
}
}

Listing

DtBigSil5.gif

Figure 5

As you see in above figure the Validation error is thrown. The visual feedback indicates the control that contains the error, and displays the error message nearby, as shown in the above illustration.

In order to receive notification that a validation error has occurred, you must also set the NotifyOnValidationError property to true on the binding object. Setting ValidatesOnExceptions to true tells the binding engine to create a validation error when an exception occurs. Setting NotifyOnValidationError to true tells the binding engine to raise the BindingValidationError event when a validation error occurs and resolves. For example, you can handle the error event to log the error or provide additional visual feedback.

To handle the BindingValidationError event, create an event handler on the target object or any of it's parents. The BindingValidationError event is a routed event, so if you do not handle it on the element that raised the event, it will continue to bubble up until it is handled.

public MainPage()
{
InitializeComponent();
LayoutRoot.BindingValidationError += new EventHandler<ValidationErrorEventArgs>
(LayoutRoot_BindingValidationError);
emp = new Employee
{
EmployeeID = 1,
EmployeeName = "Mahesh",
EmployeeStatus = new SolidColorBrush(Colors.Green)
};

txtEmployeeName.DataContext = emp;
}
void LayoutRoot_BindingValidationError(object sender, ValidationErrorEventArgs e)
{
if (e.Action == ValidationErrorEventAction.Added)
{
txtEmployeeName.Background = new SolidColorBrush(Color.FromArgb(23,242,67,67));

}
else if (e.Action == ValidationErrorEventAction.Removed)
{
txtEmployeeName.Background = new SolidColorBrush(Colors.White);
}
}

Listing

DtBigSil6.gif

Figure 6

As you see in above figure the error TextBox has a background associated with it when it has the error.

Data Conversions

You may need to display data in a format that differs from how it is stored. Some examples are the following:

You can format any String value for display by setting the StringFormat property. For information on formatting codes, see Formatting Types.
You can display a friendly default value for null backing values by setting the TargetNullValue property.

You can also set a converter on any binding. The converter is customized for each scenario by creating a class and implementing the IValueConverter interface. The following example shows how to implement IValueConverter.

The binding engine calls the Convert and ConvertBack methods if the Converter parameter is defined for the binding. When data is passed from the source, the binding engine calls Convert and passes the returned data to the target.

When data is passed from the target, the binding engine calls ConvertBack and passes the returned data to the source.

The converter also has optional parameters: ConverterCulture, which allows specifying the culture to be used in the conversion, and ConverterParameter, which allows passing a parameter for the conversion logic. For an example using these parameters, see IValueConverter.

If there is an error in the conversion, do not throw an exception. Instead, return DependencyProperty.UnsetValue, which will stop the data transfer.

To display a default value that appears whenever the binding source cannot be resolved, set the FallbackValue property. This is useful to handle conversion and formatting errors. It is also useful to bind to source properties that might not exist on all objects in a bound collection.