SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

オブジェクトバインディングに関するヒントとテクニック

.NET Framework 2.0で強化されたデータバインディング機能

リストの管理

 アプリケーションのユーザーインターフェイスに、項目をリストしなければならないことはよくあります。例えば、すべての顧客をコンボボックスに表示して、その中から編集する顧客を選択する場合が考えられます。顧客の種類、状態、支払い予定のリストを表示することもあるでしょう。顧客が購入したすべての製品のリストをグリッドで表示する場合もあるかもしれません。

 最初に、適度なサイズの項目リストを実装する方法について見ていきましょう。このリストでは、項目のいくつかのプロパティを表示し、場合によっては編集する必要があります。ここでは、顧客が購入した項目のリストをグリッドで表示することにします。

 まずプロジェクトにPurchasedItemクラスを追加し、ItemNameDescriptionItemIDなどの適当なプロパティを設定します。CustomerクラスのCreateメソッドに似たCreateメソッドを実装しますが、今回のメソッドでは、IDではなくDataRowを渡します(このクラスの実装については、ダウンロードサンプルを参照してください)。

 プロジェクトにPurchasedItemCollectionクラスを追加し、このクラスをBindingList(Of T)から継承します。BindingList汎用コレクションは、System.ComponentModelにあることに注意してください。

VBの場合
Public Class PurchasedItemCollection
Inherits BindingList(Of PurchasedItem)
C#の場合
class PurchasedItemCollection :
   BindingList<PurchasedItem>

 Factoryパターンに従い、コレクションエントリを作成するCreateメソッドを実装します。

VBの場合
Public Shared Function Create(ByVal CustomerID _
   As Int32) As PurchasedItemCollection
   Dim oCollection As New PurchasedItemCollection
   Dim dt As DataTable
   ' Perform the appropriate retrieve
   dt = oCollection.Retrieve(CustomerID)
   ' For each row, create an object in the list
   For Each dr As DataRow In dt.Rows
      Dim oItem As PurchasedItem
      oItem = PurchasedItem.Create(dr)
      oCollection.Add(oItem)
   Next
   Return oCollection
End Function
C#の場合
public static PurchasedItemCollection Create(
   Int32 CustomerID)
{
   PurchasedItemCollection oCollection =
      new PurchasedItemCollection();
   // Perform the appropriate retrieve
   DataTable dt =
      oCollection.Retrieve(CustomerID);
   // For each row, create an object in the list
   foreach (DataRow dr in dt.Rows)
   {
      PurchasedItem oItem =
         PurchasedItem.Create(dr);
      oCollection.Add(oItem);
   }
   return oCollection;
}

 CustomerクラスのRetrieveメソッドに似たRetrieveメソッドを追加します(このメソッドの実装については、ダウンロードサンプルを参照してください)。

 両クラスの実装が完成したら、プロジェクトをビルドして、[Data Sources]ウィンドウから新しいクラスにアクセスできるようにします。次に、Customerクラスの場合と同じステップで、PurchasedItemCollectionクラスのデータソースを作成します。必ずDataGridViewを既定として設定し、PurchasedItemCollectionデータソースをフォームの下部にドラッグ&ドロップします。これにより、PurchasedItemクラスで定義した各プロパティを列として持つグリッドが表示されます。

 フォームのLoadイベントの中で、PurchasedItemCollectionBindingSourceのデータソースを、PurchasedItemCollectionクラスのインスタンスに設定します。

VBの場合
Private Sub CustomerWin_Load(ByVal sender As _
   Object, ByVal e As System.EventArgs) _
   Handles Me.Load
   Dim CustomerInstance As Customer
   CustomerInstance = Customer.Create(1)
   CustomerBindingSource.DataSource = _
      CustomerInstance
   Dim ItemCollection As PurchasedItemCollection
   ItemCollection = _
      PurchasedItemCollection.Create(1)
   PurchasedItemCollectionBindingSource. _
      DataSource = ItemCollection
End Sub
C#の場合
private void CustomerWin_Load(object sender,
   EventArgs e)
{
   Customer CustomerInstance =
      Customer.Create(1);
   customerBindingSource.DataSource =
      CustomerInstance;
   PurchasedItemCollection ItemCollection =
      PurchasedItemCollection.Create(1);
   purchasedItemCollectionBindingSource.
      DataSource = ItemCollection;
}

 BindingList(Of T)を継承するクラスへのオブジェクトバインディングは、項目のリストを表示および管理するのに非常に適しています。ただし、リスト内のすべての項目に対して、インスタンスが作成および設定されることに注意してください。この例のように、適度な数の項目であれば問題ありません。しかし、リスト内の項目数が数十万にも及ぶ場合は、適切に動作しない可能性があります。

 例えば編集対象の顧客を選択するための顧客名リストのように、大量の読み取り専用のキー値が必要な場合には、値ごとにインスタンスを作成するのは非効率的に感じられます。この場合は、キー値のDataTableに直接バインドしたいと思うかもしれません。しかし、この場合でも、ちょっとしたテクニックによって、一種のオブジェクトバインディングを引き続き使用することができます。

 そのためには、CustomerListクラスをプロジェクトに追加し、CustomerIDFullNameなど、キー値の名前に一致するプロパティを用意します。また、キー値のDataTableを返すCustomerDataTableプロパティも用意します。最後に、Retrieveメソッドを実装して、DataTableを取得します。リスト3およびリスト4は、結果のコードを示しています。

リスト3 オブジェクトバインディングを利用してDataTableとのバインドを実現するクラス(VB)
Public Class CustomerList
   Public ReadOnly Property FullName() As String
      Get
         Return ""
      End Get
   End Property
   Public ReadOnly Property CustomerID() As Int32
      Get
         Return 0
      End Get
   End Property
   Public ReadOnly Property CustomerDataTable() As DataTable
      Get
         Return Retrieve()
      End Get
   End Property
   Private Function Retrieve() As DataTable
      Dim dt As New DataTable
      dt.Columns.Add("FullName")
      dt.Columns.Add("CustomerID")
      dt.Rows.Add("Einstein, Albert", 1)
      dt.Rows.Add("Curie, Marie", 2)
      dt.Rows.Add("Brahe, Tycho", 3)
      dt.Rows.Add("Faraday, Michael", 4)
      Return dt
   End Function
End Class
リスト4 オブジェクトバインディングを利用してDataTableとのバインドを実現するクラス(C#)
class CustomerList
{
   public string FullName
   {
      get { return "";}
   }
   public Int32 CustomerID
   {
      get { return 0;}
   }
   public DataTable CustomerDataTable
   {
      get { return Retrieve();}
   }
   private DataTable Retrieve()
   {
      DataTable dt = new DataTable();
      dt.Columns.Add("FullName");
      dt.Columns.Add("CustomerID");
      dt.Rows.Add("Einstein, Albert", 1);
      dt.Rows.Add("Curie, Marie", 2);
      dt.Rows.Add("Brahe, Tycho", 3);
      dt.Rows.Add("Faraday, Michael", 4);
      return dt;
   }
}

 プロジェクトをビルドし、CustomerListクラスのデータソースを作成します。[Data Sources]ウィンドウ内で、FullNameプロパティをコンボボックスとして表示するように設定します。次に、FullNameプロパティをフォームにドラッグします。図2のように表示されます。

図2 コンボボックスを使って、より複雑なデータバインディングを実現する。
図2 コンボボックスを使って、より複雑なデータバインディングを実現する。

 コンボボックスのスマートタグをクリックして、コンボボックスのプロパティを設定します。DisplayMemberプロパティをFullNameに、ValueMemberプロパティをCustomerIDに設定します。

 最後に、ユーザーインターフェイスを修正して、コンボボックスに値を読み込むためのコードと、ユーザーがコンボボックスから顧客を選択したときに他のコントロールの内容を更新するためのコードを追加します(以前に作成したLoadイベントをコメントアウトするか、削除するのを忘れないようにしてください)。

VBの場合
Private Sub CustomerWin_Load(ByVal sender As _
   Object, ByVal e As System.EventArgs) _
   Handles Me.Load
   ' Bind the list
   Dim oList As New CustomerList
   CustomerListBindingSource.DataSource = _
      oList.CustomerDataTable
End Sub
Private Sub _
   FullNameComboBox_SelectionChangeCommitted( _
   ByVal sender As Object, _
   ByVal e As System.EventArgs) _
   Handles FullNameComboBox.SelectionChangeCommitted
   Dim oCustomer As Customer
   oCustomer = Customer.Create(
      FullNameComboBox.SelectedValue)
   CustomerBindingSource.DataSource = oCustomer
   Dim oCollection As PurchasedItemCollection
   oCollection = PurchasedItemCollection.Create( _
      FullNameComboBox.SelectedValue)
   PurchasedItemCollectionBindingSource. _
      DataSource = oCollection
End Sub
C#の場合
private void CustomerWin_Load(object sender,
   EventArgs e)
{
   CustomerList oList = new CustomerList();
   customerListBindingSource.DataSource =
      oList.CustomerDataTable;
}
private void
   fullNameComboBox_SelectionChangeCommitted(
      object sender, EventArgs e)
{
   Int32 selectedID = Convert.ToInt32(
      fullNameComboBox.SelectedValue);
   Customer oCustomer =
      Customer.Create(selectedID);
   customerBindingSource.DataSource =
      oCustomer;
   PurchasedItemCollection oCollection =
      PurchasedItemCollection.Create(selectedID);
   purchasedItemCollectionBindingSource.
      DataSource = oCollection;
}

 これは基本的に見せかけのオブジェクトバインディングで、オブジェクトにバインドしていると思わせていますが、実際のバインド先はDataTableです。この方法は、クラスのインスタンスは不要だが、どのデータソースでもオブジェクトバインディングを使用するように統一したい場合に、非常に効果的な方法です(データソースの混在を気にしない場合は、ここに示す「見せかけの」オブジェクトバインディングではなく、データベースバインディングを使ってTableAdapterに直接バインドすることもできます)。

 この例の場合、顧客が多数に及ぶ可能性があるため、DataTableへのバインドには意味があります。また、ワークフローの観点からは、ユーザーが一度に編集する顧客数はわずかであることが予想されます。従って、数十万もの個別のインスタンスを作成し、それぞれのインスタンスにバインドするのは無駄が多すぎます。

 この「見せかけのオブジェクトバインディング」の手法は、顧客の種類、状態、支払い予定などのリストが必要なときにも使えます。このようなケースでは、リストは常に読み取り専用であり、インスタンスを本当に作成して管理する必要はありません。

 例として、CustomerクラスにStateプロパティを追加してみましょう。プロジェクトをビルドすると、[Data Sources]ウィンドウにStateが追加されます。次に、本稿で作成したCustomerListクラスに似たStateListクラスをビルドします。このStateListクラスは、状態のリストを管理します。

 このようなリストを使用する場合は、コンボボックスの3つのプロパティを操作する必要があります。このプロパティはすべて、コンボボックスのスマートタグからアクセスできます。

プロパティ名説明
DisplayMemberコンボボックスに表示する値のソースとなるプロパティ。StateListクラスのプロパティを指定する必要があります("StateName"など)。
ValueMemberコンボボックスの選択値を表すときに使用するプロパティ。StateListクラスのプロパティを指定する必要があります("StateID"など)。
SelectedValueユーザーの選択値の代入先となるバインディングソースおよびプロパティ。Customerクラスのプロパティ(ひいてはCustomerBindingListのプロパティ)を指定する必要があります("State"など)。

 このように、すべての型コードに対してクラスとインスタンスを作成しなくても、完全な機能を備えたオブジェクトバンディングを実現できます。

ユーザーインターフェイスに関する注意

 オブジェクトバインディングに関連して、ユーザーインターフェイスにはいくつか注意を要することがあります。例えば、ラジオボタンにバインドしたり、親プロパティを子データのグリッドに追加する簡単な方法はありません。最後に、この点に触れておきたいと思います。

 ラジオボタンにバインドするには、「RadioButtonList」というようなコントロールを使用するのが最も簡単です。しかし、Windows Formsにはこのようなコントロールがありません。一連のラジオボタンにバインドするには、ラジオボタンごとに個々のビジネスオブジェクトプロパティを作成しなければなりません。

 例えば、顧客が請求書を受け取る方法(電子メール、FAX、郵送のいずれか)を定義する一連のラジオボタンがあるものとします。これらにバインドするには、3つの個別のブール型のビジネスオブジェクトプロパティ(E-mail、FAX、Postal)を作成し、各プロパティを個々のラジオボタンにバインドする必要があります。各プロパティのプロパティプロシージャでは、このプロパティがtrueの場合は他のプロパティ値をfalseに設定するコードを追加します(この実装については、ダウンロードサンプルを参照してください)。

 ユーザーインターフェイスに関する2番目の注意事項は、親情報を子データのグリッドに簡単に表示できないということです。例えば、すべての請求書をリストするユーザーインターフェイスを作成するときに、図2のCustomerフォームと同様のグリッドを配置する一方で、他の詳細情報はフォームにいっさい表示しないというデザインにしたとします。このグリッドをユーザーが実用的に使うためには、関連するCustomerインスタンスからCustomerの名前を表示する必要があります。

 そのためには、PurchasedItemビジネスオブジェクトから親オブジェクトCustomerを参照して、Customerインスタンスを作成するのが一番分かりやすいでしょう。この参照は可能であり、[Data Sources]ウィンドウにもCustomerインスタンスが表示されていますが、グリッドからそのCustomerインスタンスにアクセスすることはできません。

 これに対処するには、PurchasedItemビジネスオブジェクトにCustomerNameプロパティを追加するしかありません。このプロパティのプロパティプロシージャで、関連するCustomerの顧客名を取得します。具体的な例については、ダウンロードサンプルを参照してください。

 オブジェクトバインディングは、ユーザーインターフェイス上のコントロールをビジネスオブジェクトプロパティに結び付ける、非常に論理的なメカニズムを提供します。ここで紹介したようなヒントとテクニックを実装することによって、オブジェクトバインディングを大いに活用できます。

 オブジェクトバインディングは完全ではありませんが、この世に完全なものなどないのですから、どんどん使ってみることをお勧めします。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Deborah Kurata(Deborah Kurata)

ビジネスの構想をMicrosoft .NETテクノロジで実現することに力を注ぐコンサルティング会社InStep Technologies Inc.の共同設立者。優れた.NETアプリケーションの設計、デザイン、開発に15年以上従事。『Doing Objects in Visual Basic 6.0』...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/383 2006/08/22 15:46

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー