Edit

Share via


Bind objects as data sources in .NET Framework applications using Visual Studio

Note

Datasets and related classes are legacy .NET Framework technologies from the early 2000s that enable applications to work with data in memory while the applications are disconnected from the database. The technologies are especially useful for applications that enable users to modify data and persist the changes back to the database. Although datasets have proven to be a very successful technology, we recommend that new .NET applications use Entity Framework Core. Entity Framework provides a more natural way to work with tabular data as object models, and it has a simpler programming interface.

Visual Studio provides design-time tools for working with custom objects as the data source in your application. When you want to store data from a database in an object that you bind to UI controls, the recommended approach is to use Entity Framework to generate the class or classes. Entity Framework auto-generates all the boilerplate change-tracking code, which means that any changes to the local objects are automatically persisted to the database when you call AcceptChanges on the DbSet object. For more information, see Entity Framework Documentation.

Tip

The approaches to object binding in this article should only be considered if your application is already based on datasets. You can also use these approaches if you are already familiar with datasets, and the data you will be processing is tabular and not too complex or too big. For an even simpler example, involving loading data directly into objects by using a DataReader and manually updating the UI without databinding, see Create a simple data application by using ADO.NET.

Object requirements

The only requirement for custom objects to work with the data design tools in Visual Studio is that the object needs at least one public property.

Generally, custom objects do not require any specific interfaces, constructors, or attributes to act as a data source for an application. However, if you want to drag the object from the Data Sources window to a design surface to create a data-bound control, and if the object implements the ITypedList or IListSource interface, the object must have a default constructor. Otherwise, Visual Studio cannot instantiate the data source object, and it displays an error when you drag the item to the design surface.

Examples of using custom objects as data sources

While there are countless ways to implement your application logic when working with objects as a data source, for SQL databases there are a few standard operations that can be simplified by using the Visual Studio-generated TableAdapter objects. This page explains how to implement these standard processes using TableAdapters. It is not intended as a guide for creating your custom objects. For example, you will typically perform the following standard operations regardless of the specific implementation of your objects, or application's logic:

  • Loading data into objects (typically from a database).

  • Creating a typed collection of objects.

  • Adding objects to and removing objects from a collection.

  • Displaying the object data to users on a form.

  • Changing/editing the data in an object.

  • Saving data from objects back to the database.

Load data into objects

For this example, you load data into your objects by using TableAdapters. By default, TableAdapters are created with two kinds of methods that fetch data from a database and populate data tables.

  • The TableAdapter.Fill method fills an existing data table with the data returned.

  • The TableAdapter.GetData method returns a new data table populated with data.

The easiest way to load your custom objects with data is to call the TableAdapter.GetData method, loop through the collection of rows in the returned data table, and populate each object with the values in each row. You can create a GetData method that returns a populated data table for any query added to a TableAdapter.

Note

Visual Studio names the TableAdapter queries Fill and GetData by default, but you can change those names to any valid method name.

The following example shows how to loop through the rows in a data table, and populate an object with data:

private void LoadCustomers()
{
    NorthwindDataSet.CustomersDataTable customerData = 
        customersTableAdapter1.GetTop5Customers();
    
    foreach (NorthwindDataSet.CustomersRow customerRow in customerData)
    {
        Customer currentCustomer = new Customer();
        currentCustomer.CustomerID = customerRow.CustomerID;
        currentCustomer.CompanyName = customerRow.CompanyName;

        if (customerRow.IsAddressNull() == false)
        {
            currentCustomer.Address = customerRow.Address;
        }

        if (customerRow.IsCityNull() == false)
        {
            currentCustomer.City = customerRow.City;
        }

        if (customerRow.IsContactNameNull() == false)
        {
            currentCustomer.ContactName = customerRow.ContactName;
        }

        if (customerRow.IsContactTitleNull() == false)
        {
            currentCustomer.ContactTitle = customerRow.ContactTitle;
        }

        if (customerRow.IsCountryNull() == false)
        {
            currentCustomer.Country = customerRow.Country;
        }

        if (customerRow.IsFaxNull() == false)
        {
            currentCustomer.Fax = customerRow.Fax;
        }

        if (customerRow.IsPhoneNull() == false)
        {
            currentCustomer.Phone = customerRow.Phone;
        }

        if (customerRow.IsPostalCodeNull() == false)
        {
            currentCustomer.PostalCode = customerRow.PostalCode;
        }

        if (customerRow.IsRegionNull() == false)
        {
            currentCustomer.Region = customerRow.Region;
        }

        LoadOrders(currentCustomer);
        customerBindingSource.Add(currentCustomer);
    }
}

Create a typed collection of objects

You can create collection classes for your objects, or use the typed collections that are automatically provided by the BindingSource component.

When you're creating a custom collection class for objects, we suggest that you inherit from BindingList<T>. This generic class provides functionality to administer your collection, as well as the ability to raise events that send notifications to the data-binding infrastructure in Windows Forms.

The automatically generated collection in the BindingSource uses a BindingList<T> for its typed collection. If your application does not require additional functionality, you can maintain your collection within the BindingSource. For more information, see the List property of the BindingSource class.

Note

If your collection requires functionality not provided by the base implementation of the BindingList<T>, you should create a custom collection so you can add to the class as needed.

The following code shows how to create the class for a strongly-typed collection of Order objects:

/// <summary>
/// A collection of Orders
/// </summary>
public class Orders: System.ComponentModel.BindingList<Order>
{
    // Add any additional functionality required by your collection.
}

Add objects to a collection

You add objects to a collection by calling the Add method of your custom collection class or of the BindingSource.

Note

The Add method is automatically provided for your custom collection when you inherit from BindingList<T>.

The following code shows how to add objects to the typed collection in a BindingSource:

Customer currentCustomer = new Customer();
customerBindingSource.Add(currentCustomer);

The following code shows how to add objects to a typed collection that inherits from BindingList<T>:

Note

In this example, the Orders collection is a property of the Customer object.

Order currentOrder = new Order();
currentCustomer.Orders.Add(currentOrder);

Remove objects from a collection

You remove objects from a collection by calling the Remove or RemoveAt method of your custom collection class or of BindingSource.

Note

The Remove and RemoveAt methods are automatically provided for your custom collection when you inherit from BindingList<T>.

The following code shows how to locate and remove objects from the typed collection in a BindingSource with the RemoveAt method:

int customerIndex = customerBindingSource.Find("CustomerID", "ALFKI");
customerBindingSource.RemoveAt(customerIndex);

Display object data to users

To display the data in objects to users, create an object data source using the Data Source Configuration wizard, and then drag the entire object or individual properties onto your form from the Data Sources window.

Modify the data in objects

To edit data in custom objects that are data-bound to Windows Forms controls, simply edit the data in the bound control (or directly in the object's properties). Data-binding architecture updates the data in the object.

If your application requires the tracking of changes and the rolling back of proposed changes to their original values, then you must implement this functionality in your object model. For examples of how data tables keep track of proposed changes, see DataRowState, HasChanges, and GetChanges.

Save data in objects back to the database

Save data back to the database by passing the values from your object to the TableAdapter's DBDirect methods.

Visual Studio creates DBDirect methods that can be executed directly against the database. These methods do not require DataSet or DataTable objects.

TableAdapter DBDirect method Description
TableAdapter.Insert Adds new records to a database, allowing you to pass in individual column values as method parameters.
TableAdapter.Update Updates existing records in a database. The Update method takes original and new column values as method parameters. The original values are used to locate the original record, and the new values are used to update that record.

The TableAdapter.Update method is also used to reconcile changes in a dataset back to the database, by taking a DataSet, DataTable, DataRow, or array of DataRows as method parameters.
TableAdapter.Delete Deletes existing records from the database based on the original column values passed in as method parameters.

To save data from a collection of objects, loop through the collection of objects (for example, using a for-next loop). Send the values for each object to the database by using the TableAdapter's DBDirect methods.

The following example shows how to use the TableAdapter.Insert DBDirect method to add a new customer directly into the database:

private void AddNewCustomers(Customer currentCustomer)
{
    customersTableAdapter.Insert( 
        currentCustomer.CustomerID, 
        currentCustomer.CompanyName, 
        currentCustomer.ContactName, 
        currentCustomer.ContactTitle, 
        currentCustomer.Address, 
        currentCustomer.City, 
        currentCustomer.Region, 
        currentCustomer.PostalCode, 
        currentCustomer.Country, 
        currentCustomer.Phone, 
        currentCustomer.Fax);
}