Exchanging Data between Silverlight 3.0 User Controls using Dependency Properties and Events

Posted by: Mahesh Sabnis , on 12/12/2009, in Category Silverlight 2, 3, 4 and 5
Views: 12914
Abstract: A week ago, I was discussing with one of my clients about developing Silverlight user controls and exchange data between them. There were a few interesting points that came up during the discussion and I thought this would make a good article. In this article, we will see how to exchange data between Silverlight 3.0 user controls.
Exchanging Data between Silverlight 3.0 User Controls using Dependency Properties and Events
 
A week ago, I was discussing with one of my clients about developing Silverlight user controls and exchange data between them. There were a few interesting points that came up during the discussion and I thought this would make a good article. In this article, we will see how to exchange data between Silverlight 3.0 user controls.
In my previous article, we discussed creating Silverlight 3.0 user controls and developing data driven applications. In this article, we will create two Silverlight 3.0 controls, the details of which are mentioned below:
-      SearchUserControl: This control will be used to search and filter data from collection.
-      DetailedControl: This control will be used to display data filtered by the ‘SearchUserControl’.
In this article, I have created a WCF service, which will be used to fetch data from database.
Step1: Open VS2008 and create a blank solution, name this as ‘SILV3_UserControlSharedData’.
Step 2: To this solution, add a new Silverlight Class Library Project, name it as ‘SILV_SearchUserControl’. You will get ‘Class1.cs’ > delete it and add a new Silverlight User Control in this project and name it as ‘SearchValue.xaml’  as shown below:
AddNewItem
Step 3: In ‘SearchValue.xam’ write the following xaml code:
<UserControl x:Class="SILV_SearchUserControl.SearchValue"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="400" Height="50">
    <Grid x:Name="LayoutRoot" Background="White" Width="400" Height="50">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150"></ColumnDefinition>
            <ColumnDefinition Width="150"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <TextBlock
            Text="Search Data:" Grid.Column="0" Margin="1,1,1,1"
             FontFamily="Times New Roman" FontSize="27"
             Foreground="Red" FontWeight="Bold"></TextBlock>
        <TextBox x:Name="txtSearch" Grid.Column="1" Margin="1,1,1,1"
                 TextChanged="txtSearch_TextChanged" Height="40" Width="100"></TextBox>
    </Grid>
</UserControl>
 
UI of the above xaml will be as below:
SearchData
Step 4: Open ‘SearchValue.xaml.cs’ and write the following code:
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
 
namespace SILV_SearchUserControl
{
    public partial class SearchValue : UserControl
    {
        public SearchValue()
        {
            InitializeComponent();
        }
 
 
        public string strData
        {
            get { return (string)GetValue(strDataProperty); }
            set { SetValue(strDataProperty, value); }
        }
 
        // Using a DependencyProperty as the backing store for strData. This enables animation, styling, binding, etc...
        public static readonly DependencyProperty strDataProperty =
            DependencyProperty.Register("strData", typeof(string), typeof(SearchValue), new PropertyMetadata(""));
 
        //Define Delegate and Events
 
        public delegate void TextValueChangedEventHandler(object s, EventArgs e);
 
        public event TextChangedEventHandler TextBoxChanged;
 
        private void txtSearch_TextChanged(object sender, TextChangedEventArgs e)
        {
            strData = txtSearch.Text;
 
            if (TextBoxChanged != null)
            {
                TextBoxChanged(this, e);
            }
        }
    }
}
 
VB.NET
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Net
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Documents
Imports System.Windows.Input
Imports System.Windows.Media
Imports System.Windows.Media.Animation
Imports System.Windows.Shapes
 
Namespace SILV_SearchUserControl
      Partial Public Class SearchValue
            Inherits UserControl
            Public Sub New()
                  InitializeComponent()
            End Sub
 
 
            Public Property strData() As String
                  Get
                        Return CStr(GetValue(strDataProperty))
                  End Get
                  Set(ByVal value As String)
                        SetValue(strDataProperty, value)
                  End Set
            End Property
 
            ' Using a DependencyProperty as the backing store for strData. This enables animation, styling, binding, etc...
            Public Shared ReadOnly strDataProperty As DependencyProperty = DependencyProperty.Register("strData", GetType(String), GetType(SearchValue), New PropertyMetadata(""))
 
            'Define Delegate and Events
 
            Public Delegate Sub TextValueChangedEventHandler(ByVal s As Object, ByVal e As EventArgs)
 
            Public Event TextBoxChanged As TextChangedEventHandler
 
            Private Sub txtSearch_TextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
                  strData = txtSearch.Text
 
                  RaiseEvent TextBoxChanged(Me, e)
            End Sub
      End Class
End Namespace
The above code defines dependency property for exchanging data between user control and the consumer. The important part here is that since Silverlight 3.0 does not support creating custom routed events, a standard mechanism (Delegate and event) is used for event creation.
Step 5: Build the project, make sure that it is error free.
Step 6: To the solution, add a new Silverlight class library project, name it as ‘SILV_DetailedControl’. Remove the existing ‘Class1.cs’ and add a new Silverlight user control and name it as ‘DetailedControl.xaml’. Write the following xaml in it:
<UserControl xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"  x:Class="SILV_DetailedControl.DetailedControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="500" Height="400">
    <Grid x:Name="LayoutRoot" Background="White" Width="500" Height="400">
        <Grid.RowDefinitions>
            <RowDefinition Height="100"></RowDefinition>
            <RowDefinition Height="300"></RowDefinition>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0"
                   Text="Information Details"
                   FontFamily="Times New Roman"
                    TextAlignment="Center"
                    FontWeight="Bold"
                    Foreground="Red"
                    FontSize="56"></TextBlock>
        <data:DataGrid Grid.Row="1"
                 AlternatingRowBackground="Azure"
                 AutoGenerateColumns="True"
                 Background="Coral"
                 x:Name="dgDetails">
        </data:DataGrid>
    </Grid>
</UserControl>
 
The above xaml shows the following design:
InformationDetails
The above grid will display data which will be filtered using the first user control.
Step 7: Open ‘DetailedControl.xaml.cs’ and write the following code:
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
 
namespace SILV_DetailedControl
{
    public partial class DetailedControl : UserControl
    {
 
        public DetailedControl()
        {
            InitializeComponent();
        }
 
        public void BindDataGrid(object[] Source)
        {
            dgDetails.ItemsSource = Source;
        }
    }
}
 
VB.NET
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Net
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Documents
Imports System.Windows.Input
Imports System.Windows.Media
Imports System.Windows.Media.Animation
Imports System.Windows.Shapes
Imports System.Collections.ObjectModel
 
Namespace SILV_DetailedControl
      Partial Public Class DetailedControl
            Inherits UserControl
 
            Public Sub New()
                  InitializeComponent()
            End Sub
 
            Public Sub BindDataGrid(ByVal Source() As Object)
                  dgDetails.ItemsSource = Source
            End Sub
      End Class
End Namespace
 
The above code defines ‘BindDataGrid()’ method which will be called by the consumer to assign data to the DataGrid.
Step 8: To the Solution, add a new WCF service and name it as ‘WCF_Service’. Rename ‘IService1.cs’ to ‘IService.cs’ and write the following code by removing the code which is by default available to you:
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
 
namespace WCF_Service
{
    [ServiceContract]
    public interface IService
    {
        [OperationContract]
        Customer[] GetAllCustomers();
    }
 
    [DataContract]
    public class Customer
    {
        [DataMember]
        public int CustomerID { get; set; }
        [DataMember]
        public string CustomerName { get; set; }
        [DataMember]
        public string Address { get; set; }
        [DataMember]
        public string City { get; set; }
        [DataMember]
        public string State { get; set; }
        [DataMember]
        public int Age { get; set; }
    }
}
 
VB.NET
 
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Runtime.Serialization
Imports System.ServiceModel
Imports System.Text
 
Namespace WCF_Service
      <ServiceContract> _
      Public Interface IService
            <OperationContract> _
            Function GetAllCustomers() As Customer()
      End Interface
 
      <DataContract> _
      Public Class Customer
            Private privateCustomerID As Integer
            <DataMember> _
            Public Property CustomerID() As Integer
                  Get
                        Return privateCustomerID
                  End Get
                  Set(ByVal value As Integer)
                        privateCustomerID = value
                  End Set
            End Property
            Private privateCustomerName As String
            <DataMember> _
            Public Property CustomerName() As String
                  Get
                        Return privateCustomerName
                  End Get
                  Set(ByVal value As String)
                        privateCustomerName = value
                  End Set
            End Property
            Private privateAddress As String
            <DataMember> _
            Public Property Address() As String
                  Get
                        Return privateAddress
                  End Get
                  Set(ByVal value As String)
                        privateAddress = value
                  End Set
            End Property
            Private privateCity As String
            <DataMember> _
            Public Property City() As String
                  Get
                        Return privateCity
                  End Get
                  Set(ByVal value As String)
                        privateCity = value
                  End Set
            End Property
            Private privateState As String
            <DataMember> _
            Public Property State() As String
                  Get
                        Return privateState
                  End Get
                  Set(ByVal value As String)
                        privateState = value
                  End Set
            End Property
            Private privateAge As Integer
            <DataMember> _
            Public Property Age() As Integer
                  Get
                        Return privateAge
                  End Get
                  Set(ByVal value As Integer)
                        privateAge = value
                  End Set
            End Property
      End Class
End Namespace
 
Step 9: Change ‘Service1.svc’ to ‘Service.svc’ and write the following code in ‘Service.svc.cs’:
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
 
using System.Data;
using System.Data.SqlClient;
 
namespace WCF_Service
{
    public class Service : IService
    {
        SqlConnection Conn;
        SqlCommand Cmd;
        SqlDataReader Reader;
 
        #region IService Members
 
        public Customer[] GetAllCustomers()
        {
            Conn = new SqlConnection("Data Source=.;Initial Catalog=Company;Integrated Security=SSPI");
            Conn.Open();
            Cmd = new SqlCommand();
            Cmd.Connection = Conn;
            Cmd.CommandText = "Select * from Customers";
            Reader = Cmd.ExecuteReader();
            DataTable dtCustomers = new DataTable();
            dtCustomers.Load(Reader);
            Customer[] arrCustomers = new Customer[dtCustomers.Rows.Count];
            int rowCount = 0;
            foreach (DataRow Dr in dtCustomers.Rows)
            {
                arrCustomers[rowCount] = new Customer();
                arrCustomers[rowCount].CustomerID = Convert.ToInt32(Dr["CustomerID"]);
                arrCustomers[rowCount].CustomerName = Dr["CustomerName"].ToString();
                arrCustomers[rowCount].Address = Dr["Address"].ToString();
                arrCustomers[rowCount].City = Dr["City"].ToString();
                arrCustomers[rowCount].State = Dr["State"].ToString();
                arrCustomers[rowCount].Age = Convert.ToInt32(Dr["Age"]);
                rowCount++;
            }
            Conn.Close();
            Cmd.Dispose();
            Conn.Dispose();
            return arrCustomers;
        }
 
        #endregion
    }
}
 
VB.NET
 
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Runtime.Serialization
Imports System.ServiceModel
Imports System.Text
 
Imports System.Data
Imports System.Data.SqlClient
 
Namespace WCF_Service
      Public Class Service
            Implements IService
            Private Conn As SqlConnection
            Private Cmd As SqlCommand
            Private Reader As SqlDataReader
 
            #Region "IService Members"
 
            Public Function GetAllCustomers() As Customer()
                  Conn = New SqlConnection("Data Source=.;Initial Catalog=Company;Integrated Security=SSPI")
                  Conn.Open()
                  Cmd = New SqlCommand()
                  Cmd.Connection = Conn
                  Cmd.CommandText = "Select * from Customers"
                  Reader = Cmd.ExecuteReader()
                  Dim dtCustomers As New DataTable()
                  dtCustomers.Load(Reader)
                  Dim arrCustomers(dtCustomers.Rows.Count - 1) As Customer
                  Dim rowCount As Integer = 0
                  For Each Dr As DataRow In dtCustomers.Rows
                        arrCustomers(rowCount) = New Customer()
                        arrCustomers(rowCount).CustomerID = Convert.ToInt32(Dr("CustomerID"))
                        arrCustomers(rowCount).CustomerName = Dr("CustomerName").ToString()
                        arrCustomers(rowCount).Address = Dr("Address").ToString()
                        arrCustomers(rowCount).City = Dr("City").ToString()
                        arrCustomers(rowCount).State = Dr("State").ToString()
                        arrCustomers(rowCount).Age = Convert.ToInt32(Dr("Age"))
                        rowCount += 1
                  Next Dr
                  Conn.Close()
                  Cmd.Dispose()
                  Conn.Dispose()
                  Return arrCustomers
            End Function
 
            #End Region
      End Class
End Namespace
 
Step 10: In the Web.Config file, change the default ‘wsHttpBinding’ to ‘basicHttpBinding’ and remove ‘mexHttpBinding’.
Step 11: Compile the WCF service and publish the WCF service on IIS.
Step 12: To the same solution, add a new Silverlight application and name it as ‘SILV3_UserControlNAvigation’. To this project, add references of assemblies of the user controls which we have created in the above steps.
Step 13: In this Silverlight project add WCF service reference.
Step 14: To display Silverlight user controls referred in this project write the following xaml in ‘MainPage.Xaml’:
<UserControl x:Class="SILV3_UserControlNAvigation.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:Search="clr-namespace:SILV_SearchUserControl;assembly=SILV_SearchUserControl"
    xmlns:Details="clr-namespace:SILV_DetailedControl;assembly=SILV_DetailedControl"
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="500"
              Loaded="UserControl_Loaded">
    <Grid x:Name="LayoutRoot" Width="620" Height="480">
        <Grid.RowDefinitions>
            <RowDefinition Height="60"></RowDefinition>
            <RowDefinition Height="420"></RowDefinition>
        </Grid.RowDefinitions>
        <Search:SearchValue x:Name="searchData" Grid.Row="0"></Search:SearchValue>
        <Details:DetailedControl x:Name="detailedData" Grid.Row="1"></Details:DetailedControl>
    </Grid>
</UserControl>
 
The above xaml shows, the way using which Silverlight user controls can be referred into the application, the code is as below:
xmlns:Search="clr-namespace:SILV_SearchUserControl;assembly=SILV_SearchUserControl"
    xmlns:Details="clr-namespace:SILV_DetailedControl;assembly=SILV_DetailedControl"
 
Step 15: Open ‘MainPage.Xaml.cs’ and write the following code it is:
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Browser;
 
namespace SILV3_UserControlNAvigation
{
    public partial class MainPage : UserControl
    {
        MyRef.ServiceClient Proxy;
 
        MyRef.Customer[] arr;
  
        public MainPage()
        {
            InitializeComponent();
            searchData.TextBoxChanged += new TextChangedEventHandler(searchData_TextBoxChanged);
        }
 
        void searchData_TextBoxChanged(object sender, TextChangedEventArgs e)
        {
            //USe Linq to Filter Data
            var FilteredData = from Cust in arr
                               where Cust.CustomerName.StartsWith(searchData.strData)
                               select Cust;
 
            //Convert the IEnumaration to Object Array
            object[] CollectionToBind = FilteredData.ToArray();
 
 
 
            detailedData.BindDataGrid(CollectionToBind);
            if (CollectionToBind.Length == 0 || CollectionToBind == null)
            {
                HtmlPage.Window.Alert("Sorry No Data Available..");
            }
        }
 
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            Proxy = new SILV3_UserControlNAvigation.MyRef.ServiceClient();
            Proxy.GetAllCustomersCompleted += new EventHandler<SILV3_UserControlNAvigation.MyRef.GetAllCustomersCompletedEventArgs>(Proxy_GetAllCustomersCompleted);
            Proxy.GetAllCustomersAsync();
        }
 
        void Proxy_GetAllCustomersCompleted(object sender, SILV3_UserControlNAvigation.MyRef.GetAllCustomersCompletedEventArgs e)
        {
            if (e.Result != null)
            {
                arr = e.Result.ToArray();
 
                detailedData.BindDataGrid(arr);
            }
        }
    }
}
 
VB.NET
 
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Net
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Documents
Imports System.Windows.Input
Imports System.Windows.Media
Imports System.Windows.Media.Animation
Imports System.Windows.Shapes
Imports System.Windows.Browser
 
Namespace SILV3_UserControlNAvigation
      Partial Public Class MainPage
            Inherits UserControl
            Private Proxy As MyRef.ServiceClient
 
            Private arr() As MyRef.Customer
 
 
 
            Public Sub New()
                  InitializeComponent()
                  AddHandler searchData.TextBoxChanged, AddressOf searchData_TextBoxChanged
            End Sub
 
            Private Sub searchData_TextBoxChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
                  'USe Linq to Filter Data
                  Dim FilteredData = From Cust In arr _
                                     Where Cust.CustomerName.StartsWith(searchData.strData) _
                                     Select Cust
 
                  'Convert the IEnumaration to Object Array
                  Dim CollectionToBind() As Object = FilteredData.ToArray()
 
 
 
                  detailedData.BindDataGrid(CollectionToBind)
                  If CollectionToBind.Length = 0 OrElse CollectionToBind Is Nothing Then
                        HtmlPage.Window.Alert("Sorry No Data Available..")
                  End If
            End Sub
 
            Private Sub UserControl_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
                  Proxy = New SILV3_UserControlNAvigation.MyRef.ServiceClient()
                  AddHandler Proxy.GetAllCustomersCompleted, AddressOf Proxy_GetAllCustomersCompleted
                  Proxy.GetAllCustomersAsync()
            End Sub
 
            Private Sub Proxy_GetAllCustomersCompleted(ByVal sender As Object, ByVal e As SILV3_UserControlNAvigation.MyRef.GetAllCustomersCompletedEventArgs)
                  If e.Result IsNot Nothing Then
                        arr = e.Result.ToArray()
 
                        detailedData.BindDataGrid(arr)
                  End If
            End Sub
      End Class
End Namespace
 
If you see the code above, the loaded event of the Silverlight calls WCF service asynchronously and passes the retrieved values to ‘BindDataGrid()’ method of the ‘SILV_DetailedControl’.
In the ‘TextBoxChanged’ event of ‘SILV_SearchUserControl’, LINQ is used to query the result returned from WCF service by using ‘’strData’ dependency property declared into the ‘SILV_SearchUserControl’. This filtered data is converted into an array and passed to ‘BindDataGrid()’ method. This event actually raise the ‘TextChanged’ event into the ‘SILV_SearchUserControl’.
So the above code shows that ‘MainPage.Xaml.cs’ establishes communication between ‘SILV_SearchUserControl’ and ‘SILV_DetailedControl’ using ‘TextBoxChanged’ event and ‘strData’ dependency property declared into ‘SILV_SearchUserControl’ and ‘BindDataGrid()’ method declared in ‘SILV_DetailedControl’.       
Step 16: Run the application and the following result will be displayed:
InformationDetails_1
Step 17: Start entering a name into ‘Search Data’ text box and if the matching name is found, then following output will be displayed:
InformationDetails_2
The entire source code of this article can be downloaded over here.
Give a +1 to this article if you think it was well written. Thanks!
Recommended Articles


Page copy protected against web site content infringement by Copyscape


User Feedback

Post your comment
Name:  
E-mail: (Will not be displayed)
Comment:
Insert Cancel