SHOEISHA iD

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

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

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

japan.internet.com翻訳記事

Visual Studio 2005のフォームにおけるBindingNavigatorの拡張

BindingNavigatorによるデータアクセスアプリケーションの開発効率アップ

ルックアップリストの追加

 最後の問題は、「問題」というよりは「利便性を高めるための事柄」です。筆者の見解では、データ入力フォームでは大抵の場合、特定レコードをルックアップする手段が必要です。通常、このためにはコンボボックスの形式を使い、IDを値として、また、一意のテキストを表示番号として使用します。SelectedValueChangedイベントでは、BindingSourceの位置を選択したレコードの位置へと変更することができます。

 ナビゲーションバーにコンボボックスを追加するのは、いくぶん難しくなります。このコンボボックスはバインドできず、しかもこれはただのドロップダウンリストボックスであり、値メンバを持ちません。この不足を補うため、本稿ではハッシュテーブルを使って値メンバを格納します。

 ナビゲーションバーにコンボボックスを追加するには、次の手順を実行します。

  1. ユーザーコントロールのデザインビューのバーの右端にあるアイコンは、追加可能な項目を表示するドロップダウンリストです。まず[Label]を選ぶと、「ToolStripLabel1」という名前のラベルがバーに追加されます。
  2. この名前を「Lookup」に変更します(また、この値のプロパティを作成しておき、開発者がデザイン時に変更できるようにしておく必要があります)。
  3. ドロップダウンリストを再びクリックし、[Combo Box]コントロールを追加して、「ToolStripComboBox1」という項目を新規に追加します。
  4. このユーザーコントロールにプロパティをもう1つ追加して、開発者がどのフィールドを表示メンバにするかを選択できるようにします。
  5. Private _DisplayMember As String
    Public Property DisplayMember() As String
        Get
            Return _DisplayMember
        End Get
        Set(ByVal value As String)
            _DisplayMember = value
        End Set
    End Property
    
  6. このプロパティの既定値を、バウンドテーブル内の最初のテキスト型フィールドにします。そのためには次のコードをbs_DataSourceChangedメソッドに追加します。
  7. If Not _DataTable Is Nothing Then
        'find the first text column
        For Each col As DataColumn In _DataTable.Columns
            If col.GetType().Equals(Type.GetType("System.String")) Then
                _DisplayMember = col.ColumnName
                Exit For
            End If
        Next
    End If
    
  8. ハッシュテーブルを定義し、コンボボックスに適切な値を読み込むためのBuildLookupListメソッドを追加します。
  9. Private ht As New System.Collections.Hashtable
    Public Sub BuildLookupList()
        If Not _DisplayMember Is Nothing Then
            'fill both lookup box and hash table with values
            ToolStripComboBox1.Items.Clear()
            ht.Clear()
            If (BindingSource.List.Count > 0) Then
                'get the primary key as value member (assumes single field key)
                Dim _valueMember As String = _DataTable.PrimaryKey(0).ColumnName
                'temp change the sort of the binding source
                Dim tempSort As String = _BindingSource.Sort
                BindingSource.Sort = _DisplayMember + " ASC"
                'step through the records in the binding source as filtered
                For Each drv As DataRowView In _BindingSource
                    If Not drv(_DisplayMember) Is Nothing Then
                        ToolStripComboBox1.Items.Add(drv(_DisplayMember))
                        Try
                            ht.Add(drv(_DisplayMember), drv(_valueMember))
                        Catch 'ignore dups
                        End Try
                    End If
                Next
                'restore sort field
                BindingSource.Sort = tempSort
            End If
        End If
    End Sub
    
  10. このメソッドは、リストが変更されたときに呼び出される必要があります。BindingSourceメソッドのハンドラセクションにハンドラを1つ追加し、さらにもう1つ、「イベントを処理して上記のメソッドを呼び出すためのメソッド」を追加します。
  11. AddHandler _BindingSource.ListChanged, AddressOf bs_ListChanged
    
    Private Sub bs_ListChanged(ByVal sender As Object, _
        ByVal e As System.ComponentModel.ListChangedEventArgs)
    
        BuildLookupList()
    End Sub
    
  12. バインディングソースがリレーションに基づいている場合は、リストの内容は親の位置が変わるたびに変更されます。そのため、親バインディングソースを追跡するプロパティと、それを呼び出すメソッドを新たに追加する必要があります(この処理もListChangedハンドラで行いたくなるかもしれませんが、イベントの引数がそれぞれ違うという点に注意してください)。
  13. Private WithEvents _ParentBindingSource As BindingSource
    Public Property ParentBindingSource() As BindingSource
        Get
            Return _ParentBindingSource
        End Get
        Set(ByVal value As BindingSource)
            _ParentBindingSource = value
            If Not _ParentBindingSource Is Nothing Then
                AddHandler _BindingSource.PositionChanged, _
                    AddressOf parentBS_PositionChanged
            End If
        End Set
    End Property
    
    Private Sub parentBS_PositionChanged(ByVal sender As Object, _
        ByVal e As EventArgs)
    
        BuildLookupList()
    End Sub
    
  14. 親の位置が変わった場合に対処する、もう1つのイベントハンドラを追加します。
  15. Private Sub parentBS_PositionChanged(ByVal sender As Object, ByVal e As EventArgs)
        BuildLookupList()
    End Sub
    
  16. 開発者は、親バインディングソースのプロパティを設定することができます、そして、次のソースをbs_DataSourceChangedメソッドに追加することで、データテーブルにプロパティを割り当てるときにキャプチャできるようになります。
  17. 'if child BS, get reference to parent BS
    Dim testBS As BindingSource = TryCast(_BindingSource.DataSource, BindingSource)
    If Not testBS Is Nothing Then
        ParentBindingSource = testBS 'call the getter to subcribe to events
    End If
    
  18. この時点でフォームを実行するとリストは作成されますが、ユーザーが何かを選んでも何も起こりません。ユーザーがコンボボックスリストから項目を選ぶ際のイベントをキャプチャする必要があります。コンボボックスコントロールを選び(場合により、この作業は非常に厄介です。ラベルを選択して[Tab]キーを押してください)、次にプロパティパネルでイベントのセクションに移動(上部の稲妻アイコンをクリック)します。それから、[SelectedIndexChanged]イベントをクリックして、コードスタブを生成します。次のコードを追加してください。
  19. Private Sub ToolStripComboBox1_SelectedIndexChanged(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles ToolStripComboBox1.SelectedIndexChanged
    
        If (ToolStripComboBox1.Focused And ToolStripComboBox1.Text <> "") Then
            'get the primary key field
            Dim _valueMember As String = _DataTable.PrimaryKey(0).ColumnName
            'set the position by finding the primary key in the binding source
            _BindingSource.Position = BindingSource.Find(_valueMember, _
                ht(ToolStripComboBox1.Text))
        End If
    End Sub
    
  20. 最後の問題は、「レコードの選択後に[Next]または[Previous]ボタンを使って位置を移動すると、コンボボックス内のエントリがレコードの表示と同期しなくなる」ということです。次のメソッドを使えば、リストとレコードの同期をとることができます。
  21. 'make sure the same record shows in the combo box as is in the record
    Private Sub syncLookupCombobox()
        If (ToolStripComboBox1.Items.Count > 0 And _BindingSource.Position >= 0) Then
            'get the display string for the current record in the binding source
            Dim lookup As String = CType(_BindingSource.Current, _
                DataRowView).Row(_DisplayMember).ToString()
            If (lookup.Length > 0) Then
                ToolStripComboBox1.SelectedIndex = _
                    ToolStripComboBox1.FindStringExact(lookup)
            End If
        End If
    End Sub
    
  22. このメソッドがPositionChangedイベントハンドラから呼び出されるようにします。
  23. 最後に、コンパイルしてフォームを実行してみましょう。フォームの背後に何もコードを書かなくても、すべてが問題なく機能します。

 このユーザーコントロールをさらに拡張して、フォーム用の他のCRUD型イベント(例えばユーザーがデータを編集するまでコントロールを無効にするなど)を実現することができます。新しいコントロールを実装するには、ウィザードによって配置されたBindingNavigatorを削除し、[Toolbox]パネルを開きます。すると、今回作成したユーザーコントロールがWinセクションに配置されています。このユーザーコントロールをフォームにドラッグし、BindingSourceプロパティに適切な値を指定します。

 親子関係を持つフォームでも、さまざまなナビゲーション機能を使用することができました。これを実際に試してみるには、次のようにします。

  1. まず、パネルコントロールをフォームの右側にドラッグします。
  2. [Data Sources]パネルを開いて「Customer」テーブルを展開し、子リレーションシップテーブルの[Orders]を選びます。このテーブルを、パネルにドラッグします。ウィザードによって、もう1つのバインディングソースとグリッドが追加されます。
  3. 必要に応じて、パネルとグリッドのサイズを変更します。グリッドよりも上の位置にあるパネルでは、少し余白を残してください。
  4. もう1つのexBindingNavigatorユーザーコントロールをパネルにドラッグします。このユーザーコントロールは最上部にドッキングします。
  5. ナビゲータのBindingSourceプロパティを、新しく「OrdersBindingSource」にセットします。

 これでフォームが実行できるようになり、ナビゲーションも以前と変わらず動作します。

結論

 BindingNavigatorコンポーネントは一度ユーザーコンポーネントにして、そこにプロパティとメソッドを追加するという方法で簡単に拡張できます。このユーザーコントロールをフォームに追加すると、データテーブルへのオートフィルや更新機能(ユーザーへの確認メッセージ付き)が実装されるほか、特定レコードのルックアップができるようになります。拡張したユーザーコントロールをコントロールライブラリプロジェクトに格納すると、任意のソリューションに追加できるようになり、データアクセスアプリケーションの開発速度がアップします。

 データアクセスロジックにインターフェイスを実装すると、データセット内の各テーブルで必要となるメソッドのスタブをすばやく作成できますが、それでも一部のコードは書く必要があります。ただ、これは非常に構造化されたコードなので、CodeDomやサードパーティ製のコード生成システムで簡単に生成できます。

 この方法論には、いくつかの問題がついて回ります(その大部分は、Visual Studio 2005自身の問題です)。ユーザーコントロールをフォームに追加した後、そのユーザーコントロールに何らかの変更を加えても、Visual Studio内でフォームが自動的にリロードされることはありません。フォームを一度閉じてから開き直し、デザインモードで表示する必要があります。ときには、フォームを正確に表示するためにプロジェクトをリビルドしなければならないこともあります。そのため、ユーザーコントロールをフォームで使用するのは、ユーザーコントロールを完全に作成し終えてからにしてください。

 この記事のコードを、ダウンロードサンプルとしてご用意してあります。このダウンロードファイルには、Northwindデータベースを使ったVBとC#の両方のサンプルが収められています。

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

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

もっと読む

この記事の著者

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

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

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

David Catherman(David Catherman)

CMI Solutions所属。データベースアプリケーションのデザイン/開発に20年以上の経験を持ち、ここ4、5年は特にMicrosoft .NETとSQL Serverに仕事が集中している。現在はCMI Solutionsのアプリケーション設計者および上級開発者であり、Visual Studioと...

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

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

この記事をシェア

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

イベント

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

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

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

メールバックナンバー