Tag Archives: data binding

DataGrids in WPF

In the last post, we saw how to connect a MySQL database to a WPF application, and how to bind a ComboBox to the DataTable containing the data from the database. In most applications, however, we’d like to view more than one record from the database at a time, and for that, the DataGrid is the ideal control. In the last post, we showed the UI containing a DataGrid for displaying the issues of a particular title of comic book. Here we’ll have a look at how this is achieved.

The process is similar to that used for binding a ComboBox to a DataTable, but a DataGrid is more versatile so there are a few tricks we need to get it to work properly. First, we need to get the data from the MySQL database, and we use the same technique as in the previous post. We create an ObjectDataProvider in the XAML:

    <ObjectDataProvider x:Key="BooksTable"
    ObjectType="{x:Type local:DatabaseTable}"
    MethodName="GetTable">
      <ObjectDataProvider.MethodParameters>
        <s:String>SELECT * FROM issues</s:String>
        <s:String>IssueYear, IssueMonth, IssueDay</s:String>
      </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>

In this case, we load all the columns from the ‘issues’ table in the database. The second string passed to the GetTable() method shows how we sort the data in the DataTable: first by IssueYear, then by IssueMonth and finally by IssueDay.

Now we need to see how to define a DataGrid that is bound to the DataTable and displays the columns we want. We have:

      <TabControl>
        <TabItem Header="Books" Margin="0"  DataContext="{StaticResource BooksTable}">
<!-- Code setting up properties of TabItem... -->
            <DataGrid ItemsSource="{Binding Mode=OneWay}" AutoGenerateColumns="False" IsSynchronizedWithCurrentItem="True"
                    x:Name="booksDataGrid" Margin="0,0,0,0" d:LayoutOverrides="Height" Grid.Row="2" Grid.ColumnSpan="2" HorizontalAlignment="Center">
              <DataGrid.Columns>
                <DataGridTextColumn Header="Vol" Width="Auto" Binding="{Binding Volume}"/>
                <DataGridTextColumn Header="#" Width="Auto" Binding="{Binding Number}"/>
                <DataGridTextColumn Header="Day" Width="Auto" Binding="{Binding IssueDay}"/>
                <DataGridTextColumn Header="Month" Width="Auto" Binding="{Binding IssueMonth}"/>
                <DataGridTextColumn Header="Year" Binding="{Binding IssueYear}"/>
                <DataGridTextColumn Header="Title" Binding="{Binding IssueTitle}" MaxWidth="400" Width="400"/>
                <DataGridHyperlinkColumn  Header="ComicVine" Binding="{Binding ComicVine}"
                                        ContentBinding="{Binding ComicVine, Converter={StaticResource ComicVineConverter}}"/>
              </DataGrid.Columns>
            </DataGrid>
        </TabItem>
      </TabControl>

We’ve shown the enclosing TabControl and TabItem since we set the data context of the TabItem to be the BooksTable StaticResource we created above. This is the default data context for all controls within this particular TabItem, so when we bind the DataGrid on line 4, the binding is to BooksTable.

The default behaviour of a DataGrid is to automatically generate a column for each column in the DataTable that it is displaying. We don’t want this to happen here, since we don’t want Key_Issues (the primary key for the issues table)  to be displayed. We therefore set AutoGenerateColumns to False.

The IsSynchronizedWithCurrentItem property determines whether the currently selected row in the DataGrid is synchronized with the CurrentItem in the collection view of the data source. Remember that WPF automatically inserts a collection view between a data source and the control which is bound to that source. This collection view maintains a CurrentItem marker that points to the currently selected item, and we want that item also to be selected in the DataGrid.

The other properties in the DataGrid deal mostly with its appearance, so we can move along to looking at the DataGrid’s column definitions. There are several pre-defined types of column that we can use. This application uses three of them: the DataGridTextColumn, which displays a text box in each cell in the grid, a DataGridHyperlinkColumn, which displays text as a hyperlink, and a DataGridComboBoxColumn, where each cell appears as text unless it is selected, in which case it turns into a ComboBox containing a fixed list of items (the DataGridComboBoxColumn is used in another part of the program, so we won’t examine it here). It is also possible to create your own custom type of DataGrid column, but we won’t need that here. The Books DataGrid displays all its columns as text, except for the ComicVine column, which is displayed as a hyperlink so the user can open the ComicVine web page for that comic book issue.

Each of the DataGridTextColumns is bound to one of the columns in the DataTable that is the data context. (The ComicVine column has an extra ContentBinding that we’ll get to later.)

As it stands, when the program starts, the DataGrid displays all the issues in the database, sorted by their dates. This isn’t terribly useful, so we need to arrange things so that when the user selects a title from the ComboBox, the DataGrid displays only those issues from that title. To do this, we add an event handler to the booksTitlesComboBox (the ComboBox we dealt with in the last post) to handle the SelectionChanged event. It looks like this:

    bool changingTitle = false;  // Prevent row changed events when changing title
    private void booksTitlesComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
      changingTitle = true;
      if (booksTitlesComboBox.SelectedValue != null)
      {
        DataTable booksTable = (DataTable)((DataSourceProvider)FindResource("BooksTable")).Data;
        int titleNumber = (int)booksTitlesComboBox.SelectedValue;
        databaseTable.GetTable("SELECT * FROM issues WHERE Title=" + titleNumber, booksTable);
      }
      changingTitle = false;
    }

The ‘changingTitle’ variable will be needed a bit later, so for now just ignore it. What we want to do is update the booksTable DataTable so that it contains only those issues corresponding to the currently selected title. We must first retrieve booksTable in the C# code, since it was defined in the XAML. This is done on line 7, where we used FindResource() to retrieve a resource with a given name. We need to cast this to a DataSourceProvider, and then extract its Data property, which we must then cast to a DataTable (complicated, but once we’ve done it, it’s the same format for retrieving any resource).

We then get the SelectedValue from booksTitleComboBox (remember that the SelectedValue is a Key_Titles value, not the textual Title; this is what we need for looking up issues based on their title).  On line 9, we construct a SQL query to select issues for that title. This uses a different version of the GetTable() method, which takes an existing DataTable and changes its contents. It looks like this:

    public void GetTable(String query, DataTable refreshDataTable)
    {
      refreshDataTable.Clear();
      String connString = "server=localhost;uid=root;pwd=xxxx;database=comics;";
      connection = new MySqlConnection(connString);
      adapter = new MySqlDataAdapter(query, connection);
      adapter.Fill(refreshDataTable);
    }

The current contents of the DataTable are cleared and then the table is refreshed by filling it using the MySqlDataAdapter with the revised query. Note that we don’t create a new DataTable object, so the Sort property we added when the table was created is still there. This means that the new data loaded into the table will still be sorted according to the year, month and day.

That’s all we need to do to refresh the DataGrid, since the DataGrid is bound to booksTable. Any changes to booksTable automatically are reflected in the DataGrid, and vice versa.

Editing data

By default, the cells in a DataGrid are editable, so to change the data for a particular issue, all we need to do is select the cell by double-clicking on it, and then type in the new text. Due to the data binding between the DataGrid and DataTable, these changes are automatically transmitted to the DataTable. However, these changes will not be passed on to the original MySQL database unless we write some code to make this happen. This is one place where using a MySQL database requires more hand-crafted code than using a DataSet for interacting with one of Microsoft’s databases such as SQL Server or Access. MySQL requires us to define a specific command for updating (or inserting and deleting) a record, so we need to see how to do this.

Since we want any change to the booksTable DataTable to be saved back to the MySQL database, we can add a handler for the RowChanged event in booksTable. We can do this by adding this line to the MainWindow() constructor:

      booksTable.RowChanged += new DataRowChangeEventHandler(booksTable_RowChanged);

The handler method is:

    void booksTable_RowChanged(object sender, DataRowChangeEventArgs e)
    {
      if (!changingTitle)
      {
        UpdateDBIssues();
      }
    }

Remember the ‘changingTitle’ variable we declared earlier? We need to call the UpdateDBIssues() method (which writes updates back to the MySQL database – we’ll look at it in a minute) only when the user actually makes changes to rows in the DataGrid by editing them. It turns out that when we change the title being viewed in the DataGrid, every row that gets inserted into booksTable by our call to GetTable() generates a RowChanged event, and if we called UpdateDBIssues() when that happened, we would end up duplicating all the records in the database. The ‘changingTitle’ parameter is a bit of a kludge, but it ensures that we update the database only when genuine changes to existing records have been made.

Now we need to see how to update the database. We have:

    private void UpdateDBIssues()
    {
      MySqlConnection connection = new MySqlConnection("server=localhost;uid=root;pwd=xxxx;database=comics;");
      string updateString = "UPDATE issues SET IssueTitle=?IssueTitle, Title=?Title, Volume=?Volume, Number=?Number, " +
        "IssueDay=?IssueDay, IssueMonth=?IssueMonth, IssueYear=?IssueYear, ComicVine=?ComicVine WHERE " +
        "Key_Issues=?oldKey_Issues";
      MySqlCommand updateCommand = new MySqlCommand(updateString, connection);
      updateCommand.Parameters.Add("?IssueTitle", MySqlDbType.VarChar, 100, "IssueTitle");
      updateCommand.Parameters.Add("?Title", MySqlDbType.Int32, 10, "Title");
      updateCommand.Parameters.Add("?Volume", MySqlDbType.Int32, 10, "Volume");
      updateCommand.Parameters.Add("?Number", MySqlDbType.VarChar, 10, "Number");
      updateCommand.Parameters.Add("?IssueDay", MySqlDbType.Int32, 10, "IssueDay");
      updateCommand.Parameters.Add("?IssueMonth", MySqlDbType.Int32, 10, "IssueMonth");
      updateCommand.Parameters.Add("?IssueYear", MySqlDbType.Int32, 10, "IssueYear");
      updateCommand.Parameters.Add("?ComicVine", MySqlDbType.VarChar, 100, "ComicVine");
      MySqlParameter parameter = updateCommand.Parameters.Add("?oldKey_Issues", MySqlDbType.Int32, 10, "Key_Issues");
      parameter.SourceVersion = DataRowVersion.Original;
      MySqlDataAdapter adapter = new MySqlDataAdapter();
      adapter.UpdateCommand = updateCommand;

      string insertString = "INSERT INTO issues (Key_Issues, IssueTitle, Title, Volume, Number, IssueDay, IssueMonth, IssueYear, ComicVine) " +
        "VALUES (?Key_Issues, ?IssueTitle, " + booksTitlesComboBox.SelectedValue + ", ?Volume, ?Number, ?IssueDay, ?IssueMonth, ?IssueYear, ?ComicVine)";
      MySqlCommand insertCommand = new MySqlCommand(insertString, connection);
      insertCommand.Parameters.Add("?Key_Issues", MySqlDbType.Int32, 10, "Key_Issues");
      insertCommand.Parameters.Add("?IssueTitle", MySqlDbType.VarChar, 100, "IssueTitle");
      insertCommand.Parameters.Add("?Volume", MySqlDbType.Int32, 10, "Volume");
      insertCommand.Parameters.Add("?Number", MySqlDbType.VarChar, 10, "Number");
      insertCommand.Parameters.Add("?IssueDay", MySqlDbType.Int32, 10, "IssueDay");
      insertCommand.Parameters.Add("?IssueMonth", MySqlDbType.Int32, 10, "IssueMonth");
      insertCommand.Parameters.Add("?IssueYear", MySqlDbType.Int32, 10, "IssueYear");
      insertCommand.Parameters.Add("?ComicVine", MySqlDbType.VarChar, 100, "ComicVine");
      adapter.InsertCommand = insertCommand;

      MySqlCommand deleteCommand = new MySqlCommand("DELETE FROM issues WHERE Key_Issues=?Key_Issues", connection);
      MySqlParameter delParameter = deleteCommand.Parameters.Add("?Key_Issues", MySqlDbType.Int32, 10, "Key_Issues");
      delParameter.SourceVersion = DataRowVersion.Original;
      adapter.DeleteCommand = deleteCommand;

      DataTable booksTable = (DataTable)((DataSourceProvider)FindResource("BooksTable")).Data;
      adapter.Update(booksTable);
    }

This method looks complicated, but it’s quite straightforward. We’ve included the code for inserting and deleting records as well, since we’ll be doing that shortly. Consider the update code. Lines 4 to 6 build the SQL for updating a record from the issues table. We need to add a parameter for each column we wish to update, and the names prefixed by a ? are place-holders for values that the command will read from booksTable for each row that has changed. The arguments to a call to Add() are: the place-holder name, such as ?IssueTitle, the data type of this parameter and the size of this data type (these can be read off the original definition in the database), and the name of the column to update.

The one special column is the Key_Issues column. This primary key is the one used to determine which record to update (it’s in the WHERE clause in the SQL). We determine which row to update by looking up the Key_Issues value. Line 16 adds this parameter to the command, and line 17 specifies that the value of Key_Issues is taken from the original version of the table (before the modification).

With the command and all its parameters built, we attach the command as the UpdateCommand property of the adapter on line 19. Similar commands are built for inserting and deleting rows on lines 21 to 37. Finally line 39 retrieves booksTable (the table containing the changed rows) and line 40 calls the Update() command, which is what actually writes the changes back to the MySQL database.

Deleting a row from the DataGrid is done by selecting the row and then pressing the Delete key.  By adding a handler for booksTable’s RowDeleted event which calls UpdateDBIssues(), the rows are also deleted from the original database.

Finally, we consider adding a new issue. This is most easily done by scrolling down to the last row in the DataGrid and typing in the new data, then hitting return. This adds a new row to booksTable, and since adding a row counts as a RowChanged event, UpdateDBIssues() is called and the ‘insert’ command adds the row to the database.

There’s one slight problem though. When we enter the data for a new row, we don’t specify the Key_Issues primary key. Ideally we would like this to be generated automatically. Fortunately, a DataTable has an AutoIncrement property for its columns. There’s one snag though: by default, AutoIncrement starts by assigning a value of 0 to the column and increments from there. In the MySQL database, of course, we will usually have a number of records already present, so the primary key Key_Issues will already be full up to some non-zero value. We therefore need to set the DataTable’s starting value equal to the maximum value of the column from the database. We can do that as follows. First, write a little method that gets the maximum value of a column:

    private int GetMaxKey(string column, string table)
    {
      string query = "SELECT max(" + column + ") FROM " + table + ";";
      MySqlConnection connection = new MySqlConnection("server=localhost;uid=root;pwd=xxxx;database=comics;");
      MySqlCommand maxCommand = new MySqlCommand(query, connection);
      maxCommand.Connection.Open();
      int maxKey = (int)maxCommand.ExecuteScalar();
      maxCommand.Connection.Close();
      return maxKey;
    }

This method uses SQL’s max() function to retrieve the maximum value of the given column from the given table. We use the ExecuteScalar() function to retrieve this single value. ExecuteScalar() returns the first element from a SQL query only, discarding the rest, and that’s just what we want here.

We use this value to set up the DataTable’s AutoIncrement property by inserting the following in MainWindow’s constructor:

      DataTable booksTable = (DataTable)((DataSourceProvider)FindResource("BooksTable")).Data;
      booksTable.Columns["Key_Issues"].AutoIncrement = true;
      booksTable.Columns["Key_Issues"].AutoIncrementSeed = GetMaxKey("Key_Issues", "issues") + 1;

This sets the AutoIncrement to start at one higher than the current maximum value, which is just what we need to synchronize the DataTable with the database. It is important that the primary key Key_Issues is not set to auto-increment in the MySQL database. The reason is that MySQL keeps track of the highest primary key value that has ever been used for a given table, which the Max() function returns the highest existing value for Key_Issues. If you’ve added some records and then deleted them, these two values will not be the same. By assigning the auto-increment to the DataTable only, we ensure that the same primary key is used both in the program and in the database.

By the way, even though it is easy enough to delete entries from the DataGrid by selecting them and pressing the delete key, it is sometimes handy to be able to do this in code. It took ages to discover how to do this, but I have a little method that does seem to work so I’ll throw it in here.

Suppose we add a button to the Books tab which will delete all selected rows when pressed. The event handler for this button will look like this:

    private void deleteButton_Click(object sender, RoutedEventArgs e)
    {
      if (booksDataGrid.SelectedItems.Count == 0) return;
      IList items = booksDataGrid.SelectedItems;
      IEnumerable<DataRowView> toDelete = items.Cast<DataRowView>();
      try
      {
        List<DataRowView> rows = new List<DataRowView>(toDelete);
        foreach (DataRowView row in rows)
        {
          row.Row.Delete();
        }
      }
      catch (InvalidCastException)
      {
        return;
      }
    }

On line 3, we check to make sure that at least one row is selected. Line 4 extracts a list of selected items from the DataGrid. This list is returned as a list that implements the IList interface, which is simply a list that can contain any data type, so at the moment the elements in this list are simply of the ‘object’ data type. Line 5 casts this list into an IEnumerable type whose elements are of type DataRowView (the view of a DataRow within a DataTable). The syntax in line 5 makes use of the Enumerable.Cast method, which is part of the System.Linq namespace, so you’ll need a ‘using System.Linq’ line at the top of your code file.

So far, so good, but there is a catch here. Since we want to delete elements from the DataTable (which, through data binding, will also delete rows from the DataGrid), we need to do it in such a way that we don’t disrupt the IEnumerable object. If we tried to delete objects directly from this object, we are trying to modify the collection while iterating over it, which will throw an exception. To get around this, we need to make a copy of the collection into an ordinary List, and use that for the iteration. That’s what we do on lines 8 to 12.

The try-catch block is there because after all the selected rows have been deleted, the DataGrid will position the selected row on the empty row at the bottom of the DataGrid if there are no other rows in the grid. This row has no associated entry in the DataTable, so attempting to access it throws an exception. There doesn’t seem to be any elegant way of detecting whether an row in the DataGrid has a partner in the DataTable, so it seems easiest to just catch the exception and ignore the error.

That completes the code for displaying a DataGrid bound to a DataTable, and allowing the user to edit, add and delete records from the database by editing the DataGrid. One final topic needs to be covered to complete the setup of this DataGrid, and that’s the configuration of the hyperlink column to display the links to the ComicVine website. We’ll have a look at that in the next post.

Using MySQL with WPF

MySQL is a popular free database which many (including me) prefer for writing database applications. Being relatively new to WPF, I wanted to see how MySQL could be used in a WPF application. This proved to be a fairly long journey, so we’ll probably need several posts to get through it all.

First, if you’re new to MySQL, you’ll need to download and install it. I won’t go into great detail here, apart from pointing you in the direction of the main download page for the free version (there are commercial versions that cost real money as well). After installing MySQL itself, you’ll need to install the MySQL ADO.NET connector in order to use it in WPF programming. This is currently located here, but if in the future this link is dead, just do a search for MySQL and ADO.NET.

With these two packages installed you should be ready to start writing code in Visual Studio (VS). The program I’ll discuss is a front end to an existing database, which allows you to insert, modify and delete records in the database. In homage to Sheldon Cooper, the database will contain details of my comic book collection. I’ll assume that you understand the basics of database construction, including the creation of tables and insertion of records into these tables. I’ll also assume you know the rudiments of SQL, since I’ll be using it to construct a few commands to be sent to the database. If you don’t know SQL very well (or at all) it shouldn’t hamper you too much since you can probably figure out what the commands are doing (SQL is fairly transparent at this level).

First, we’ll need to consider the structure of the database itself. It contains three tables, which contain the following fields:

Publishers (publishers of comic books)

  • Key_Publishers (the primary key)
  • Name (the name of the publisher)

Titles (titles of comic book series, not of individual issues)

  • Key_Titles (the primary key)
  • Title (the title’s name, such as Action Comics)
  • Publisher (an int giving the key of the publisher of this title)

Issues (individual issues of given titles)

  • Key_Issues (the primary key)
  • IssueTitle (the title of the individual issue)
  • Title (an int linking to the key in the Titles table)
  • Volume
  • Number
  • IssueDay
  • IssueMonth
  • IssueYear
  • ComicVine (the URL of the page on http://www.comicvine.com giving details about this issue)

The UI of the program consists of a tab control with three tabs: one for editing the issues, one for the titles and one for the publishers. We’ll consider the issues one first. It looks like this:

At the top is a ComboBox from which we can choose the title. Beneath this is a DataGrid in which we display the individual records from the data base for that title. At the bottom are a few boxes in which individual data for the selected issue are displayed. The user can edit the information either directly in the DataGrid, or by editing the boxes at the bottom.  The info in the boxes is identical to that displayed in the DataGrid except for the ComicVine link, which is shown as a URL in the text box, but as a hyperlink with the label ‘ComicVine’ in the DataGrid. This is done because the URL is usually far too long to be displayed conveniently in the grid, as you can see. The ‘Add’ button allows the user to add a new issue.

Clearly there’s a lot going on here, so we’ll need to take things step by step. To begin, let’s see how we can get the ComboBox to display the list of titles. (I’ll assume you can create the basic UI using Expression Blend (EB) or VS so I won’t go into that here.)

First, we need to let our VS project know we’ll be using MySQL. If you’ve installed the connecter mentioned above, you still need to include MySQL as a reference in your VS project. To do this, right-click on References in Solution Explorer and then click on the .NET tab. You may need to wait a few seconds while this list finishes loading, but eventually you can find MySQL.Data in the list, so you should add that. Now we can start writing some code.

Most of the linkage between controls and the MySQL database is done via data binding, which we’ve already looked at for simpler cases (see the index for a list of pages). Before we dive into the code it’s essential that you understand the structure of the program.

Ultimately the data are stored in the MySQL database, but the program itself doesn’t use this for the displays. First, the data must be loaded from the MySQL database into an internal data structure, which is then used for the main working of the program. Changes made by the user to the data using the UI affect only this internal representation of the data. If we want these changes to be made permanent, we need to write the code that will save these changes back to the original MySQL database.

WPF provides several internal data structures useful for storing information from a database. In fact, VS provides an automated way of creating a DataSet from database servers it has access to. Although you can create a DataSet from a MySQL server, the associated functionality provided by VS doesn’t seem to work with MySQL (at least I couldn’t find any way of making it work – there may be something that I haven’t discovered), so we need to write our own code to handle changes to the MySQL database. This isn’t that hard to do as we’ll see, but we’re getting ahead of ourselves.

Let’s see how we can access the data required to populate the ComboBox containing the list of Titles. From the above structure of the database, we see that the information is stored in the Titles table, so we need to load that data into the program. We’ve provided a separate class that handles this, and here’s the relevant code:

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using MySql.Data.MySqlClient;
//.....
class DatabaseTable
{
    MySqlConnection connection;
    MySqlDataAdapter adapter;
    public DataTable GetTable(String query, String sortBy)
    {
      String connString = "server=localhost;uid=root;pwd=xxxx;database=comics;";
      connection = new MySqlConnection(connString);
      adapter = new MySqlDataAdapter(query, connection);
      DataTable dataTable = new DataTable();
      adapter.Fill(dataTable);
      dataTable.DefaultView.Sort = sortBy;
      return dataTable;
    }
}

Note the ‘using’ statement that specifies the MySql library.

To get at data in a MySQL database we first need to connect to it, and that’s what the MySqlConnection is for. Its constructor requires a string giving the information needed to connect. In this case, we specify the server as ‘localhost’ which means it’s on the local machine. If you’re connecting to a remote server, you’d give the URL instead. The userID (uid) and password (pwd) are then given, followed by the name of the database.

Next, we need to create a MySqlDataAdapter, which is what does the actual work of retrieving the information from the database and storing it in the local object. In order for the adapter to get some data, it needs to know what to look for, and that’s what the ‘query’ string is for. This is an SQL command, which we’ll get to in a minute.

The adapter has to be given an object in which to store the data it retrieves. The standard data structure is the DataTable (in System.Data), which is essentially a single table of data. The Fill() method of MySqlDataAdapter executes the query on the MySQL database and loads the result in the DataTable. The DataTable will have columns with the same names as those in the original database, and it is these column names that can be used in data binding later on.

The last thing we do is define a sorting condition. Remember that whenever we bind a control to a data source such as a list, a collection view is automatically created and inserted between the data source and the control. This also applies to data binding between a control such as a ComboBox and a DataTable. The ComboBox displays the data as given by the collection view, so if we wish to sort the data as displayed in the UI, we add a sort command to the view. The data in the original DataTable is unchanged; all that changes is the way that data appears on screen.

In the case of a DataTable, we access its DefaultView as shown in the code, and then attach a Sort command to the view. The Sort is simply a string giving the sorting command we want. If we wish to sort the list of Titles, for example, we would specify ‘sortBy’ as “Title”, which is the name of the column we want to use as the sort key. (More complex sorting can be done too. We’ll see this when we consider the DataGrid showing the issues.)

There’s an important point here. Those of you familiar with SQL will know that we can include a sorting command as part of an SQL query, so you might be wondering why we didn’t just include the sorting command as part of the ‘query’ we sent to the database. The reason is that doing it this way would ask the MySQL database to do the sorting, so that the data used to populate the DataTable would be correctly sorted when it first arrives. However, if we then add extra rows to the DataTable by using our program, these rows would merely be tacked onto the end of the DataTable and would not be correctly sorted, since the DataTable hasn’t been told to sort its data. By putting the sort command into the DataTable rather that in the original SQL command, we ensure that the data as displayed in the program are always correctly sorted.

OK, we now have our internal DataTable, so how do we use this to populate the ComboBox? Fortunately, we’ve already considered data binding with a ComboBox, so we can model our code along that example. We’ll do things a little differently here, however, to illustrate a common technique for providing data sources in data binding.

We saw above that we provided the code for getting the DataTable in a class called DatabaseTable. We can access the DataTable by declaring an ObjectDataProvider in the XAML part of the code. In the Window.Resources section of the XAML, we can write:

<Window
  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"
  mc:Ignorable="d"
  x:Class="Comics.MainWindow"
  x:Name="Window"
  xmlns:local="clr-namespace:Comics"
  xmlns:s="clr-namespace:System;assembly=mscorlib"
  Title="Comics"
  Width="680" Height="814">

  <Window.Resources>
    <ObjectDataProvider x:Key="TitlesTable"
    ObjectType="{x:Type local:DatabaseTable}"
    MethodName="GetTable">
      <ObjectDataProvider.MethodParameters>
        <s:String>SELECT * FROM titles</s:String>
        <s:String>Title</s:String>
      </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
  </Window.Resources>
  <!-- Code for UI -->
</Window>

This creates a resource with the name given as the Key (TitlesTable) by creating an object of the given type (DatabaseTable) and calling the indicated method (GetTable). The parameters passed to the method are given next, and in this case are two strings. The first string is the query to be sent to the MySQL database, and the second string is the Sort command, so that the DataTable sorts the records by Title.

Note that you’ll need to ensure that the various prefixes used in this XAML code are defined. Typically most of them are defined for you when you create a project in VS, but there are a couple that you’ll probably need to define yourself. Here, the ‘local’ namespace is the namespace used for the code in the project (Comics here). The ‘s’ namespace is required to define the String data type. (This is one reason why I hate XAML: although it is more compact in some cases, there is so much finicky stuff that it can be hard to get it right.)

We’re almost there. The last thing we’ll look at in this post is how to define the ComboBox so it gets its data from the ObjectDataProvider we just defined. The relevant line is:

            <ComboBox x:Name="booksTitlesComboBox" ItemsSource="{Binding Source={StaticResource TitlesTable}}" DisplayMemberPath="Title"
                    SelectedValuePath="Key_Titles" VerticalAlignment="Center" HorizontalContentAlignment="Stretch" Grid.Row="1" Grid.Column="1"
                    SelectionChanged="booksTitlesComboBox_SelectionChanged" FontWeight="Bold"/>

There’s a bit of stuff in here that we’ll get to later, but the relevant bits are:

  • ItemsSource is bound to the StaticResource TitlesTable that we just defined. This means that the items displayed in the ComboBox are bound to the items in that DataTable.
  • DisplayMemberPath is specified as ‘Title’. Since the DataTable contains more than one (two, actually) columns, we need to tell the ComboBox which column to display. The ‘Title’ is the textual title which the user can read.
  • SelectedValuePath: internally, we use the Key_Titles column for connecting a title with an issue (as we saw in the database tables above), so when the user selects an item from the ComboBox, we need to know the Key_Titles value in order to be able to retrieve the issues for that Title. More on this later when see how to populate the DataGrid.
  • SelectionChanged: when the user selects an item in the ComboBox, we need to update the display in the DataGrid to display all issues with that Title, so we provide an event handler to do this. Again, we’ll see how this works in a later post.

The program at this stage should display the list of titles in the ComboBox, although selecting an item won’t do anything yet. But that’s enough for one post, so we’ll continue the story in the next post.

Data binding with a ListBox and ComboBox in WPF

In the last post, we saw how to use data binding to a List to allow stepping through the elements of the List, displaying each item in separate TextBoxes. We also saw how a collection view provides an interface to the underlying List, and allows moving around the List as well as other operations like sorting and filtering.

However, when dealing with a list of objects, usually we would like a view that allows us to see several items in the list at the same time, and allows us to scroll through this display and select which item we want to deal with. To that end, we’ll have a look at how to bind a List to a ListBox and a ComboBox in WPF.

The program we use as illustration is an expanded version of the one from the previous post. The interface of the new program looks like this:

As before, we are looking at a list of books. Each book has an author, title, price and subject category. The available categories are shown in the ComboBox, while the titles of the books are displayed in a ListBox just above the buttons. Each category is represented by a unique integer code (so ‘physics’ has the code 2 in the picture). It is this numerical code that is stored in each Book object, so the textual representation of a given category is contained in a separate class called BookCategory. (Yes, I realize this example would be better done using a database, but for the purposes of illustrating data binding to lists, it serves its purpose.)

The buttons have the same functions as in the previous post (the First, Previous, Next and Last buttons move through the list of books, Add adds a new book to the list, Sort sorts the books by price and Cheap selects books with a price under 20.00. Nothing new here.

We’ve used data binding to keep the various parts of the interface synchronized. As we step through the list using one of the navigation buttons, the displays in the TextBoxes and ComboBox update to display the data in the current book, and the highlighted line in the ListBox also keeps in step with out current selection. If we select a book by clicking on a line in the ListBox, the rest of the display shows the data for that book. If we select a new category in the ComboBox, the Code box will update as well, and vice versa. (We haven’t put in any validation, so if you enter an invalid Code number, you won’t get an error message.)

The interface was built in Expression Blend as usual, so if you want to see the XAML for that, download the code (link at the end of the post) and have a look. We’ll look here at the code (all in C#) that does the binding between the lists and controls.

First, we’ll look at the classes that store the data that will be inserted into the lists. The Book class is slightly expanded from the previous post in order to accommodate the category code:

  class Book : INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;
    protected void Notify(string propName)
    {
      if (this.PropertyChanged != null)
      {
        PropertyChanged(this, new PropertyChangedEventArgs(propName));
      }
    }

    string author;

    public string Author
    {
      get { return author; }
      set
      {
        if (author.Equals(value)) { return; }
        author = value;
        Notify("Author");
      }
    }

    string title;

    public string Title
    {
      get { return title; }
      set { if (title.Equals(value)) { return; }
        title = value;
        Notify("Title");
      }
    }

    decimal price;

    public decimal Price
    {
      get { return price; }
      set { if (price == value) { return; }
        price = value;
        Notify("Price");
      }
    }

    int categoryCode;

    public int CategoryCode
    {
      get { return categoryCode; }
      set { if (categoryCode == value) { return; }
        categoryCode = value;
        Notify("CategoryCode");
      }
    }

    public Book() { }
    public Book(string author, string title, decimal price, int code)
    {
      this.author = author;
      this.title = title;
      this.price = price;
      this.categoryCode = code;
    }
  }

Note, by the way, that the string returned in the Notify() call must match the name of the property in the code.

Next, we look at the BookCategory class:

  class BookCategory : INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;
    protected void Notify(string propName)
    {
      if (this.PropertyChanged != null)
      {
        PropertyChanged(this, new PropertyChangedEventArgs(propName));
      }
    }

    int code;

    public int Code
    {
      get { return code; }
      set
      {
        code = value;
        Notify("Code");
      }
    }

    string category;

    public string Category
    {
      get { return category; }
      set
      {
        category = value;
        Notify("Category");
      }
    }

    public BookCategory(int code, string category)
    {
      Code = code;
      Category = category;
    }
  }

Again, no surprises here; it has the same form as the Book class.

Now we look at the binding code, contained in MainWindow.xaml.cs. The constructor is

    public MainWindow()
    {
      this.InitializeComponent();
      InitializeLibrary();
      InitializeCategoryList();
      UpdateButtonStates();
    }

After the standard InitializeComponent() call, we initialize the list of Books (the library) that is displayed in the ListBox and then the list of categories that is displayed in the ComboBox.

Here’s InitializeLibrary():

    ObservableCollection<Book> library;
    private void InitializeLibrary()
    {
      library = new ObservableCollection<Book>();
      library.Add(new Book("Feynman", "Feynman Lectures on Physics", BookPrice(), 2));
      library.Add(new Book("Asimov", "I, Robot", BookPrice(), 1));
      library.Add(new Book("Christie", "Death on the Nile", BookPrice(), 3));
      library.Add(new Book("Taylor", "From Sarajevo to Potsdam", BookPrice(), 4));
      LayoutRoot.DataContext = library;

      Binding listBoxBinding = new Binding();
      BindingOperations.SetBinding(booksListBox, ListBox.ItemsSourceProperty, listBoxBinding);
      booksListBox.DisplayMemberPath = "Title";
      booksListBox.SelectedValuePath = "CategoryCode";
      booksListBox.IsSynchronizedWithCurrentItem = true;
    }

We’re just hard-coding the first four books in the list. In more advanced code, you’d probably read them from a file and/or offer the user a way of entering the data in the interface (or, of course, read them from a database). Here we create a List of Books and set it as the data context.

Next we create a Binding and attach it to the list box (booksListBox is the ListBox that displays the books). The ItemsSourceProperty is set to this binding. Note that we don’t specify a Path for the binding itself. This causes the ListBox to bind to the entire data set in the data context. (Well, actually, it’s binding to the collection view that has been automatically wrapped around the List, as we saw in the previous post.) If we stopped at this point and ran the program, the ListBox would display a list of entires, each of which would say “ListBinding1.Book”. This is because, in the absence of any further instructions on how to display each element in the List, the ListBox calls the ToString() method of each object in the List. If no ToString() has been provided explicitly (we haven’t here), the default ToString() just prints out the data type of the object, which in this case is the Book class in the ListBinding1 namespace. In order to get something more informative to show up in the ListBox, we need to tell it which part of the Book object to display. That’s what the DisplayMemberPath is for. We set this to the name of the property of the Book that we want to show up; in this case, the Title. Note that DisplayMemberPath is a property of the ListBox and not of the binding. This can be a bit confusing, but if you think about it, it does make sense. The binding is concerned only with which element to display; the mechanics of what to display are handled by the UI control, in this case the ListBox.

The SelectedValuePath is a bit more subtle. List controls such as ListBoxes allow one property of the current item to be used for display, while reserving another property as the actual value of the item. The current item selected in a ListBox is the SelectedItem, and the value of this item is the SelectedValue. The SelectedValuePath tells the ListBox which property of the SelectedItem should be used for the SelectedValue. Thus SelectedValuePath is the name of a property and is not a value itself; the SelectedValue is the actual value of that property for the SelectedItem.

Finally we tell the ListBox to keep synchronized with the CurrentItem as specified in the collection view. If you don’t do this, the SelectedItem in the ListBox won’t change as you step through the Books in the collection view by using the navigation buttons.

If you ran the program at this point, you should see the titles displayed in the ListBox, and the highlighted item keep step with the CurrentItem as you use the buttons to move around the list. The ComboBox, however, will still be inert since we haven’t linked it into the program yet.

Now we look at the category list and how to bind it to the ComboBox. We use an ObservableCollection for this list as well, although in the current program there is no provision for adding or deleting categories, so we could have used an ordinary List with the same result. The code is:

    ObservableCollection<BookCategory> CategoryList;
    private void InitializeCategoryList()
    {
      CategoryList = new ObservableCollection<BookCategory>();
      CategoryList.Add(new BookCategory(1, "Science Fiction"));
      CategoryList.Add(new BookCategory(2, "Physics"));
      CategoryList.Add(new BookCategory(3, "Mystery"));
      CategoryList.Add(new BookCategory(4, "History"));
      Binding comboBinding = new Binding();
      comboBinding.Source = CategoryList;
      BindingOperations.SetBinding(categoryComboBox, ComboBox.ItemsSourceProperty, comboBinding);
      categoryComboBox.DisplayMemberPath = "Category";
      categoryComboBox.SelectedValuePath = "Code";

      Binding codeComboBinding = new Binding();
      codeComboBinding.Path = new PropertyPath("CategoryCode");
      BindingOperations.SetBinding(categoryComboBox, ComboBox.SelectedValueProperty, codeComboBinding);
    }

As with the book list, we are hard-coding a few categories for illustration; in real life you’d read these from a file or a database.

We begin by binding CategoryList to the combo box. Since CategoryList isn’t the data context, we need to specify it as the Source of the binding. As with the book list, we get the ComboBox to display the textual form of the category while using the numerical Code as its SelectedValue.

The semi-magical bit comes in the last 3 lines of code. Here we introduce a second binding for the ComboBox. This is a binding for the ComboBox’s SelectedValue, and we bind it to the CategoryCode property in the library (the list of Books). Note that we don’t specify a Source for this binding, so it will use the data context, which is the same library as the ListBox uses. By binding the ComboBox’s SelectedValue to the CategoryCode in the CurrentItem in the library, we synchronize the category displayed by the ComboBox to the category in the CurrentItem (the current Book being displayed). This means that if we change the category by typing a new number into the Code TextBox, then because that TextBox is bound to the CategoryCode in the Book, it will update that property. This in turn will cause the ComboBox’s SelectedValue to change. Then, due to the first binding between the CategoryList and the ComboBox, the new SelectedValue will be used to look up the corresponding DisplayMember, and the display of the ComboBox will be updated. The process also works in reverse: if you select a new category from the ComboBox, the same chain of events (in reverse) will cause the CategoryCode in the Book to change, which in turn will cause the Code TextBox to update.

There are only a couple of other minor changes that were made in this code compared to that in the previous post. The UpdateButtonStates() method has an extra line to ensure that the currently selected item in the ListBox is always visible, using the ScrollIntoView() method in ListBox:

    private void UpdateButtonStates()
    {
      CollectionView libraryView = GetLibraryView();
      previousButton.IsEnabled = libraryView.CurrentPosition > 0;
      nextButton.IsEnabled = libraryView.CurrentPosition < libraryView.Count - 1;
      booksListBox.ScrollIntoView(booksListBox.SelectedItem);
    }

Finally, the event handler for the Add button is modified to provide a category for a new book:

    private void addButton_Click(object sender, RoutedEventArgs e)
    {
      int newBookNum = library.Count + 1;
      library.Add(new Book("Author " + newBookNum, "Title " + newBookNum,
        BookPrice(), rand.Next(1, CategoryList.Count)));
      CollectionView libraryView = GetLibraryView();
      libraryView.MoveCurrentToLast();
      UpdateButtonStates();
    }

We generate the category randomly from those available in CategoryList.

Note that we didn’t need to change any of the logic for things like moving around the list, adding to the list, sorting or filtering. All of that is handled by the collection view, and the binding between the ListBox and ComboBox and the collection view works in exactly the same way as between the more cumbersome TextBox representation we used in the previous post.

Code available here.

Collection views in WPF

We’ve seen (for simple binding and also using converters to convert from one data type to another) how to use data binding to keep a dependency property of a control synchronized with the value of a field in an object. These examples, however, worked only when the data to which we were binding consisted of a single object. In real life, we often wish to bind to a list of data objects, so we’ll start having a look at that here.

We’ll use as an example the case where our data consists of a list of books. The interface displays the properties of a single book at a time, but we wish to be able to step through the list and display the properties of each book in order. As a bonus, we would also like to sort the books, and apply a filter to the list to pick out the cheapest books. The interface we’ll be using looks like this:

The buttons have the following effects:

  • First: go to the first book in the list.
  • Previous: go to the book immediately preceding the current book.
  • Next: go to the book immediately following the current book.
  • Last: go to the last book in the list.
  • Add: add a new book to the list.
  • Sort: sort the books in ascending order by price.
  • Cheap: display only those books with a price under 20.00.

The interface was constructed in Expression Blend and uses standard techniques. We won’t go into the interface here, but if you want a look, download the code (link at end of the post) and look at the MainWindow.xaml file.

Before we get into the code, we need to explain some of the theory behind data binding with lists. As you can see from the interface, the controls (TextBoxes) bind to the properties of only one book at a time, so somehow we need a way of extracting this information from the overall list. Also, we need a way of keeping track of where we are in the list so that the Previous and Next buttons work properly.

If all we were concerned with was creating a data structure which stores a list of books, we would create a Book class containing author, title and price fields, and then create a list of Books by using WPF’s built-in generic List type, as in List<Book>. We might then think that we’d be faced with having to write a lot of code to keep track of a position in the list (as well as do the sorting and filtering that we want in this case). However, WPF provides a magical interface called ICollectionView the implementation of which does all this for us. So what exactly is a collection view?

A collection view, as its name implies, provides a customizable view of a collection, such as a list. The job of the collection view is to allow us to move around the list, as well as performing other tasks such as sorting and filtering. As it’s a view, it provides a read-only interface to the list, so it’s not possible to change anything in the original list by means of the collection view.

A collection view is automatically created for us when we bind to a List object. That is, the act of defining a List object as a data context will create a collection view, and it is in fact this view that we bind to rather than the original List object itself. Once the view has been created, we can manipulate our position in the list, as well as sort and filter the list, by using methods of the view, rather than methods of the original list. At all times, it is the CurrentItem of the view that is used as the single data item within the list to which controls are bound. Thus changing the CurrentItem (for example, by stepping through the list) changes which item is bound to a control, with the result that the values displayed by the control change as we change the CurrentItem.

Let’s see how all this works in practice. First, we define the Book class, which must implement INotifyPropertyChanged since ultimately it is a Book object to which the controls bind. At this level, data binding works just the same way as in our earlier examples where we considered binding to a single object. The Book class should thus look familiar if you’ve read the earlier posts:

  class Book : INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;
    protected void Notify(string propName)
    {
      if (this.PropertyChanged != null)
      {
        PropertyChanged(this, new PropertyChangedEventArgs(propName));
      }
    }

    string author;

    public string Author
    {
      get { return author; }
      set
      {
        if (author.Equals(value)) { return; }
        author = value;
        Notify("Author");
      }
    }

    string title;

    public string Title
    {
      get { return title; }
      set { if (title.Equals(value)) { return; }
        title = value;
        Notify("Title");
      }
    }

    decimal price;

    public decimal Price
    {
      get { return price; }
      set { if (price == value) { return; }
        price = value;
        Notify("Price");
      }
    }

    public Book() { }
    public Book(string author, string title, decimal price)
    {
      this.author = author;
      this.title = title;
      this.price = price;
    }
  }

Note that we’re using C#’s decimal data type to store the price, since we don’t want any round-off errors.

Next, we’ll look at creating a list of Books in which we can store a number of Books. However, rather than use the List generic type, we use an ObservableCollection instead. The reason for this is that this class implements the INotifyCollectionChanged interface, which means that any changes to the list (such as adding or deleting items) notify any controls bound to the list so they can update themselves. Apart from that difference, we can treat an ObservableCollection pretty much like a List. The relevant code from the MainWindow class is:

    public MainWindow()
    {
      this.InitializeComponent();
      InitializeLibrary(4);
      LayoutRoot.DataContext = library;
      UpdateButtonStates();
    }

    ObservableCollection<Book> library;
    private void InitializeLibrary(int numBooks)
    {
      library = new ObservableCollection<Book>();
      for (int i = 1; i <= numBooks; i++)
      {
        library.Add(new Book("Author " + i, "Title " + i, BookPrice()));
      }
    }

    Random rand = new Random();
    private decimal BookPrice()
    {
      decimal price = rand.Next(0, 5000) / 100m;
      return price;
    }

If you look in MainWindow.xaml, you’ll see that each of the three TextBoxes has its Text property bound to one of the fields in Book (that is, the Author TextBox is bound to the “Author” property, and so on).

We initialize the list of books by calling InitializeLibrary(). This merely generates a number of books with an author of “Author <number>” and a title of “Title <number>”; the price is a random value between 0.01 and 50.00.

On line 5, we define library as the data context of LayoutRoot (which is the Grid layout on which the interface is built). Since library is an ObservableCollection<Book>, this automatically creates a collection view which is wrapped around the list, and which will be used to determine which Book from the library is used as the current item to which the controls are bound. For example, if the current item is set to the first item in the list, then it is the first Book (the one with Author 1, etc) that is used in the binding to the three TextBoxes.

If we ran the code at this point (without even defining click event handlers for the buttons), the TextBoxes would display the properties from the first Book in library. In order to add functionality to the buttons and allow us to step through the list of Books in the library, we need to acquire and use the collection view.

First, how do we get hold of the collection view in the first place? There is a static method which allows this to be done:

    private CollectionView GetLibraryView()
    {
      return (CollectionView)CollectionViewSource.GetDefaultView(library);
    }

We need to provide an explicit cast to CollectionView, since the GetDefaultView() method is defined only as returning something that implements the ICollectionView interface. The CollectionView class is a WPF class that implements this interface and is in fact what GetDefaultView() returns. CollectionView also provides a few properties that aren’t defined in the ICollectionView interface, so it’s useful to deal with the class explicitly rather than just with the interface.

Note that GetDefaultView() does not create the collection view; that is done when the library is assigned as the data context. GetDefaultView() merely retrieves the collection view object that already exists. Thus successive calls to GetDefaultView() don’t erase any of the changes to the view that other method calls might make.

The first method that uses some of the view properties is the UpdateButtonStates() method that we call from the constructor above:

    private void UpdateButtonStates()
    {
      CollectionView libraryView = GetLibraryView();
      previousButton.IsEnabled = libraryView.CurrentPosition > 0;
      nextButton.IsEnabled = libraryView.CurrentPosition < libraryView.Count - 1;
    }

The view maintains a CurrentPosition marker which points at the location of the CurrentItem. Note that this is not necessarily the same as the position of the item in the original list, since a view can sort or filter the list items and generate a new order for the original items. All of this is done without affecting the data or its order in the original list; the view maintains a separate catalogue of the elements of the list and their new order.

UpdateButtonStates() disables the Previous button if CurrentPosition is 0. It also disables the Next button if the CurrentPosition is at the end of the view of the list. (Note that the Count property is not provided by ICollectionView; it is something that is added in the CollectionView class, which is one reason why we deal with the class explicitly.)

We can now add event handlers for the four buttons that move around the list:

    private void firstButton_Click(object sender, RoutedEventArgs e)
    {
      CollectionView libraryView = GetLibraryView();
      libraryView.MoveCurrentToFirst();
      UpdateButtonStates();
    }

    private void previousButton_Click(object sender, RoutedEventArgs e)
    {
      CollectionView libraryView = GetLibraryView();
      libraryView.MoveCurrentToPrevious();
      UpdateButtonStates();
    }

    private void nextButton_Click(object sender, RoutedEventArgs e)
    {
      CollectionView libraryView = GetLibraryView();
      libraryView.MoveCurrentToNext();
      UpdateButtonStates();
    }

    private void lastButton_Click(object sender, RoutedEventArgs e)
    {
      CollectionView libraryView = GetLibraryView();
      libraryView.MoveCurrentToLast();
      UpdateButtonStates();
    }

These should all be fairly obvious, since we’re using the methods from CollectionView to move around the list.

Next, we look at how we can sort the list. The event handler for the Sort button is:

    private void sortButton_Click(object sender, RoutedEventArgs e)
    {
      CollectionView libraryView = GetLibraryView();
      if (libraryView.SortDescriptions.Count != 0)
      {
        libraryView.SortDescriptions.Clear();
        sortButton.Content = "Sort";
      }
      else
      {
        libraryView.SortDescriptions.Add(new SortDescription("Price", ListSortDirection.Ascending));
        sortButton.Content = "Clear";
      }
      libraryView.MoveCurrentToFirst();
      UpdateButtonStates();
    }

A collection view maintains a list of SortDescriptions, which are applied in order. In this case, the Sort button is a toggle between a sorted and unsorted list. If there are SortDescriptions present (which would result in the list being sorted), we clear any existing SortDescriptions, which restores the list to its original order. If there are no SortDescriptions, then we add one which sorts the books by their price, in ascending order. In each case, we change the button’s caption to show what pressing it again will do.

Note that we don’t need to call a Sort() method to actually do the sorting; the simple act of adding a SortDescription to the view causes sorting to take place. If we had added a second SortDescription, then any books with the same price would be sorted according to the second SortDescription, and so on for further SortDescriptions. Remember that sorting affects only the order of the Books in the view; it does not change the order of the data in the original list.

Filtering is done by applying a Predicate (a condition which must be either true or false) to the list of Books, and retaining only those Books for which the Predicate is true. Again, note that filtering affects only the view of the list, and does not delete anything from the original list. Thus we could apply one filter that selects books with a price under 20.00, and then apply another filter for books over 20.00. Each filter would alter the view of the list, but not the list itself. The code for the filter is:

    private void filterButton_Click(object sender, RoutedEventArgs e)
    {
      CollectionView libraryView = GetLibraryView();
      if (libraryView.Filter != null)
      {
        libraryView.Filter = null;
        filterButton.Content = "Cheap";
      }
      else
      {
        libraryView.Filter = new Predicate<object>(book => ((Book)book).Price < 20);
        filterButton.Content = "All";
      }
      libraryView.MoveCurrentToFirst();
      UpdateButtonStates();
    }

Again, we are treating the filter button as a toggle between a filtered and unfiltered list. We’ve used the Predicate generic type defined in recent versions of C# (see here). The argument to the Predicate constructor tests if the Book’s price is less than 20. Again, all we need do is define the filter; we don’t need to call any explicit Filter() method.

Finally, we look at adding an element to the original list. Since a collection view doesn’t allow us to change the original list, we need to do this by dealing with the ObservableCollection<Book> object directly. Once we’ve added the new Book, we can then re-acquire the collection view for the new list. Because we defined the library as an ObservableCollection<Book>, any additions we make will be automatically notified to controls bound to the library, so the displays will be updated without the need for any further code.

    private void addButton_Click(object sender, RoutedEventArgs e)
    {
      int newBookNum = library.Count + 1;
      library.Add(new Book("Author " + newBookNum, "Title " + newBookNum, BookPrice()));
      CollectionView libraryView = GetLibraryView();
      libraryView.MoveCurrentToLast();
      UpdateButtonStates();
    }

This little program should give you a fair idea of what the collection view can do. There are a few other features as well, so have a stroll through the documentation to see what you can do. One thing this post doesn’t address, however, is how to display more than one element from a list at the same time, so we’ll get to that later.

Code available here.

Data binding in WPF – converters

In the last post, we saw how to use data binding to synchronize the value of a data field with the text displayed in a TextBox. In that example, the binding was relatively straightforward, since the type of data (an int) can be displayed as text in a TextBox, as WPF does an automatic conversion from a numeric type to text.

However, data binding is much more flexible than this. Pretty well any property of a control can be bound to any type of data. If WPF doesn’t provide an automatic conversion, though, you’ll need to write a converter yourself to fix the binding.

We’ll look at a couple of examples here. First, recall that the program we were working on in the last post allowed the user to step through the integers and test if each was either prime or perfect. The interface to the program looks like this:

The ‘Factors’ box displays the number of factors (excluding 1 and the number itself) that ‘Number’ has; ‘Sum of factors’ adds up Number’s factors (this time, including 1). If ‘Factors’ is zero, the number is prime, while if ‘Sum of factors’ equals Number, the number is perfect. We’d like to colour-code these two boxes so that Factors is LightSalmon if the number is prime, and Sum of factors is LightSeaGreen if the number is perfect. This requires binding a numerical value (in the case of a prime number) or a logical value (in the case of a perfect number) to a Brush used to paint the background of a TextBox. Not surprisingly, there is no built-in conversion between these types of data, so we’ll need to write one.

A data binding converter must be a class that implements the System.Windows.Data.IValueConverter interface. This interface requires us to write two methods: Convert() and ConvertBack(). Here’s the code for NumberToBackgroundConverter, which does the required conversion in our program:

  using System.Windows.Data;
  using System.Windows.Media;
  ......
  class NumberToBackgroundConverter : IValueConverter
  {
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
      if (parameter.ToString().Equals("IsPerfect"))
      {
        bool isPerfect = bool.Parse(value.ToString());
        if (isPerfect)
        {
          return Brushes.LightSeaGreen;
        }
        else
        {
          return Brushes.Transparent;
        }
      }
      else if (parameter.ToString().Equals("NumFactors"))
      {
        int number = int.Parse(value.ToString());
        if (number == 0)
        {
          return Brushes.LightSalmon;
        }
        else
        {
          return Brushes.Transparent;
        }
      }
      return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
      throw new NotImplementedException();
    }
  }

Notice the two using statements at the start; they are required for the IValueConverter interface and the Brushes objects, respectively.

The Convert() method is the only one we need to implement, since we won’t be converting back from a Brush to a data value. The first argument to Convert(), ‘value’, is the object from which conversion is taking place, and the object returned from Convert() is the object to which conversion is done. These are both defined as object type, which means that a converter can convert any type of object to any other type (since ‘object’ is the base class of all C# classes).

The second argument, ‘targetType’, is the data type of the target, which is sometimes useful in deciding what object to generate as the return value. We don’t use it here. The third argument, ‘parameter’, is a parameter that can also be used to decide what type of conversion to do. In this example, the parameter is used to decide whether we’re dealing with a prime number or a perfect number. We’ll see how this parameter is specified in a minute, when we consider attaching the converter to a control’s property.

The final argument, ‘culture’, is used in globalization, as it allows things like date and time strings to be formatted appropriately for a given country. Again, we won’t be using this here.

The rest of the code is fairly obvious. We test to see if the parameter is “IsPerfect”. If so, ‘value’ will be a bool indicating whether the current number is perfect. If it is, we return a LightSeaGreen brush; if not, we return a Transparent brush so the regular background colour of the ‘Sum of factors’  TextBox shows through. The background colour of TextBoxes and other controls depends on the overall Windows theme the user has adopted, so we don’t want to anticipate the choice by specifying a particular colour.

The second part of Convert() deals with the prime number box. If parameter is “NumFactors”, ‘value’ will be an int, so we check to see if it’s zero. If so, we return a LightSalmon brush.

That’s it for the converter class. Now, how do we use it?

In Expression Blend (EB), select the sumFactorsTextBox. In its properties panel, find Background and click on the little square to the right. This will bring up a context menu, from which you select ‘Data binding’. Select the data field to act as the source in the same way as we did in the previous post when binding data to the text in a TextBox. In this case, we want to bind the background colour to the IsPerfect bool data field in the PrimePerfect class, so add that class to the Data Sources in the Data Field tab (if it’s not already there), and then select the IsPerfect field on the right. You will need to select ‘All properties’ in the show box at the lower right to make these data fields visible.

Next, expand the panel at the bottom by clicking on the downward pointing arrow. Click the … box to the right of ‘Value converter’ and select the NumberToBackgroundConverter class. Enter “IsPerfect” in the ‘Converter parameter’ box, then click OK. This completes the data binding, and if you run the program at this point, the Sum of factors box should turn green whenever you hit a perfect number (6 is the lowest one; 28 is the next).

The procedure for the ‘Factors’ box is just the same. This time, bind the Background to the NumFactors field in PrimePerfect. Use the same converter class, but give the parameter as “NumFactors”. Now both boxes should light up at the correct times as you step through the numbers.

As a final example, we would like the Dec button to become disabled if Number reaches 2, since we don’t want to look at any integers smaller than 2. To do this, we first write another converter class:

using System.Windows.Data;
......
  class NumberToEnabledConverter : IValueConverter
  {
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
      int number = int.Parse(value.ToString());
      if (number <= 2)
      {
        return false;
      }
      else
      {
        return true;
      }
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
      throw new NotImplementedException();
    }
  }

This time the input ‘value’ is an integer and the return object is a bool (false if the button is disabled, true otherwise). We check if ‘value’ is <= 2 and disable the button if it is.

In EB, the binding proceeds just as before. Select decButton, find its IsEnabled property (in Common Properties in the Properties panel), bind this to the Number field in PrimePerfect and attach a converter, specifying the converter class as NumberToEnabledConverter. No parameter is needed this time. Now each time Number gets reduced to 2, Dec is disabled.

There is still one flaw in this program: it is possible to select the Number TextBox and type in a value less than 2 (in fact you can enter a negative number). You can even type in any old text, though if you do this and then hit tab to remove the focus from the TextBox, you’ll see a red outline appear around the box, indicating invalid input (which admittedly isn’t very user-friendly – what does the red box mean, and what is correct input?). To fix these problems, we need to investigate validation, but that’s a topic for another post.

Source code available here.

Simple data binding in WPF: 1 – Binding controls to objects

We’ll start having a look at data binding in WPF. Briefly, data binding is the ability to bind some property of a control (such as the text in a TextBox) to the value of a data field in an object. (More generally, you can bind controls to other data sources such as databases, but that’s more advanced.) The idea, and the need for it, are best illustrated with an example.

Suppose we want a little program that allows you to step through the integers and test each one to see if it’s a prime number or a perfect number. In case you’ve forgotten, a prime number is one whose only factors are 1 and the number itself, so 2, 3, 5, 7, 11, 13, 17 and so on are the first few prime numbers. A perfect number is one where the sum of all its factors (including 1 but excluding the number itself) equals the number itself. The smallest perfect number is 6, since its factors are 1, 2 and 3, and 1+2+3=6. The next perfect number is 28, and after that they get quite large, and very rare.

The interface for the program might look like this:

The number being considered is shown at the top. The Factors box shows the number of factors (excluding 1 and the number itself) that number has; thus a number with zero factors is prime. The ‘Sum of factors’ box shows the sum of the number’s factors (including 1, but excluding the number). A number whose ‘Sum of factors’ equals the number itself is therefore perfect.

The program should paint the background of the ‘Factors’ box LightSalmon if the number is prime, and the background of the ‘Sum of factors’ box LightSeaGreen if the number is perfect.

The Reset button resets Number to 2. The Dec button decrements Number by 1; Inc increments Number by 1, and Exit quits the program. Note that the Dec button also becomes disabled when Number is 2, since we don’t want to consider any numbers less than 2.

If the only programming technique you knew about was event handling, you can see that you would need to add handlers for each button’s Click event, and these handlers would need to update Number and the values displayed in the various TextBoxes. You would also need to handle the TextChanged event in the Number TextBox so if the user typed in a new number it would update the other boxes. Quite a bit of back-and-forth coding would be needed to keep everything synchronized.

Data binding allows the value displayed by a TextBox, or in fact pretty well any property of any control, to be bound to a data value, so that changing the data value automatically updates the property, and changing the property can also update the data value.

Let’s start with the simplest case: binding the value displayed by a TextBox to a data field in an object. To work on this project, we’ll use Expression Blend (EB) for the design and binding work, and Visual Studio (VS) for writing the classes we need to support the data binding. I’ve built the user interface using EB and won’t go into the details here; you can look here for an introduction to using EB to build interfaces. If you want the XAML code, you can download the project files; see the end of this post for the link.

We’ll need a class for storing the details of the number being considered, and for calculating the factors of that number. We’ll call that class PrimePerfect, and create it in VS. It’s a pretty standard class, except for one thing: it must be able to notify the controls when any of the relevant data fields are changed so they can be updated. In data binding, the way this is done is by writing a class that implements the INotifyPropertyChanged interface. The beginning of PrimePerfect therefore looks like this:

  using System.ComponentModel;
  ......
  class PrimePerfect : INotifyPropertyChanged  // in System.ComponentModel
  {
    public event PropertyChangedEventHandler PropertyChanged;
    protected void Notify(string propName)
    {
      if (this.PropertyChanged != null)
      {
        PropertyChanged(this, new PropertyChangedEventArgs(propName));
      }
    }
  ......

Note the using statement at the top; it is required for notification code.

Implementing INotifyPropertyChanged requires that we declare an event handler for PropertyChanged, as we’ve done on line 5. The Notify() method on line 6 takes a string parameter which is used to identify which property has changed, and the event handler is then called, passing along this parameter.

The rest of the class consists of standard code, defining the properties and calculating the factors of the number. A typical property looks like this:

    int numFactors;
    public int NumFactors
    {
      get { return numFactors; }
      set
      {
        numFactors = value;
        Notify("NumFactors");
      }
    }

The property contains the usual ‘get’ and ‘set’ clauses, but note that in the ‘set’, a call to Notify() is made, passing along a string identifying which property has been set. This property string is used to bind the text field of a TextBox to the value of NumFactors.

To see how this is done, we return to EB (make sure you save and build the project at this point, so the updates to the PrimePerfect class are available to EB). We’ll add the binding between the factorsTextBox (that displays the current value of NumFactors) and the NumFactors data field in PrimePerfect. In EB, select factorsTextBox. In ‘Common Properties’ in the right panel, find the Text entry. Click on the little square to the right of the entry, and select ‘Data binding’ from the context menu. In the dialog box that appears, click “Data field” at the top, then “+CLR Object”. This brings up another dialog that shows the available data sources for the binding. Find the namespace containing the PrimePerfect class (in the project here, I’ve called it PrimePerfectBinding), and select the PrimePerfect class, then OK. You should now see PrimePerfectDataSource appear in the Data sources column. Click on it, and PrimePerfect should appear in the Fields list on the right. Open it and select NumFactors from the list (if you can’t see it, try setting ‘Show’ to ‘All properties’ in the combo box at the bottom). Click OK and the binding is complete.

If you now look at the XAML code for factorsTextBox, you should see Text=”{Binding NumFactors}” as one of the attributes.

If you’ve been following faithfully, you’ll probably have noticed that although we’ve defined the PrimePerfect class and added notification code to it, and also added a binding to factorsTextBox, we haven’t actually created an instance of PrimePerfect yet. To remedy this, go to the C# code-behind file for MainWindow, and add a couple of lines to it as follows.

    PrimePerfect primePerfect;
    public MainWindow()
    {
      InitializeComponent();
      primePerfect = new PrimePerfect();
      baseGrid.DataContext = primePerfect;
    }

You’ll see we create an instance of PrimePerfect (its default constructor sets Number = 2). However, we’ve also added a cryptic line in which a DataContext is set to this object. What’s a DataContext?

The data context is an object that serves as the source of the data in a binding. When a control, such as factorsTextBox, was assigned a binding, all we did was specify the name of the data field (“NumFactors”) to which it is bound. We didn’t give the TextBox any information about where it should look for this data. The WPF data binding mechanism takes care of this by having a control with a data binding look for a data context. It will look first in the control itself. If a data context isn’t found there, it will look in the parent container of that control, and so on until it reaches the root container (usually the Window). What we’ve done here is attach a DataContext to the Grid that contains all the controls in the interface (‘baseGrid’ is the name of that Grid object). This means that all controls in that Grid that have data bindings will look in the same PrimePerfect object for their data. This completes the data binding process for factorsTextBox.

To get the program to run at this point, we need to complete the code in PrimePerfect so that it calculates NumFactors and SumFactors for the current Number. This code doesn’t have any bearing on the data binding so we won’t list it here, but if you want to see how it’s done, just download the source code and have a look at it.

If you run the program at this point, you should see the Factors TextBox display the current value of NumFactors, which is 0 (since 2 is the starting Number, and it’s prime). The Number and Sum of factors TextBoxes won’t show anything unless you go through the same process and bind them to the corresponding properties in PrimePerfect. There’s nothing new involved in doing this, so you can either try it yourself or just look at the source code to see how it’s done.

So far, we haven’t added any event handlers to the buttons, so let’s finish with that. We can add handlers for the buttons by double clicking on the Click event for each button in EB. This generates event handlers in MainWindow.xaml.cs, and we can enter some code in them as follows.

    private void incButton_Click(object sender, RoutedEventArgs e)
    {
      primePerfect.Number++;
    }

    private void decButton_Click(object sender, RoutedEventArgs e)
    {
      primePerfect.Number--;
    }

    private void exitButton_Click(object sender, RoutedEventArgs e)
    {
      Application.Current.Shutdown();
    }

    private void resetButton_Click(object sender, RoutedEventArgs e)
    {
      primePerfect.Number = 2;
    }

There’s nothing surprising here. The three buttons that change the value of Number do so with a single line each. Now if you run the program, you’ll see that clicking on each button does change the value of Number, and also updates Factors and Sum of factors. Why? Because whenever we change the value of Number, we are calling the ‘set’ method of the Number property in PrimePerfect, and this calls code that recalculates NumFactors and SumFactors. All of this fires a Notify event for each property, so any control that is bound to that property gets updated automatically.

At this stage, we have a program that allows us to step through the numbers and display NumFactors and SumFactors for each number. However, we still haven’t added the colour-coding of the textboxes when we find a prime or perfect number, and the Dec button will allow us to set Number lower than 2, which may have disastrous consequences if the number goes negative. We can fix all these problems using data binding as well, but that’s a topic for the next post.

Code for this post is here.