Visual Studio を使用して .NET Framework アプリケーションのデータ ソースとしてオブジェクトをバインドする

Note

データセットと関連クラスは、アプリケーションがデータベースから切断されている間にアプリケーションがメモリ内のデータを操作できるようにする、2000 年代初期からのレガシ .NET Framework テクノロジです。 これらが特に役立つのは、ユーザーがデータを変更し、変更をデータベースに戻して保持できるようにするアプリケーションです。 データセットは非常に優れたテクノロジであることが証明されていますが、新しい .NET アプリケーションでは Entity Framework Core を使用することをお勧めしています。 Entity Framework には、オブジェクト モデルとして表形式データを操作する、より自然な方法が用意されており、よりシンプルなプログラミング インターフェイスが備わっています。

Visual Studio では、カスタム オブジェクトをアプリケーションのデータ ソースとして操作するためのデザイン時ツールが提供されています。 UI コントロールにバインドしたオブジェクトにデータベースのデータを格納したい場合は、Entity Framework を使用して、クラス (1 つまたは複数) を生成する方法が推奨されます。 Entity Framework では、定型の変更追跡コードがすべて自動生成されます。これは、DbSet オブジェクトで AcceptChanges を呼び出したときに、ローカル オブジェクトに対する変更がデータベースに自動的に保存されることを意味します。 詳しくは、Entity Framework のドキュメントをご覧ください。

ヒント

この記事にあるオブジェクト バインドの方法は、アプリケーションが既にデータセットに基づいている場合にのみ、その使用を検討してください。 これらの方法は、データセットを既に使い慣れていて、処理するデータが表形式であり、複雑すぎたり大きすぎたりしない場合にも使用できます。 DataReader を使ってオブジェクトにデータを直接読み込み、データバインドを使わずに UI を手動で更新するなど、さらに簡単な例については、「ADO.NET を使用した単純なデータ アプリケーションの作成」を参照してください。

オブジェクトの要件

カスタム オブジェクトを Visual Studio のデータ デザイン ツールで操作するための唯一の要件は、オブジェクトに少なくとも 1 つのパブリック プロパティが必要であるということです。

一般に、カスタム オブジェクトでは、アプリケーションのデータ ソースとして機能する、特定のインターフェイス、コンストラクター、属性は必要ありません。 ただし、オブジェクトを [データ ソース] ウィンドウからデザイン サーフェイスにドラッグしてデータ バインド コントロールを作成する場合で、オブジェクトが ITypedList または IListSource インターフェイスを実装している場合は、オブジェクトに既定のコンストラクターが必要です。 これがない場合、Visual Studio はデータ ソース オブジェクトをインスタンス化できないので、デザイン サーフェイスに項目をドラッグするとエラーが表示されます。

カスタム オブジェクトをデータ ソースとして使用する例

オブジェクトをデータ ソースとして使用する場合、アプリケーション ロジックを実装する方法は数多くありますが、SQL データベースに関しては、Visual Studio で生成された TableAdapter オブジェクトを使用することで簡略化できる、いくつかの標準操作があります。 このページでは、TableAdapter を使ってこれらの標準プロセスを実装する方法について説明します。 カスタム オブジェクトを作成するためのガイドではありません。 たとえば、通常は、オブジェクトの特定の実装やアプリケーションのロジックに関係なく、次のような標準の操作を実行します。

  • オブジェクトにデータを読み込む (通常はデータベースから)。

  • オブジェクトの型指定されたコレクションを作成する。

  • コレクション内のオブジェクトの追加や削除を行う。

  • フォーム上でオブジェクト データをユーザー向けに表示する。

  • オブジェクト内のデータを変更/編集する。

  • オブジェクトのデータを取得元のデータベースに保存する。

オブジェクトにデータを読み込む

この例では、TableAdapter を使ってオブジェクトにデータを読み込みます。 既定では、TableAdapter は、データベースからデータをフェッチしてデータ テーブルに設定する、2 種類のメソッドを使って作成されます。

  • TableAdapter.Fill メソッドは、返されたデータを既存のデータ テーブルに格納します。

  • TableAdapter.GetData メソッドは、データが設定された新しいデータ テーブルを返します。

カスタム オブジェクトにデータを読み込む最も簡単な方法は、TableAdapter.GetData メソッドを呼び出し、返されたデータ テーブル内の行のコレクションをループ処理して、各オブジェクトに各行の値を設定するという方法です。 データが設定されたデータ テーブルを返す GetData メソッドは、TableAdapter に追加された任意のクエリに対して作成できます。

注意

Visual Studio では、TableAdapter クエリに既定で Fill および GetData という名前が指定されますが、これらの名前は任意の有効なメソッド名に変更することもできます。

次の例は、データ テーブル内の行をループ処理し、オブジェクトにデータを設定する方法を示したものです。

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);
    }
}

オブジェクトの型指定されたコレクションを作成する

オブジェクトのコレクション クラスを作成することも、BindingSource コンポーネントによって自動的に提供される、型指定されたコレクションを使用することもできます。

オブジェクトのカスタム コレクション クラスを作成する場合は、BindingList<T> から継承することをお勧めします。 このジェネリック クラスでは、コレクションを管理するための機能だけでなく、Windows フォームのデータ バインディング インフラストラクチャに通知を送るイベントを発生させる機能も提供されます。

BindingSource で自動的に生成されたコレクションでは、型指定されたコレクション用に BindingList<T> が使用されます。 アプリケーションで追加の機能が必要ない場合は、コレクションを BindingSource 内で保持することができます。 詳しくは、BindingSource クラスの List プロパティを参照してください。

注意

BindingList<T> の基本実装によって提供されない機能がコレクションに必要な場合は、カスタム コレクションを作成して、必要に応じてクラスに追加できるようにする必要があります。

次のコードは、Order オブジェクトの厳密に型指定されたコレクション用のクラスを作成する方法を示したものです。

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

コレクションにオブジェクトを追加する

コレクションにオブジェクトを追加するには、カスタム コレクション クラスまたは BindingSourceAdd メソッドを呼び出します。

注意

BindingList<T> から継承した場合、Add メソッドは、カスタム コレクションに対して自動的に提供されます。

次のコードは、BindingSource 内の型指定されたコレクションにオブジェクトを追加する方法を示したものです。

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

次のコードは、BindingList<T> から継承した型指定されたコレクションにオブジェクトを追加する方法を示したものです。

注意

この例では、Orders コレクションは Customer オブジェクトのプロパティです。

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

コレクションからオブジェクトを削除する

コレクションからオブジェクトを削除するには、カスタム コレクション クラスまたは BindingSource の、Remove または RemoveAt メソッドを呼び出します。

注意

BindingList<T> から継承した場合、Remove および RemoveAt メソッドは、カスタム コレクションに対して自動的に提供されます。

次のコードは、RemoveAt メソッドを使って、BindingSource 内の型指定されたコレクションからオブジェクトを検索し、削除する方法を示したものです。

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

オブジェクトのデータをユーザーに表示する

オブジェクトのデータをユーザーに表示するには、データ ソース構成ウィザードを使ってオブジェクト データ ソースを作成した後、オブジェクト全体または個々のプロパティを、 [データ ソース] ウィンドウからフォーム上にドラッグします。

オブジェクト内のデータを変更する

Windows フォーム コントロールにデータ バインドされているカスタム オブジェクト内のデータを編集するには、バインドされたコントロール内のデータを編集します (またはオブジェクトのプロパティ内で直接編集します)。 オブジェクト内のデータは、データ バインディング アーキテクチャによって更新されます。

アプリケーションで変更を追跡する必要がある場合で、提案された変更を元の値にロールバックする必要がある場合は、オブジェクト モデルにその機能を実装する必要があります。 データ テーブルで提案された変更がどのように追跡されるかの例については、DataRowStateHasChanges、および GetChanges に関する記事を参照してください。

オブジェクト内のデータを取得元のデータベースに保存する

データを取得元のデータベースに保存するには、オブジェクトから、TableAdapter の DBDirect メソッドに値を渡します。

データベースに対して直接実行できる DBDirect メソッドが、Visual Studio によって作成されます。 これらのメソッドでは、DataSet オブジェクトや DataTable オブジェクトは必要ありません。

TableAdapter DBDirect メソッド 説明
TableAdapter.Insert データベースに新しいレコードを追加します。これにより、個々の列の値をメソッド パラメーターとして渡せるようになります。
TableAdapter.Update データベースの既存のレコードを更新します。 Update メソッドは、元の列と新しい列の値をメソッドのパラメーターとして受け取ります。 元の値は元のレコードを検索するために使用され、新しい値はそのレコードを更新するために使用されます。

TableAdapter.Update メソッドは、DataSetDataTableDataRow、または DataRow の配列をメソッド パラメーターとして受け取り、データベースに戻されるデータセット内の変更を調整するためにも使用されます。
TableAdapter.Delete メソッド パラメーターとして渡された元の列の値に基づいて、データベースから既存のレコードを削除します。

オブジェクトのコレクションのデータを保存するには、オブジェクトのコレクションをループ処理します (たとえば、for-next ループを使用するなど)。 各オブジェクトの値をデータベースに送信するには、TableAdapter の DBDirect メソッドを使用します。

次の例は、TableAdapter.Insert DBDirect メソッドを使って、新規の顧客をデータベースに直接追加する方法を示したものです。

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);
}