Click here to Skip to main content
15,881,089 members
Articles / Desktop Programming / WPF

.NET WPF 4: Discovering the power of data binding

Rate me:
Please Sign up or sign in to vote.
3.40/5 (4 votes)
7 May 2010CPOL7 min read 33.1K   1.1K   15   2
One thing that has changed from Windows Forms to WPF is the way we bind our controls to our sources of data and show them to the user.

WPFDataBinding_src

Introduction

One thing that has changed from Windows Forms to WPF is the way we show our data; not only the visual aspect, what really has changed is how we bind our controls to our sources of data and show them to the user.

To illustrate a bit the new paradigm of WPF data binding, we will make a small application (small because between XAML and code, it is no more than 85 lines) that will allow us to display in a datagrid orders from our customers, and by selecting any order, we can see it in a listbox next to the datagrid products included in the order and below the contact information for the customer.

The Code

Preparing the Data Source

In this case, I used the Northwind database in Access to make it easier to work with it. I have included it in the sample project that is at the top of this page, but you can also download it here.

Once you have the database Nwind.mdb, copy it to your project directory, and from the menu Project -> Add Existing Item, select the NWind.mdb file, and Visual Studio 2010 will open the Data Source Configuration Wizard:

WPFDataBinding2.PNG

Select DataSet and press Next, where the wizard prompts you to select the objects of the database that we want to include in our dataset:

WPFDataBinding3.PNG

I selected all tables, but our project only needs Customers, Order Details, Orders, Products, and Shippers. Select the tables, give a name to the dataset in the dataset Name textbox and press Finish. The database will be included in our project and Visual Studio will create a typed dataset file (.xsd). If you open it, you'll see something like this (depending on the tables you've selected):

WPFDataBinding4.PNG

This is a visual representation of your dataset. Each box has two sections, the top is the table with its fields, and the bottom (called like the table + TableAdapter) is the object that we use to retrieve records from that table. You also see lines connecting the tables. These are the relationships between them. If you click on them, they are placed in blue; and remove them all since we will create them in the next step.

Let's create a new relationship between the tables Orders (parent table) and Order Details (child table). For this, press the right mouse button on the header of Order Details, and on the context menu, select Add and click on Relation. You will see a window like this:

WPFDataBinding6.PNG

Orders and Order Details Relation

Property

Value

Name

OrderDetails

Parent Table

Orders

Child Table

Order Details

Key Columns

OrderID

Foreign Key Columns

OrderID

Now create the relationship between the Order Details and Products tables in the same manner and with the following parameters:

Order Details and Products Relation

Property

Value

Name

ProductsOrderDetails

Parent Table

OrderDetails

Child Table

Products

Key Columns

ProductID

Foreign Key Columns

ProductID

Also, the relationship between the Customers and Orders tables:

Orders and Customers Relation

Property

Value

Name

CustomersOrders

Parent Table

Orders

Child Table

Customers

Key Columns

CustomerID

Foreign Key Columns

CustomerID

And finally, the relationship between the Orders table and Shippers:

Orders and Shippers Relation

Property

Value

Name

ShippersOrders

Parent Table

Orders

Child Table

Shippers

Key Columns

ShipVia

Foreign Key Columns

ShipperID

With this, we have finalized the preparation of our data source. Now we will design our window.

Designing our Window

We will design our window. It consists of a grid divided into two rows and two columns. We will use the first row as the title and the second row will have the content. In the first column of the second row, we will insert a datagrid that displays the customer's orders, and in the second column, we will insert controls that display the currently selected order details:

WPFDataBinding7.PNG

The XAML code for the grid is this:

XML
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="30"></RowDefinition>
        <RowDefinition Height="*"></RowDefinition>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="75*"></ColumnDefinition>
        <ColumnDefinition Width="25*"></ColumnDefinition>
    </Grid.ColumnDefinitions>
</Grid>

It's simple, simply stating the definition of rows and columns, with variable or fixed size.

Now we will insert the title of the DataGrid and the DataGrid itself, and specify the column and row it belongs to:

XML
<ContentPresenter Grid.Row="0" Grid.Column="0" 
    VerticalAlignment="Center" Content="Data Grid."/>
<DataGrid Name="grdData" Grid.Row="1" Grid.Column="0" 
    Margin="5" VerticalAlignment="Stretch" 
    HorizontalAlignment="Stretch" AutoGenerateColumns="False" 
    IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}">
</DataGrid>

As you can see, in my case, I opted not to define the columns automatically (AutoGenerateColumns = "false"). To define the binding of the columns I want to show, in each of the two controls, I specify the attached properties Grid.Column and Grid.Row to place the control where it belongs. In the DataGrid, I also set the property IsSyncronizedWithCurrentItem to true. This property is very important as this is what will enable the other controls to use the currently selected data.

Within the DataGrid, we must specify the columns we want to show. We include the following XAML code into the DataGrid:

XML
<DataGrid.Columns>
    <DataGridTextColumn Header="Order Code" 
       Binding="{Binding Path=OrderID}" MinWidth="80"/>
    <DataGridTextColumn Header="Customer" 
        Binding="{Binding Path=CustomersOrders/CompanyName}" 
        MinWidth="200"/>
    <DataGridTextColumn Header="Order Date" 
        Binding="{Binding Path=OrderDate}" MinWidth="100"/>
    <DataGridTextColumn Header="Shipper" 
        Binding="{Binding Path=ShippersOrders/CompanyName}" 
        MinWidth="200"/>
</DataGrid.Columns>

And here is where we begin to benefit from the WPF data binding. As you can see, each column has a Binding property. This property points to the field you want to show. The first column (Order Code) aims directly at the OrderID field in the Orders table, which will be our main DataContext. It is the same in Order Date, but you will see that Customers and Shippers are different. This is because, using the previously created relations, we are heading our column to the child table and choosing a field of it to show. We don't have to worry about specific queries with joins. We simply use the relationships between the tables to display the fields we need.

Now we create the window part to show the details of the selected order:

XML
<ContentPresenter Grid.Row="0" Grid.Column="1" 
        VerticalAlignment="Center" Content="Extra Information"/>

<StackPanel Grid.Row="1" Grid.Column="1" Margin="5">
    <ContentPresenter Content="Order Details:"></ContentPresenter>
    <ListBox Name="lstDetallePedidos" Height="125"
             ItemsSource="{Binding Path=OrderDetails}"
             DisplayMemberPath="ProductsOrderDetails/ProductName"
             IsSynchronizedWithCurrentItem="True" >
    </ListBox>

    <ContentPresenter Content="Customer Code:"></ContentPresenter>
    <TextBox Name="txtCustomerCode" 
          Text="{Binding Path=CustomerID}"></TextBox>
    <ContentPresenter Content="Company:"></ContentPresenter>
    <TextBox Name="txtCompany" 
          Text="{Binding Path=CustomersOrders/CompanyName}"></TextBox>
    <ContentPresenter Content="Contact person:"></ContentPresenter>
    <TextBox Name="txtContact" 
          Text="{Binding Path=CustomersOrders/ContactName}"></TextBox>
    <ContentPresenter Content="Contact phone:"></ContentPresenter>
    <TextBox Name="txtPhone" 
          Text="{Binding Path=CustomersOrders/Phone}"></TextBox>
</StackPanel>

You see that this in itself is very simple. We used a ContentPresenter in column 1 row 0 to define the title, and then in row 1 column 1, we introduce a StackPanel, since we want a simple layout of elements. As in the columns of the DataGrid, here we bind the Text property of each TextBox to the field in the Orders table we want to show and use the relationships to display the fields from other tables.

The control that is slightly different is the ListBox, since in this, we establish two properties for data binding. In ItemsSource, we establish the relationship OrderDetails. This way, we obtain many items as the relationship between the Orders table and OrderDetails (i.e., if an order contains two OrderDetails, we get those two items). But, to set the property DisplayMemberPath, we use a second relationship ProductsOrderDetails to bind to the product table and the ProductName field. As we are not interested in showing the product ID selected, which is on the table OrderDetails, we want the name found in the Products table. So, we use the relationship ProductsOrderDetails to move to the Products table for each item of the listbox.

And with this, we have finished designing and preparing our window. Now we only need to write code that makes all this work, and I say only, because it is really simple and easy.

Code Writing

Well, first we have to open the file associated with our XAML code and declare the private members that contain our data and the instances of the TableAdapter that you use to fill it:

VB
'Private Objects definition.
Private dsDatos As New NwindDataSet
Private custAdap As New NwindDataSetTableAdapters.CustomersTableAdapter
Private ordeAdap As New NwindDataSetTableAdapters.OrdersTableAdapter
Private detailAdap As New NwindDataSetTableAdapters.Order_DetailsTableAdapter
Private shipAdap As New NwindDataSetTableAdapters.ShippersTableAdapter
Private prodAdap As New NwindDataSetTableAdapters.ProductsTableAdapter

We define our Dataset dsDatos and the TableAdapters that we need to populate the tables Customers, Orders, Order_Details, Shippers, and Products.

Now in the constructor of our window, we will load the data and set the data source of our controls:

VB
Public Sub New()
    ' This call is required by the designer.
    InitializeComponent()

    ' Add any initialization after the InitializeComponent() call.
    ' Fill Tables.
    custAdap.Fill(dsDatos.Customers)
    detailAdap.Fill(dsDatos.Order_Details)
    shipAdap.Fill(dsDatos.Shippers)
    prodAdap.Fill(dsDatos.Products)
    ordeAdap.Fill(dsDatos.Orders)

    ' Stablish Orders Table as our Window DataContext
    Me.DataContext = dsDatos.Orders
End Sub

This code is not complicated. After the call to InitializeComponent (created automatically by Visual Studio when create the Sub New of the window), we simply use our TableAdapters to fill the tables in our DataSet dsDatos.

The most important line of code of our application is the last:

VB
Me.DataContext = dsDatos.Orders

This line sets the data context of our window to the table Orders, which will get all the relationships and the required fields. Here is where the magic of data binding in WPF occurs. In XAML code, each control searches it data source in its parent controls and gets it at the window. This way, all controls that do not specify a datasource share the same source, and with the use of the IsSynchronizedWithCurrentItem property, if you change the selected item in the Order table, all controls reflect the change automatically.

And ... there is no more code. This is all the code necessary for the window to work perfectly. All you need is to run the application to see the result.

History

  • 7 May 2010 - Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Freelance Developer
Spain Spain
MVP Windows Platform Development 2014
MVP Windows Phone Development 2013
MVP Windows Phone Development 2012

Comments and Discussions

 
QuestionThanks! Pin
John DMC2-Jan-15 8:30
John DMC2-Jan-15 8:30 
GeneralMy vote of 1 Pin
Yahia Alhami9-May-10 3:02
Yahia Alhami9-May-10 3:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.