Posted by : Adnan Farooq Hashmi Saturday, May 13, 2006






INETA Pakistan
WPF Databinding Tutorial

Watch the screencast for this tutorial/post here.



As promised, I am posting the code and explanation of the WPF Data Binding example I showed at the "Introduction to Windows Presentation Foundation" NED.NET user group event this Saturday.

A developer typically creates a class to represent an entity (table) in the database, whereby CRUD operations are performed by calling methods on the objects, e.g; for class Employee, objEmployee.Save(), objEmployee.Load(), objEmployee.Update(), objEmployee.Delete() etc. Also, everytime the values in the object change, the developer has to manually write code to update each of the UI elements that display the values stored in the object's members. The same holds true when the values on the UI change. With WPF, you can lessen your code greatly by binding the object directly with the UI (each of object's members data-bound to individual UI elements) using XAML syntax. I will be covering 3 different approaches for displaying data in the object onto the UI.

  1. Typical

  2. Using only C# [INotifyPropertyChanged]

  3. Using C# and XAML [INotifyPropertyChanged and XAML]



I am going to use the same "Examinee" table I used at several demos earlier this year for the HEC (Higher Education Commission) Web Service example. The columns for the table are

  • RollNo (PK, nvarchar(5), not null)

  • FirstName (nvarchar(10), not null)

  • LastName (nvarchar(10), not null)

  • University (nvarchar(50), not null)

  • MarksObtained (int, not null)

  • Exam (nvarchar(15), not null)



Typical Approach
The following provides a glimpse of what goes on in code with the typical approach. The developer creates the Examinee class for the table as follows: [For clarity, code has been colored and lines numbered. Also, ignore the SqlDataReader use for fetching a single row; it was done to lessen code ;)]


// =========================
// Code Listing 1
// =========================

1: using System;
2: using System.Collections.Generic;
3: using System.Text;
4: using System.Data;
5: using System.Data.SqlClient;
6:
7: namespace Demos.WPF.Binding.PropertyChangeNotNotified
8: {
9: class Examinee
10: {
11: private string _RollNo;
12: public string RollNo
13: {
14: get { return _RollNo; }
15: set { _RollNo = value; }
16: }
17:
18: private string _FirstName;
19: public string FirstName
20: {
21: get { return _FirstName; }
22: set { _FirstName = value; }
23: }
24:
25: private string _LastName;
26: public string LastName
27: {
28: get { return _LastName; }
29: set { _LastName = value; }
30: }
31:
32: private string _University;
33: public string University
34: {
35: get { return _University; }
36: set { _University = value; }
37: }
38:
39: private int _MarksObtained;
40: public int MarksObtained
41: {
42: get { return _MarksObtained; }
43: set { _MarksObtained = value; }
44: }
45:
46: private string _Exam;
47: public string Exam
48: {
49: get { return _Exam; }
50: set { _Exam = value; }
51: }
52:
53: public Examinee()
54: {
55: }
56:
57: public void Load()
58: {
59: if (_RollNo.Trim().Length == 0)
60: throw new Exception("Roll Number not specified.");
61:
62: SqlConnection oConnection = new SqlConnection([...]);
63: oConnection.Open();
64:
65: string str = "";
66: str += "SELECT * ";
67: str += "FROM Examinee ";
68: str += "WHERE RollNo = @RollNo";
69: SqlCommand oCommand = new SqlCommand(str, oConnection);
70: oCommand.CommandType = CommandType.Text;
71: oCommand.Parameters.Add(new SqlParameter("@RollNo", _RollNo));
72: SqlDataReader oDR = oCommand.ExecuteReader();
73:
74: if (oDR.Read())
75: {
76: _FirstName = oDR["FirstName"].ToString();
77: _LastName = oDR["LastName"].ToString();
78: _University = oDR["University"].ToString();
79: _MarksObtained = Convert.ToInt32(oDR["MarksObtained"]);
80: _Exam = oDR["Exam"].ToString();
81: }
82:
83: oCommand.Dispose();
84: oConnection.Close();
85: oConnection.Dispose();
86: }
87:
88: public void Update()
89: {
90: if (_RollNo.Trim().Length == 0)
91: throw new Exception("Roll Number not specified.");
92:
93: SqlConnection oConnection = new SqlConnection("[...]");
94: oConnection.Open();
95:
96: string str = "";
97: str += "UPDATE Examinee ";
98: str += "SET FirstName = @FirstName, ";
99: str += "LastName = @LastName, ";
100: str += "University = @University, ";
101: str += "MarksObtained = @Marks ";
102: SqlCommand oCommand = new SqlCommand(str, oConnection);
103: oCommand.CommandType = CommandType.Text;
104: oCommand.Parameters.Add(new SqlParameter("@FirstName", _FirstName));
105: oCommand.Parameters.Add(new SqlParameter("@LastName", _LastName));
106: oCommand.Parameters.Add(new SqlParameter("@University", _University));
107: oCommand.Parameters.Add(new SqlParameter("@Marks", _MarksObtained));
108: oCommand.Parameters.Add(new SqlParameter("@RollNo", _RollNo));
109: oCommand.ExecuteNonQuery();
110:
111: oCommand.Dispose();
112: oConnection.Close();
113: oConnection.Dispose();
114: }
115: }
116: }



I figured that since it is XAML that we are demonstrating, might as well develop the UI in Microsoft Cider, the Visual Studio 2005 add-in visual designer for XAML. So, to use the above class, the UI would be something like this.



The resulting XAML for the window shown above is



<!--
=========================
Code Listing 2
Simple.xaml
=========================
-->

<Window
x:Class="Demos.WPF.Binding.Simple"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Demos.WPF.Binding" Height="209" Width="371"
Background="WhiteSmoke" WindowStartupLocation="CenterScreen">
<Grid>
<Label
VerticalAlignment="Top"
HorizontalAlignment="Left"
Grid.Column="0"
Grid.ColumnSpan="1"
Grid.Row="0"
Grid.RowSpan="1"
Margin="21.37,23,0,0"
Width="76.63"
Height="23.2766666666667"
Name="label1">Roll Number:</Label>
<Label
VerticalAlignment="Top"
HorizontalAlignment="Left"
Grid.Column="0"
Grid.ColumnSpan="1"
Grid.Row="0"
Grid.RowSpan="1"
Margin="20.37,42.7233333333333,0,0"
Width="76.63"
Height="23.2766666666667"
Name="label2">First Name:</Label>
<Label
VerticalAlignment="Top"
HorizontalAlignment="Left"
Grid.Column="0"
Grid.ColumnSpan="1"
Grid.Row="0"
Grid.RowSpan="1"
Margin="20.37,66,0,0"
Width="75.63"
Height="23.2766666666667"
Name="label3">Last Name:</Label>
<Label
VerticalAlignment="Top"
HorizontalAlignment="Left"
Grid.Column="0"
Grid.ColumnSpan="1"
Grid.Row="0"
Grid.RowSpan="1"
Margin="21.37,89,0,0"
Width="62.63"
Height="23.2766666666667"
Name="label4">University:</Label>
<Label
VerticalAlignment="Top"
HorizontalAlignment="Left"
Grid.Column="0"
Grid.ColumnSpan="1"
Grid.Row="0"
Grid.RowSpan="1"
Margin="21.37,112.723333333333,0,0"
Width="62.63"
Height="23.276666666666671"
Name="label5">Marks:</Label>
<TextBox
VerticalAlignment="Top"
HorizontalAlignment="Stretch"
Grid.Column="0"
Grid.ColumnSpan="1"
Grid.Row="0"
Grid.RowSpan="1"
Margin="134,23,56,0"
Width="NaN"
Height="20"
Name="txtRollNo"
BorderBrush="#FF000000"
Foreground="#FF336699"></TextBox>
<TextBox
VerticalAlignment="Top"
HorizontalAlignment="Stretch"
Grid.Column="0"
Grid.ColumnSpan="1"
Grid.Row="0"
Grid.RowSpan="1"
Margin="134,46,56,0"
Width="NaN"
Height="20"
Name="txtFirstName"
Foreground="#FF336699"
BorderBrush="#FF000000"></TextBox>
<TextBox
VerticalAlignment="Top"
HorizontalAlignment="Stretch"
Grid.Column="0"
Grid.ColumnSpan="1"
Grid.Row="0"
Grid.RowSpan="1"
Margin="134,69.2766666666667,56,0"
Width="NaN"
Height="20"
Name="txtLastName"
Foreground="#FF336699"
BorderBrush="#FF000000"></TextBox>
<TextBox
VerticalAlignment="Top"
HorizontalAlignment="Stretch"
Grid.Column="0"
Grid.ColumnSpan="1"
Grid.Row="0"
Grid.RowSpan="1"
Margin="134,92.2766666666667,56,0"
Width="NaN"
Height="20"
Name="txtUniversity"
Foreground="#FF336699"
BorderBrush="#FF000000"></TextBox>
<TextBox
VerticalAlignment="Top"
HorizontalAlignment="Stretch"
Grid.Column="0"
Grid.ColumnSpan="1"
Grid.Row="0"
Grid.RowSpan="1"
Margin="134,114,56,0"
Width="NaN"
Height="20"
Name="txtMarks"
Foreground="#FF336699"
BorderBrush="#FF000000"></TextBox>
<Button
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
Grid.Column="0"
Grid.ColumnSpan="1"
Grid.Row="0"
Grid.RowSpan="1"
Margin="134,0,0,11"
Width="51"
Height="23"
Name="btnLoad"
Click="btnLoad_Click">Load</Button>
<Button
VerticalAlignment="Bottom"
HorizontalAlignment="Right"
Grid.Column="0"
Grid.ColumnSpan="1"
Grid.Row="0"
Grid.RowSpan="1"
Margin="0,0,128.5625,11"
Width="48.4375"
Height="23"
Name="btnUpdate"
Click="btnUpdate_Click">Update</Button>
<Button
VerticalAlignment="Bottom"
HorizontalAlignment="Right"
Grid.Column="0"
Grid.ColumnSpan="1"
Grid.Row="0"
Grid.RowSpan="1"
Margin="0,0,56,11"
Width="55"
Height="23"
Name="btnCheck"
Click="btnCheck_Click">Check</Button>
<Button
VerticalAlignment="Top"
HorizontalAlignment="Right"
Grid.Column="0"
Grid.ColumnSpan="1"
Grid.Row="0"
Grid.RowSpan="1"
Margin="0,115,12,0"
Width="39"
Height="19"
Name="btnAddTen"
Click="btnAddTen_Click">+10</Button>
</Grid>
</Window>



The app lets the user enter the Roll Number for a student, and dislays the Examinee's stats when the "Load" button is clicked. The user can then make any changes in values if required (except for the Roll Number), and click "Update" to make the changes in the database. Nothing fancy here. The code-behind file is pretty straight-forward as well. I have placed a "Check" button on the form to let the user view and compare the values in both, the Examinee object and on the UI.


// =========================
// Code Listing 3
// Simple.xaml.cs
// =========================

1: using System;
2: using System.Windows;
3: using System.Windows.Controls;
4: using System.Windows.Data;
5: using System.Windows.Documents;
6: using System.Windows.Media;
7: using System.Windows.Media.Imaging;
8: using System.Windows.Shapes;
9: using Demos.WPF.Binding.PropertyChangeNotNotified;
10:
11: namespace Demos.WPF.Binding
12: {
13: public partial class Simple : Window
14: {
15: Examinee oExaminee = new Examinee();
16:
17: public Simple()
18: {
19: InitializeComponent();
20: }
21:
22: private void btnLoad_Click(object sender, EventArgs e)
23: {
24: try
25: {
26: oExaminee.RollNo = txtRollNo.Text;
27: oExaminee.Load();
28:
29: // tedious code to update UI with object's properties
30: txtFirstName.Text = oExaminee.FirstName;
31: txtLastName.Text = oExaminee.LastName;
32: txtUniversity.Text = oExaminee.University;
33: txtMarks.Text = oExaminee.MarksObtained.ToString();
34: }
35: catch (Exception oEx)
36: {
37: MessageBox.Show(oEx.Message);
38: }
39: }
40:
41: private void btnUpdate_Click(object sender, EventArgs e)
42: {
43: try
44: {
45: // tedious code to set object's properties from UI elements
46: oExaminee.FirstName = txtFirstName.Text;
47: oExaminee.LastName = txtLastName.Text;
48: oExaminee.University = txtUniversity.Text;
49: oExaminee.MarksObtained = Convert.ToInt32(txtMarks.Text);
50:
51: oExaminee.Update();
52: MessageBox.Show("Information Updated.", "Update");
53: }
54: catch (Exception oEx)
55: {
56: MessageBox.Show(oEx.Message);
57: }
58: }
59:
60: private void btnCheck_Click(object sender, EventArgs e)
61: {
62: string str = string.Format("OBJECT:\nFirstName: {0}"
63: + "\nLastName: {1}"
64: + "\nUniversity: {2}"
65: + "\nMarksObtained: {3}"
66: + "\n\nUI:"
67: + "\nFirst Name: {4}"
68: + "\nLast Name: {5}"
69: + "\nUniversity: {6}"
70: + "\nMarksObtained: {7}",
71: oExaminee.FirstName,
72: oExaminee.LastName,
73: oExaminee.University,
74: oExaminee.MarksObtained.ToString(),
75: txtFirstName.Text,
76: txtLastName.Text,
77: txtUniversity.Text,
78: txtMarks.Text);
79: MessageBox.Show(str);
80: }
81:
82: private void btnAddTen_Click(object sender, EventArgs e)
83: {
84: if ((oExaminee.MarksObtained + 10) > 100)
85: oExaminee.MarksObtained = 100;
86: else
87: oExaminee.MarksObtained += 10;
88: }
89:
90: }
91: }



Notice that in the event handlers for both, the "Load" (lines 29 to 33) and "Update" (lines 45 to 49) buttons, the values need to be shuttled between the object and the UI elements to keep them synchronized. If the form had, lets say, 30+ UI elements, the developer would have to write atleast 60 additional lines of code to achieve this synchronization. Ironically, this is what usually happens.

Using only C# [INotifyPropertyChanged]
The typical approach is pretty cumbersome. The same result can be achieved by implementing the INotifyPropertyChanged interface in the Examinee class to generate an event everytime the value in the object's property changes. I have highlighted portions in code listing 4 that need to be added to implement INotifyPropertyChanged.


// =========================
// Code Listing 4
// =========================

1: using System;
2: using System.Collections.Generic;
3: using System.Text;
4: using System.Data;
5: using System.Data.SqlClient;
6: using System.ComponentModel;
7:
8: namespace Demos.WPF.Binding.PropertyChangeNotified
9: {
10: class Examinee : INotifyPropertyChanged
11: {
12: private string _RollNo;
13: public string RollNo
14: {
15: get { return _RollNo; }
16: set
17: {
18: _RollNo = value;
19: // Notify Property Change
20: OnPropertyChanged("RollNo");
21: }
22: }
23:
24: private string _FirstName;
25: public string FirstName
26: {
27: get { return _FirstName; }
28: set
29: {
30: _FirstName = value;
31: // Notify Property Change
32: OnPropertyChanged("FirstName");
33: }
34: }
35:
36: private string _LastName;
37: public string LastName
38: {
39: get { return _LastName; }
40: set
41: {
42: _LastName = value;
43: // Notify Property Change
44: OnPropertyChanged("LastName");
45: }
46: }
47:
48: private string _University;
49: public string University
50: {
51: get { return _University; }
52: set
53: {
54: _University = value;
55: // Notify Property Change
56: OnPropertyChanged("University");
57: }
58: }
59:
60: private int _MarksObtained;
61: public int MarksObtained
62: {
63: get { return _MarksObtained; }
64: set
65: {
66: _MarksObtained = value;
67: // Notify Property Change
68: OnPropertyChanged("MarksObtained");
69: }
70: }
71:
72: private string _Exam;
73: public string Exam
74: {
75: get { return _Exam; }
76: set
77: {
78: _Exam = value;
79: // Notify Property Change
80: OnPropertyChanged("Exam");
81: }
82: }
83:
84: public Examinee()
85: {
86: }
87:
88: public void Load()
89: {
90: if (_RollNo.Trim().Length == 0)
91: throw new Exception("Roll Number not specified.");
92:
93: SqlConnection oConnection = new SqlConnection([...]);
94: oConnection.Open();
95:
96: string str = "";
97: str += "SELECT * ";
98: str += "FROM Examinee ";
99: str += "WHERE RollNo = @RollNo";
100: SqlCommand oCommand = new SqlCommand(str, oConnection);
101: oCommand.CommandType = CommandType.Text;
102: oCommand.Parameters.Add(new SqlParameter("@RollNo", _RollNo));
103: SqlDataReader oDR = oCommand.ExecuteReader();
104:
105: if (oDR.Read())
106: {
107: _FirstName = oDR["FirstName"].ToString();
108: _LastName = oDR["LastName"].ToString();
109: _University = oDR["University"].ToString();
110: _MarksObtained = Convert.ToInt32(oDR["MarksObtained"]);
111: _Exam = oDR["Exam"].ToString();
112:
113: // following code raises onPropertyChanged event because
114: // properties are being set from within the class itself.

115: OnPropertyChanged("FirstName");
116: OnPropertyChanged("LastName");
117: OnPropertyChanged("University");
118: OnPropertyChanged("MarksObtained");
119: OnPropertyChanged("Exam");
120: }
121:
122: oCommand.Dispose();
123: oConnection.Close();
124: oConnection.Dispose();
125: }
126:
127: public void Update()
128: {
129: if (_RollNo.Trim().Length == 0)
130: throw new Exception("Roll Number not specified.");
131:
132: SqlConnection oConnection = new SqlConnection("[...]");
133: oConnection.Open();
134:
135: string str = "";
136: str += "UPDATE Examinee ";
137: str += "SET FirstName = @FirstName, ";
138: str += "LastName = @LastName, ";
139: str += "University = @University, ";
140: str += "MarksObtained = @Marks ";
141: SqlCommand oCommand = new SqlCommand(str, oConnection);
142: oCommand.CommandType = CommandType.Text;
143: oCommand.Parameters.Add(new SqlParameter("@FirstName", _FirstName));
144: oCommand.Parameters.Add(new SqlParameter("@LastName", _LastName));
145: oCommand.Parameters.Add(new SqlParameter("@University", _University));
146: oCommand.Parameters.Add(new SqlParameter("@Marks", _MarksObtained));
147: oCommand.Parameters.Add(new SqlParameter("@RollNo", _RollNo));
148: oCommand.ExecuteNonQuery();
149:
150: oCommand.Dispose();
151: oConnection.Close();
152: oConnection.Dispose();
153: }
154:
155: public event PropertyChangedEventHandler PropertyChanged;
156: protected void OnPropertyChanged(string propertyName)
157: {
158: if (this.PropertyChanged != null)
159: PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
160: }
161: }
162: }



Changes made to class Examinee of listing 1 are given in code listing 4 above. The INotifyPropertyChanged interface implementation allows the class to raise the PropertyChanged event everytime a property value changes. The above class can now be instanitated in the code-behind file (see code listing 5 below) of another XAML file (WithoutXAML.xaml) which is similar to that of code listing 2.


// =========================
// Code Listing 5
// WithoutXAML.xaml.cs
// =========================

1: using System;
2: using System.Windows;
3: using System.Windows.Controls;
4: using System.Windows.Data;
5: using System.Windows.Documents;
6: using System.Windows.Media;
7: using System.Windows.Media.Imaging;
8: using System.Windows.Shapes;
9: using Demos.WPF.Binding.PropertyChangeNotified;
10:
11: namespace Demos.WPF.Binding
12: {
13: public partial class WithoutXAML : Window
14: {
15: Examinee oExaminee = new Examinee();
16:
17: public Simple()
18: {
19: InitializeComponent();
20: }
21:
22: private void WithoutXAML_Load(object sender, EventArgs e)
23: {
24: oExaminee.PropertyChanged += oExaminee_PropertyChanged;
25: }
26:
27: // Property Changed Event Handler [Pathetic Code!]
28: private void oExaminee_PropertyChanged(object sender,
29: PropertyChangedEventArgs e)
30: {
31: switch(e.PropertyName)
32: {
33: case "FirstName":
34: txtFirstName.Text = oExaminee.FirstName;
35: break;
36: case "LastName":
37: txtLastName.Text = oExaminee.LastName;
38: break;
39: case "University":
40: txtUniversity.Text = oExaminee.University;
41: break;
42: case "MarksObtained":
43: txtMarks.Text = oExaminee.MarksObtained.ToString();
44: break;
45: }
46: }
47:
48: private void btnLoad_Click(object sender, EventArgs e)
49: {
50: try
51: {
52: oExaminee.RollNo = txtRollNo.Text;
53: oExaminee.Load();
54: }
55: catch (Exception oEx)
56: {
57: MessageBox.Show(oEx.Message);
58: }
59: }
60:
61: private void btnUpdate_Click(object sender, EventArgs e)
62: {
63: try
64: {
65: oExaminee.Update();
66: MessageBox.Show("Information Updated.", "Update");
67: }
68: catch (Exception oEx)
69: {
70: MessageBox.Show(oEx.Message);
71: }
72: }
73:
74: private void btnCheck_Click(object sender, EventArgs e)
75: {
76: string str = string.Format("OBJECT:\nFirstName: {0}"
77: + "\nLastName: {1}"
78: + "\nUniversity: {2}"
79: + "\nMarksObtained: {3}"
80: + "\n\nUI:"
81: + "\nFirst Name: {4}"
82: + "\nLast Name: {5}"
83: + "\nUniversity: {6}"
84: + "\nMarksObtained: {7}",
85: oExaminee.FirstName,
86: oExaminee.LastName,
87: oExaminee.University,
88: oExaminee.MarksObtained.ToString(),
89: txtFirstName.Text,
90: txtLastName.Text,
91: txtUniversity.Text,
92: txtMarks.Text);
93: MessageBox.Show(str);
94: }
95:
96: private void btnAddTen_Click(object sender, EventArgs e)
97: {
98: if ((oExaminee.MarksObtained + 10) > 100)
99: oExaminee.MarksObtained = 100;
100: else
101: oExaminee.MarksObtained += 10;
102: }
103:
104: ///////////////////////////////////
105: // WARNING: Pathetic Code below! //
106: // Event-handlers for text-boxes //
107: ///////////////////////////////////

108:
109: private void txtRollNo_TextChanged(object sender,
110: EventArgs e)
111: {
112: oExaminee.RollNo = txtRollNo.Text;
113: }
114:
115: private void txtFirstName_TextChanged(object sender,
116: EventArgs e)
117: {
118: oExaminee.FirstName = txtFirstName.Text;
119: }
120:
121: private void txtLastName_TextChanged(object sender,
122: EventArgs e)
123: {
124: oExaminee.LastName = txtLastName.Text;
125: }
126:
127: private void txtUniversity_TextChanged(object sender,
128: EventArgs e)
129: {
130: oExaminee.University = txtUniversity.Text;
131: }
132:
133: private void txtMarks_TextChanged(object sender,
134: EventArgs e)
135: {
136: int marks = 0;
137: if (int.TryParse(txtMarks.Text, out marks))
138: oExaminee.MarksObtained = marks;
139: }
140: }
141: }



Notice that the oExaminee_PropertyChanged event handler (lines 27 to 46) updates the UI with object values everytime a PropertyChanged event occurs on the Examinee object. To update the object with UI values, TextChanged event handlers were added for all the textboxes (see lines 104 to 139).

Notice also that the "Load" and "Update" button event handlers do not contain the Object-to-UI and UI-to-Object update code as that is now being handles by the event handlers.

However, just because we implemented the INotifyPropertyChanged interface in our Examinee class, we were not absolved of the responsibility to write custom event handlers for the object and UI update logic. The whole point of all the above code is to show the tedious coding required to keep the object and UI in sync.

Using C# and XAML [INotifyPropertyChanged and XAML]
Both the approaches described above were used with typical Windows applications, although I created the form using XAML. The final approach (and the right one at that) uses the Data Binding syntax in XAML, and would show you how easy it is to bind UI elements directly to an object that implements INotifyPropertyChanged.

To see WPF Data Binding in action, the Binding sytax in the textboxes' "Text" property values. The XAML file would not look something like code listing 6. [Changes from previous XAML file have been highlighted.]



<!--
=========================
Code Listing 6
WithXAML.xaml
=========================
-->

<Window
x:Class="Demos.WPF.Binding.WithXAML"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Binding - Using XAML Binding Syntax"
Height="209" Width="371" Background="WhiteSmoke"
WindowStartupLocation="CenterScreen" Loaded="WithXAML_Load" >
<Grid x:Name="myGrid" >
<Label ...>Roll Number:</Label>
<Label ...>First Name:</Label>
<Label ...>Last Name:</Label>
<Label ...>University:</Label>
<Label ...>Marks:</Label>
<TextBox
...
Name="txtRollNo"
Text="{Binding Path=RollNo}"></TextBox>
<TextBox
...
Name="txtFirstName"
Text="{Binding Path=FirstName}"></TextBox>
<TextBox
...
Name="txtLastName"
Text="{Binding Path=LastName}"></TextBox>
<TextBox
...
Name="txtUniversity"
Text="{Binding Path=University}"></TextBox>
<TextBox
...
Name="txtMarks"
Text="{Binding Path=MarksObtained}"></TextBox>
<Button ...>Load</Button>
<Button ...>Update</Button>
<Button ...>Check</Button>
<Button ...>+10</Button>
</Grid>
</Window>



The Binding statement added to the XAML above allows you to discard all the event-handlers in the code-behind that were needed before. The function that we wanted from our app is now achievable with a lot less code.


// =========================
// Code Listing 7
// WithXAML.xaml.cs
// =========================

1: using System;
2: using System.Windows;
3: using System.Windows.Controls;
4: using System.Windows.Data;
5: using System.Windows.Documents;
6: using System.Windows.Media;
7: using System.Windows.Media.Imaging;
8: using System.Windows.Shapes;
9: using Demos.WPF.Binding.PropertyChangeNotified;
10:
11: namespace Demos.WPF.Binding
12: {
13: public partial class WithXAML : Window
14: {
15: Examinee oExaminee = new Examinee();
16:
17: public WithXAML()
18: {
19: InitializeComponent();
20: }
21:
22: private void WithXAML_Load(object sender, EventArgs e)
23: {
24: myGrid.DataContext = oExaminee;
25: }
26:
27: private void btnAddTen_Click(object sender, EventArgs e)
28: {
29: if ((oExaminee.MarksObtained + 10) > 100)
30: oExaminee.MarksObtained = 100;
31: else
32: oExaminee.MarksObtained += 10;
33: }
34:
35: private void btnLoad_Click(object sender, EventArgs e)
36: {
37: try
38: {
39: oExaminee.Load();
40: }
41: catch (Exception oEx)
42: {
43: MessageBox.Show(oEx.Message);
44: }
45: }
46:
47: private void btnUpdate_Click(object sender, EventArgs e)
48: {
49: try
50: {
51: oExaminee.Update();
52: MessageBox.Show("Information Updated.", "Update");
53: }
54: catch (Exception oEx)
55: {
56: MessageBox.Show(oEx.Message);
57: }
58: }
59:
60: private void btnCheck_Click(object sender, EventArgs e)
61: {
62: string str = string.Format("OBJECT:\nFirstName: {0}"
63: + "\nLastName: {1}"
64: + "\nUniversity: {2}"
65: + "\nMarksObtained: {3}"
66: + "\n\nUI:"
67: + "\nFirst Name: {4}"
68: + "\nLast Name: {5}"
69: + "\nUniversity: {6}"
70: + "\nMarksObtained: {7}",
71: oExaminee.FirstName,
72: oExaminee.LastName,
73: oExaminee.University,
74: oExaminee.MarksObtained.ToString(),
75: txtFirstName.Text,
76: txtLastName.Text,
77: txtUniversity.Text,
78: txtMarks.Text);
79: MessageBox.Show(str);
80: }
81: }
82: }



The only change occured in line 24 in the WithXAML_Load event handler; the Examinee object was assigned to the main Grid's DataContext property. The highlighted statement in code listing 7 above defines a data context for the Grid, the UI element that encloses all the other UI controls. WPF introduces the concept of "Dependency Properties" that enables a XAML UI element to inherit a value from its parent UI control (in this case the grid). Once the data context is set in the form load event, the {Binding Path=...} statements in XAML simply retrieve the values from the bound object or resource and place them in the target textbox. If you run the application now, everytime you change the value in a textbox, the corresponding value in the object changes (You can test this by clicking the "Check" button after you update the value in a textbox). Same holds true for the object. If you click the "+10" (btnAddTen) button, the value in the object is incremented by 10. However, you will also see that the value is automatically updated in the textbox as well.

Try the above, and feel free to ask any questions that you may have. I have really outdone myself with this post. I guess I am all set to write a book now.

Download source code from here.

59 Responses so far.

  1. Anonymous says:

    This is great stuff. I wish though you can drag the datasource to XAML designer like a Windows Form and have it automatically create the textboxes and labels.

  2. Anonymous says:

    Agreed. Surely without the complete gui integration (that we already have for windows forms), this is actually a step backward. Would have been nicer to see MS go the whole hog and implement the designer more fully.

  3. Eric says:

    Is it the same 'manual' method in Orcas? I figured they would integrate a GUI drag'n'drop version for Cider. I am personally having a heck of a time finding out how to bind to my SQL database in WPF...


    This is helping.

  4. Anonymous says:

    This is a great stuff.

    Thank you very much!

  5. Anonymous says:

    Great explanation. Thanks for doing this.
    I want to ask you what is the difference between DataContext and ItemsSource. I used ItemsSource before, and you are using DataContext, but concept is the same.

    Thanks again.

    Sincerely,
    Vlad Bezden

  6. Anonymous says:

    A nice sample - is ther any reason why you would only use INotify over Dependancy Properties?

  7. Brett Ryan says:

    From my understanding ItemsSource is for individual content controls such as a ListBox where the source of the ListBox will be populated from items obtained from the ItemsSource, similar to DataSource in Windows Forms.

    However, DataContext is a neat idea that allows you to bind an object to a parent container item such as a StackPanel or Grid, this allows containing controls to access the DataContext as it is accessible throught the child controls, this way you can change a single DataContext property and all the corresponding child elements will be updated without you having to worry about rebinding them.

    I'm fairly new to WPF, and still have the training wheels on, so please correct me if I'm wrong in any way, but this is my understanding.

    I'm actually fond of not having a designer, it is forcing me to understand the way that the WPF technoligy is used, I do however use Expression Blend and have a fondle of what's been generated to try to really understand what's the best way to do something, though for the most part I find constructing a UI via text in VS2005 quit nice, mind you I do have a problem with my own custom namespaces not rendering within VS (mlns:local="clr-namespace:Example.Namespace"), this should go away with VS2008 :)

  8. Radhika says:

    Hi,Nice article..
    But Could u please tell me How to Print a DataGridView thru C# and XAML using WPF Windows Application?
    Actualy binding the simple stright way fields is simple.But Binding a DataGridView and printing is quite tought.I have been trying it for months and searched the whole web(i guess) ..If u could do this..Try it. Thanking you in advance.

  9. Robert says:

    Very nice, simple and good example for beginner like me. I have a better undserstanding of WPF binding with your simple example. As for datagrid, to my knowledge it doesn't exist yet for WPF. You can get it from a third-party company for free at http://xceed.com/

    cheers!

  10. bleh says:

    Thanks for this article, it lays it out nice and simply in a straight forward manner.

    One thing that might be good to note (and which I spent way too much time puzzling over), is that if you have an object that is set as the DataContext for an item and at a later point in your code you change that object to point to a new instance of its class (or something else), then you must also update the DataContext so it can still find the object.

    Simple and obvious, but good to know.

  11. snuckles says:

    Yes, you out did yourself with this post and you should write a book. Thank you for sharing your knowledge you really helped me out of a serious jam!

    I do have a question, originally I was setting the data context for the stack panel (in my case) in the xaml instead of the code as you have done. It only worked for the inital settings but after that it seemed like the binding was removed. Is there any info or a known bug with setting the data context in the xaml? Thanks.

  12. Harshali says:

    Hi,

    I am binding createddatetime with a textblock. The createddatetime appears in correct format in the cs part but after binding in xaml part, a fixed format is displayed irrespective of what is set in the cs part.Is there any way by which I can check and set the format according to the cultureinfo.currentculture during binding? Can you please help me out with this?

    Regards,
    Harshali.

  13. Anonymous says:

    This is the BEST freaking tutorial on basic databinding like this available on the damn internet. Took me years to find this

  14. Anonymous says:

    behan ke lode pakistani...
    teri maa chudayee all over the world.
    sala terrorist kahin kaa

  15. @ Last Anonymous (from India)

    Thank you for your stupid comment aganist Pakistan and Pakistanis. I forgive you though.

    The weak can never forgive. Forgiveness is the attribute of the strong. ~Mahatma Gandhi

  16. Anonymous says:

    Thanks for the great article...

    Also, my apologies as an Indian on behalf of the anonymous idiot making the offensive remark. I respect your knowledge and your efforts in spreading it.

    Keep up with the good work!!!

  17. Fred says:

    By the way, there are several data binding webcasts on http://www.microsoft.com/events/series/msdnnetframework3.aspx.

  18. Anonymous says:

    Tx

  19. Richard says:

    i tried many resources to udnerstand INotifiedPropertyChanged Event, and this post is the most comprehensive and easy to understand piece.

    thank you for the writing!!

  20. I really inspired of your article its really helpful.. i'm also a C# developer currently working on wpf..
    can you send me your mail address for further communication..
    my gmail id is im.masoomali@gmail.com
    and masoomaliasghar@hotmail.com

    Regards,
    Masoom ali

  21. Anonymous says:

    Great sample, man, thanks a lot. The DataContext property appears to be a crucial yet oft-omitted detail of this whole data binding thing. It is what had me for a while.

  22. Hi,

    I am Murali Krishna. The article so simple to understand and clarified lots of my doubts have you written any articles on MVVM Model.If so please mail me the links to the below mailid: nmrkmsis2000@rediffmail.com

    Thanks & Regards,
    N.Murali Krishna.

  23. Srikar says:

    this was very clear. thank you

  24. Ferenc says:

    This is Great stuff. I have a question.
    I have a datagrid and 3 textbox.
    The textbox show the data, but the datagrid is empty.
    What sould I do ?
    wf:DataGrid Name="dgDirectory" AutoGenerateColumns="false" ItemsSource="{Binding Path=myGrid}" SelectedItem="{Binding SelectedItem}" SelectionMode="Single"
    wf:DataGrid.Columns
    wf:DataGridTextColumn Binding="{Binding Path=DirectoryID}" Header="DirectoryID" /
    wf:DataGridTextColumn Binding="{Binding Path=DirectoryPath}" Header="DirectoryPath" /
    wf:DataGridTextColumn Binding="{Binding Path=DirectoryKeep}" Header="DirectoryKeep" /
    /wf:DataGrid.Columns
    wf:DataGrid

    (I miss the open and close tag for the post..)

    Thanks in advice!

  25. Anonymous says:

    Nice, thanks.

  26. Anonymous says:

    How can i use data binding in resource dictionary.I need to bind textblock from a property of class.

  27. mtaipan says:

    Really nice post. Thanks for FINALLY clarifying data binding techniques!

  28. Anonymous says:

    So wonderful article. Two Thumbs UP! :)

  29. Mutia says:

    What if i don’t want to use XAML code and only C# code, I can get to the part of binding data to the itemlist
    “itemlist.itemsource = list”

    but how can i select programatically wich data i want to be displayed

    Xaml:

    in c# how can i achieve this?

    (http://mutiarar06.student.ipb.ac.id)

  30. selly says:

    thanks for information

  31. arieff says:

    This is a great stuff.

    Thank you very much!

  32. yulianti says:

    Two Thumbs up! :D

  33. eka says:

    nice post. that is awesome!

  34. sheringham says:

    thanks for your information. that is awesome!

    thumbs up

  35. john ted says:

    thanks for your information.that is awesome!

    thumbs up!

  36. oky says:

    thanls for informasioan...
    i like wpf c#...

  37. rifky says:

    yeah...great...i'll try...

  38. utari says:

    really great..good job

  39. Anonymous says:

    Thanks a lot, helped to understand property binding so quick and fast.

  40. Sowmia says:

    Hi,

    I am new to WPF. I have a question in

    Code Listing 7
    WithXAML.xaml.cs

    On the btnload_Click and btnUpdate_Click I don't see any connection made to the database..

  41. Anonymous says:

    great article!!! Thanks a lot

  42. Anonymous says:

    I wish I'd been able to find that line about setting the datacontext a few days ago!

    I have 5 or 6 books on wpf and c#, and none of them seem to include that pretty vital setting!

    Thanks so much

  43. Anonymous says:

    Thank you for this article.

    It's very clear.
    In just one hour it let me to convert a UI mask from WinForm.

  44. nicky says:

    buddy... great... it is a relly great example to understand...

  45. Nathan says:

    Thanks for the excellent piece of work. I am attempting to bind a linq data source to a combo box in a data grid. The purpose being to allow population of the data grid by selecting from a pre-defined list. I understand DataContext inheritance for the grid but need the combo box to use a different data source. Can you provide anyinformation for this?

    Thanks

  46. Priya says:

    Excellent article for beginners... I was searching for an article to learn the binding concept clearly. Finally i got it.... Good work. Thanks a lot...

  47. Priya says:

    nan Farooq Hashmi,

    Great Stuff!!! I want to learn about the other basic concepts in WPF. Could you please provide me the link if you have posted any other article...

  48. Anonymous says:

    that was very clear and neat.

    Thanks,

    RSN

  49. Well wisher says:

    Fantastic article..awesome.
    Thank you so much; you made my day wonderful :)

  50. rrossenbg says:

    Simple, full and clear explanation.
    Thank you!

  51. Kathirvel says:

    Nice.. i got the basics..
    thanks..

  52. Kathirvel says:

    Nice.. i got the basics..
    thanks..

  53. Anonymous says:

    Nagaraj:
    Great Article.... would refer the link to friends.....

  54. Sonal Ahluwalia says:

    Hi,

    I am new to WPF. I have a question in

    Code Listing 7
    WithXAML.xaml.cs

    On the btnload_Click and btnUpdate_Click I don't see any connection made to the database..

  55. Anonymous says:

    mutta kudhi...

  56. It was such a good article and so much related to our directory.

  57. timothyceri says:

    I appreciate your idea here. Definitely it has a good content. Thank you for imparting more of your own thoughts. Good job! katherine telugu actress

My Passion, My Inspiration, My Pakistan

Popular Posts