Estimated Time: 60 minutes
Data binding is a key technology in Silverlight that allows data to be presented to end users and then processed. In this lab exercise you'll learn different data binding techniques that can be used to perform one way and two way bindings and see how data can be accessed directly without having to go through controls in the user interface.You'll also work with new binding properties available in Silverlight 4 and learn how to write a custom value converter. The application that you'll build in the lab exercises is shown next:
| Class Name |
|---|
| Customer |
| State |
| CustomerContainer |
| Property | Type |
|---|---|
| Name | String |
| Abbreviation | String |
| Language | Code |
|---|---|
| C# | After adding the interface to the Customer class right-click it and select Implement Interface → Implement Interface from the menu. |
| Visual Basic | After adding the interface to the Customer class hit [Enter] to implement the interface. |
The INotifyPropertyChanged interface is a key part of the data binding infrastructure available in Silverlight. It contains a single event named PropertyChanged that is used to notify objects when a particular property value changes.
protected void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
Protected Overridable Sub OnPropertyChanged(ByVal propName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))
End Sub
string _Name;
public string Name
{
get { return _Name; }
set
{
if (_Name != value)
{
_Name = value;
OnPropertyChanged("Name");
}
}
}
Private _Name As String
Public Property Name() As String
Get
Return _Name
End Get
Set(ByVal value As String)
If _Name IsNot value Then
_Name = value
OnPropertyChanged("Name")
End If
End Set
End Property
If you're using Visual Basic ensure that IsNot is used for comparing reference types and <> is used for comparing value types when creating the properties that follow.
A code snippet file is available in the lab's Starting Point folder that can be used to simplify the process of creating properties that call OnPropertyChanged. Use the Code Snippet Manager to import the appropriate snippet file (C# or VB) if you'd like to use the snippet. Once imported, the shortcut for the snippet is mvvmInpc.
| Property | Type |
|---|---|
| City | String |
| State | String |
| ImageUrl | String |
| Birthday | DateTime |
| IsGold | Boolean |
Anytime INotifyPropertyChanged must be implemented on multiple classes it's often more efficient to create a base class that implements the interface and provides the OnPropertyChanged method. Classes needing to implement the interface can then derive from the base class which provides better code-use and simplified maintenance. If time permits, create a base class that implements INotifyPropertyChanged and contains the OnPropertyChanged method and then derive Customer and CustomerContainer from it.
A code snippet file is available in the Starting Point folder that can be used to simplify the process of creating properties that call OnPropertyChanged. Use the Code Snippet Manager to import the appropriate snippet file (C# or VB) if you'd like to use the snippet. Once imported, the shortcut for the snippet is mvvmInpc.
| Property | Type |
|---|---|
| States | ObservableCollection of State |
| Customers | ObservableCollection of Customer |
| FilteredCustomers | ObservableCollection of Customer |
| CurrentCustomer | Customer |
| CurrentState | State |
private void FilterCustomersByState()
{
if (CurrentState != null)
{
if (CurrentState.Name != "View All")
{
var customers = Customers.Where(c => c.State== CurrentState.Name);
FilteredCustomers = new ObservableCollection<Customer>(customers);
}
else
{
FilteredCustomers = Customers;
}
}
}
Private Sub FilterCustomersByState()
If CurrentState IsNot Nothing Then
If CurrentState.Name <> "View All" Then
Dim customers=Me.Customers.Where(Function(c) c.State=CurrentState.Name)
FilteredCustomers = New ObservableCollection(Of Customer)(customers)
Else
FilteredCustomers = Customers
End If
End If
End Sub
set
{
if (_CurrentState != value)
{
_CurrentState = value;
OnPropertyChanged("CurrentState");
FilterCustomersByState();
}
}
Set(ByVal value As State)
If _CurrentState IsNot value Then
_CurrentState = value
OnPropertyChanged("CurrentState")
FilterCustomersByState()
End If
End Set
const string IMAGE = "Images/blue.png";
Const IMAGE As String = "Images/blue.png"
States = new ObservableCollection<State>
{
new State{Name="Arizona",Abbreviation="AZ"},
new State{Name="California",Abbreviation="CA"},
new State{Name="Nevada",Abbreviation="NV"},
new State{Name="View All"}
};
States = New ObservableCollection (Of State)() From {
New State With {.Name="Arizona", .Abbreviation="AZ"},
New State With {.Name="California", .Abbreviation="CA"},
New State With {.Name="Nevada", .Abbreviation="NV"},
New State With {.Name="View All"}}
This code can be cut-and-paste from the CustomerContainer class in the lab solution available in the Completed folder.
Customers = new ObservableCollection<Customer>
{
new Customer{Name="John Doe",City="Phoenix", State="Arizona",IsGold=true,
Birthday=new DateTime(1950,5,10),ImageUrl=IMAGE},
new Customer{Name="Jane Doe",City="Tempe", State="Arizona",
Birthday=new DateTime(1970,4,13),ImageUrl=IMAGE},
new Customer{Name="Johnny Doe",City="San Diego",State="California",
Birthday=new DateTime(1980,8,26),ImageUrl=IMAGE},
new Customer{Name="James Doe",City="Las Vegas",State="Nevada",IsGold=true,
Birthday=new DateTime(1956,8,30),ImageUrl=IMAGE},
new Customer{Name="Gina Doe",City="Anaheim",State="California",
Birthday=new DateTime(1984,2,28),ImageUrl=IMAGE}
};
FilteredCustomers = Customers;
Customers = New ObservableCollection(Of Customer)
From {
New Customer With {.Name = "John Doe", .City = "Phoenix", _
.State = "Arizona", .IsGold = True, _
.Birthday = New Date(1950, 5, 10), .ImageUrl = IMAGE},
New Customer With {.Name = "Jane Doe", .City = "Tempe", _
.State = "Arizona", .Birthday = New Date(1970, 4, 13), _
.ImageUrl = IMAGE},
New Customer With {.Name = "Johnny Doe", .City = "San Diego", _
.State = "California", .Birthday = New Date(1980, 8, 26), _
.ImageUrl = IMAGE},
New Customer With {.Name = "James Doe", .City = "Las Vegas", _
.State = "Nevada", .IsGold = True, _
.Birthday = New Date(1956, 8, 30), .ImageUrl = IMAGE},
New Customer With {.Name = "Gina Doe", .City = "Anaheim", _
.State = "California", .Birthday = New Date(1984, 2, 28), _
.ImageUrl = IMAGE}}
FilteredCustomers = Customers
Data is being added directly into the CustomerContainer class since the focus of this lab is on data binding. In a real-world application data would be retrieved from a Web Service or RESTful service. Additional labs in this series are available that cover retrieving data from distributed sources using WCF and WCF RIA Services.
Click on the Grid control in the Visual Studio designer and then add rows and columns by clicking within the blue regions to the left and top of the interface.
You'll need 6 TextBlock controls, a ComboBox control, a ListBox control, 3 TextBox controls and a Button. Place the controls in the appropriate rows and columns of the Grid.
LayoutRoot.DataContext = new CustomerContainer();
LayoutRoot.DataContext = New CustomerContainer()
xmlns:data="clr-namespace:DataBinding"
<UserControl.Resources>
<data:CustomerContainer x:Key="CustomerContainerObject" />
</UserControl.Resources>
This will create a new instance of the CustomerContainer object at runtime and assign it to the CustomerContainerObject key.
DataContext="{Binding Source={StaticResource CustomerContainerObject}}"
This code binds the CustomerContainerObject key (which represents an instance of the CustomerContainer object) to the DataContext declaratively. This type of binding will execute in design-mode and at runtime.
The CustomerContainer object assigned to the DataContext is automatically detected as the Source.
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Width="350">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".20*" />
<ColumnDefinition Width=".30*" />
<ColumnDefinition Width=".20*" />
<ColumnDefinition Width=".30*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<Image Grid.Column="0" Source="{Binding Path=ImageUrl}" />
<TextBlock Grid.Column="1" Text="{Binding Path=Name}"
FontSize="12" Foreground="Navy" />
<TextBlock Grid.Column="2" Text="{Binding Path=City}"
FontSize="12" Foreground="Red" />
<TextBlock Grid.Column="3" Text="{Binding Path=State}"
FontSize="12" Foreground="Green" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
var customers = LayoutRoot.DataContext as CustomerContainer; var name = customers.CurrentCustomer.Name; OutputTextBlock.Text = name + " updated!";
Dim customers = CType(LayoutRoot.DataContext, CustomerContainer) Dim name = customers.CurrentCustomer.Name OutputTextBlock.Text = name + " updated!"
This code accesses the LayoutRoot object's DataContext and casts it to a CustomerConatiner type so that you can access the CurrentCustomer object's Name property value.
The StringFormat property provides a way to apply a format code to data as it is bound to a control. Standard .NET format codes used to format dates, times, decimals, currencies, and more can be used.
Text="{Binding Path=CurrentCustomer.Birthday, Mode=TwoWay,
StringFormat=\{0:d\}}"
return ((bool)value == true) → Visibility.Visible : Visibility.Collapsed;
Return If(CBool(value) = True, Visibility.Visible, Visibility.Collapsed)
xmlns:converters="clr-namespace:DataBinding.Converters"
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
Visibility="{Binding CurrentCustomer.IsGold,Converter={StaticResource
BoolToVisibilityConverter},FallbackValue=Collapsed}"
When the screen first loads CurrentCustomer will be null so the converter will never be called. To account for this the FallbackValue property is used to define that the default Visibility is Collapsed which will hide the image. FallbackValue is used whenever a binding can't be resolved.
In this exercise you created data entity objects that implemented INotifyPropertyChanged and used Silverlight data binding features to bind object properties to controls. You also used the StringFormat property to format data and a value converter to hide a control based upon a property value. Specific requirements satisfied in this lab include:
Comments (0)