12.1 The DataView and DataViewManager

Data binding depends on two classes in the System.Data namespace: DataView and DataViewManager. These classes provide an important layer of indirection between your data and its display format, allowing you to apply sorts and filter rows without modifying the underlying information?that is, to have different views on the same data. ADO.NET binding is always provided through one of these objects.

Both ASP.NET and Windows Forms allow you to bind other types of objects to controls, including custom classes, arrays, and some collection types. However, ADO.NET binding always uses DataView and DataViewManager, so this chapter focuses on these two classes.

The DataView class acts as a view onto a single DataTable. When creating a DataView object, you specify the underlying DataTable in the constructor:

// Create a new DataView for the Customers table.
DataView view = new DataView(ds.Tables["Customers"]);

Every DataTable also provides a default DataView through the DataTable.DefaultView property:

// Obtain a reference to the default DataView for the Customers table.
DataView view = ds.Tables["Customers"].DefaultView;

The DataViewManager represents a view of an entire DataSet. As with the DataView, you can create a DataViewManager manually, passing in a reference to a DataSet as a constructor argument, or you can use the default DataViewManager provided through the DataSet.DefaultViewManager property.

The DataView and DataViewManager provide three key features:

  • Sorting based on any column criteria

  • Filtering based on any combination of column values

  • Filtering based on the row state (such as deleted, inserted, and unchanged)

12.1.1 Binding to a DataView

To make all this a little clearer, it helps to consider a simple example with the Windows DataGrid control. In Example 12-1, three tables are queried and added to a DataSet. By setting its DataSource property, the Customers table is then bound to the DataGrid in a single highlighted line.

Example 12-1. Binding a single table from a DataSet
private void DataTest_Load(object sender, System.EventArgs e)
    string connectionString = "Data Source=localhost;" +
      "Initial Catalog=Northwind;Integrated Security=SSPI";

    string SQL = "SELECT * FROM Customers";

    // Create ADO.NET objects.
    SqlConnection con = new SqlConnection(connectionString);
    SqlCommand com = new SqlCommand(SQL, con);
    SqlDataAdapter adapter = new SqlDataAdapter(com);
    DataSet ds = new DataSet("Northwind");

    // Execute the command.
        adapter.Fill(ds, "Customers");

        com.CommandText = "SELECT * FROM Products";
        adapter.Fill(ds, "Products");

        com.CommandText = "SELECT * FROM Suppliers";
        adapter.Fill(ds, "Suppliers");
    catch (Exception err)

    // Show the customers table in the grid.
    dataGrid1.DataSource = ds.Tables["Customers"];

On the surface, it looks as though this code is binding the grid directly to a DataTable object. However, behind the scenes, .NET retrieves the corresponding DataTable.DefaultDataView and uses that. You can replace the highlighted line with the following equivalent syntax:

dataGrid1.DataSource = ds.Tables["Customers"].DefaultView;

Similarly, you can create a new DataView object and use it for the binding:

DataView view = new DataView(ds.Tables["Customers"]);
dataGrid1.DataSource = view;

This technique is particularly useful if you want to display different views of the same data in multiple controls. Figure 12-1 shows the result of binding the view. The DataGrid automatically creates a column for each field in the table and displays all the data in the order it was retrieved from the data source. By default, every column is the same width, and the columns are arranged according to the order of fields in the SELECT statement; you'll learn how to customize the view later in this chapter.

There is one reason why you should bind directly to the view rather than use the table name. If you specify an invalid table name when binding directly to a table, you don't receive an error; the DataGrid just appears empty. However, if you make the same mistake when binding to a view, you receive a more informative NullReferenceException.

Figure 12-1. Binding a DataView to a DataGrid

12.1.2 Binding to a DataViewManager

The DataGrid is the only Windows Forms control that supports binding to an entire DataSet as well as a single table, although many other third-party controls follow suit. When binding a DataSet, .NET automatically uses the corresponding DataViewManager provided through the DataSet.DefaultViewManager property:

// Bind to the DefaultViewManager explicitly.
dataGrid1.DataSource = ds.DefaultViewManager;

// Bind to the DefaultViewManager implicitly. This code is equivalent.
dataGrid1.DataSource = ds;

// Bind to an identical DataViewManager you create manually.
// This code has the same effect, but isn't exactly the same
// (because it creates a new object).
dataGrid1.DataSource = new DataViewManager(ds);

Figure 12-2 shows the initial appearance of a DataGrid when bound to a DataSet. A separate navigational link is provided for every table in the DataSet. When the user clicks on one of these links, the corresponding table is shown, as in Figure 12-2.

Figure 12-2. Binding a DataViewManager to a DataGrid

There is one important difference between the DataViewManager and the DataView approach, however. When you use the navigational links to display a table, .NET doesn't use the DefaultView to configure the appearance of the that table. Instead, every DataViewManager provides a collection of DataViewSetting objects. When the user navigates to a table through a DataViewManager, a new DataView is created according to the settings in the corresponding DataViewSetting object.

    Part I: ADO.NET Tutorial
    Part II: ADO.NET Core Classes
    Part III: API Quick Reference