SHOEISHA iD

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

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

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

japan.internet.com翻訳記事

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

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

BindingNavigatorコントロールの拡張

 ナビゲーションツールバーの問題を解決して機能を拡張する方法の1つは、BindingNavigatorコントロールをベースとしたユーザーコントロールを作成し、必要なプロパティとメソッドを追加することです。

  1. Windowsプロジェクトを右クリックし、[Add]-[New Item]をクリックします 項目のリストから[User Control]を選び、「exBindingNavigator.vb」と名前を付けます(この拡張を他のアプリケーションでも使いたい場合は、ユーザーコントロールとカスタムコントロールを独立したプロジェクトに配置する必要があります。今回のサンプルでは、Windowsプロジェクトの中にそのまま残します)。
  2. 空のコンテナを持った空白のコントロールが作成されます。右下の角を、BindingNavigatorツールバーと同じ大きさになるまでドラッグします。
  3. [Customer]フォーム(あるいはデータソースウィザードがナビゲーションコントロールの作成に利用した任意のフォーム)に戻り、ナビゲーションツールバーをコピーします。
  4. ユーザーコントロールに戻り、ナビゲーションバーをコンテナに貼り付けます。
  5. プロパティボックスで、Navigatorの名前を「CustomerBindingNavigator」から「GenericBindingNavigator」などの汎用的な名前に変更します。
  6. 保存ボタン(フロッピーディスクのアイコン)をクリックし、プロパティパネルで名前をより汎用的な「BindingNavigatorSaveItem」に変更します。
  7. フォーム上で右クリックし、[View Code]をクリックしてコードページを開きます。Sub Newコンストラクタを追加し、コンストラクタ内でDockプロパティをフォームの上端にセットします。
  8. Public Class exBindingNavigator
        Public Sub New()
            ' This call is required by the Windows Form Designer.
            InitializeComponent()
    
            ' Add any initialization after the InitializeComponent() call.
            Me.Dock = DockStyle.Top
        End Sub
    End Class
    
  9. まず必要なのは、ナビゲーションバーのバインディングソースの参照を追跡するプロパティです。バインディングソースを定義するときに必要な作業の1つは、基となるテーブルへの参照を定義することです。ここで、そのテーブルへの参照を取得するコードを含めることもできます。ただし場合により、バインディングソースが定義されないうちはデータリソースも定義されないか、あるいはデータソースが変更される可能性があります。そのため、DataSourceChangedイベントを処理するイベントハンドラを追加して、テーブル参照をセットするメソッドを呼び出します。テーブルに自動的にデータを読み込むために、格納元のフォームへの参照を取得し、Form Loadイベントをサブスクライブします。
  10. Private WithEvents _BindingSource As BindingSource
    Public Property BindingSource() As BindingSource
        Get
            Return _BindingSource
        End Get
        Set(ByVal value As BindingSource)
            GenericBindingNavigator.BindingSource = value
            _BindingSource = value
            If Not _BindingSource Is Nothing Then
                'subscribe to the events in case not yet set
                AddHandler _BindingSource.DataSourceChanged, _
                    AddressOf bs_DataSourceChanged
                'get a reference to the table now
                bs_DataSourceChanged(New Object, New EventArgs)
            End If
        End Set
    End Property
    
  11. 上のDataSourceChangedイベントを処理するメソッドを追加します。
  12. Private Sub bs_DataSourceChanged(ByVal sender As Object, _
                                     ByVal e As EventArgs)
        If Not _BindingSource Is Nothing Then
            _DataTable = GetTableFromBindingSource( _
                GenericBindingNavigator.BindingSource)
            If Not _DataTable Is Nothing Then
                'if child BS, get reference to parent BS
                Dim testBS As BindingSource = _
                    TryCast(GenericBindingNavigator.BindingSource.DataSource, _
                            BindingSource)
                If Not testBS Is Nothing Then
                   'call the getter to capture event 
                    ParentBindingSource = testBS
                End If
            End If
        End If
    End Sub
    
    上記のような形でBindingSourceからテーブル参照を取得するには、もう1つのメソッドが必要です。BindingSourceにはDataSourceDataMenberという2つのプロパティがあり、それによってどのテーブルにバインドされるかが決まります。大抵の場合、DataSourceにはデータセットのインスタンスを指定し、DataMemberにはデータセット内のテーブルの名前を指定します。しかし、親子関係が確立されている状況では、DataSourceにもう1つのBindingSourceを指定し、DataMemberにリレーションの名前を指定することができます。この場合、テーブルを導き出すには、少し計算が必要です。
  13. 次に、GetTableFromBindingSourceというメソッドを追加します。このメソッドは、BindingSourceをパラメータとして受け取り、データテーブルへの参照を戻します。
  14. Public Function GetTableFromBindingSource(ByVal bs As BindingSource)
        'get a reference to the dataset
        Dim ds As DataSet, dt As DataTable
        'try to cast the data source as a binding source
        Dim bsTest As BindingSource = bs
        Do While Not TryCast(bsTest.DataSource, BindingSource) Is Nothing
            'if cast was successful, walk up the chain until dataset is reached
            bsTest = CType(bsTest.DataSource, BindingSource)
        Loop
        'since it is no longer a binding source, it must be a dataset
        If TryCast(bsTest.DataSource, DataSet) Is Nothing Then
        'Cast as dataset did not work
            Throw New ApplicationException("Invalid Binding Source ")
        End If
        ds = CType(bsTest.DataSource, DataSet)
        'check to see if the Data Member is the name of a table in the dataset
        If ds.Tables(bs.DataMember) Is Nothing Then
            'it must be a relationship instead of a table
            Dim rel As System.Data.DataRelation = ds.Relations(bs.DataMember)
            If Not rel Is Nothing Then
                dt = rel.ChildTable
            Else
                Throw New ApplicationException("Invalid Data Member")
            End If
        Else
            dt = ds.Tables(bs.DataMember)
        End If
        If TryCast(dt, ITableUpdate) Is Nothing Then
            Throw New ApplicationException("Table " & dt.TableName & _
                " does not implement ITableUpdate interface")
        End If
        Return dt
    End Function
    
    テーブルを取得するには、まずデータセットへの参照が必要です。DataSourceがBindingSourceである場合は、基になるデータセットを取得できる時点まで道筋をさかのぼっていく必要があります。ひとたびデータセットを取得すれば、DataMemberがデータセット内のテーブルを指しているかどうかを確認できます。テーブルでない場合は、DataMemberはリレーションを指しています。データセット内の一連のリレーションのなかから該当するリレーションを探し、そのリレーションのChildTableプロパティからテーブル参照を取得することができます。
    テーブルへの参照を取得した後は、ポリモーフィズムを利用して、この参照を先ほど作成したインターフェイスにキャストすることができます。正しくキャストされない場合は、そのデータテーブルがインターフェイスを正しく実装していないということであり、例外をスローする必要があります。キャストされる場合は、汎用的な更新ルーチンを呼び出してデータを保存することができます。
  15. 以下のメソッドをexBindingNavigatorクラスに追加して、保存ボタンのクリックイベントを処理します(保存ボタンをダブルクリックして、コードスタブを生成することもできます)。
  16. Private Sub SaveItem_Click(ByVal sender As System.Object, _
                               ByVal e As System.EventArgs)
        Me.Validate()
        _BindingSource.EndEdit()
        'cast table as ITableUpdate to get the Update method
        CType(_DataTable, _Interface.ITableUpdate).Update()
        IsDataDirty = False
    End Sub
    
  17. Form Loadイベントを処理するメソッドをもう1つ追加すると、フォームが開いたときにテーブルに自動的に値を読み込むことができます。テーブルは大抵の場合、必要な場合にロジックを使って値を読み込むことが多いので、この処理は必須というわけではありません。そのため、この機能を有効にするかどうかを判別するプロパティを追加して、有効/無効を開発者が選択できるようにします。
  18. Private _AutoFillFlag As Boolean = True
    Public Property AutoFillFlag() As Boolean
        Get
            Return _AutoFillFlag
        End Get
        Set(ByVal value As Boolean)
            _AutoFillFlag = value
        End Set
    End Property
    
    Private Sub Form_Load(ByVal sender As Object, ByVal e As EventArgs)
        If _AutoFillFlag Then
            'cast table as ITableUpdate to get the Fill method
            CType(_DataTable, _Interface.ITableUpdate).Fill()
        End If
    End Sub
    
  19. 最後に、親フォームのLoadイベントをサブスクライブすることによって、これらすべてが実行されるようにします。ユーザーコントロールのLoadイベントが起こると、親フォームへの参照が取得できるようになります。
  20. Private Sub exBindingNavigator_Load(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load
        'get the reference to the hosting form
        Dim frm As Object = CType(Me, ContainerControl).ParentForm
        While TryCast(frm, System.Windows.Forms.Form) Is Nothing
            'if not a form, walk up chain
            If Not TryCast(frm, System.ComponentModel.Container) Is Nothing Then
                frm = CType(frm, ContainerControl).Parent
            Else
                frm = CType(frm, Control).Parent
            End If
        End While
        _Form = CType(frm, System.Windows.Forms.Form)
        'add the handler for the Form Load to fill the table
        AddHandler _Form.Load, AddressOf Form_Load
    End Sub
    

 このユーザーコントロールをフォームに追加し、BindingSourceプロパティをセットすると、保存ボタンはプログラムで使用するあらゆるデータセットとプロパティセットを扱えるようになり、テーブルに自動的に値が読み込まれるようになります(インターフェイスを実装している限り)。これで、1つ目の問題は解決です。

次のページ
自動保存(AutoSave)機能の実装

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

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」など、さまざまなカンファレンスを企画・運営しています。

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

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

メールバックナンバー