none
DataViewをDataGridViewにバインドした際、子行をバインドしているDataGridViewにも連携を行うには

    質問

  • Datasetに属するDataTableを2つ用いてそれぞれDataGridView2つにバインドしております。

    DataTable間にDataRelationを構築し、
    DataGridView1.DataSource = Dataset1;
    DataGridView1.DataMember = DataTable1.TableName;
    DataGridView2.DataSource = Dataset1;
    DataGridView2.DataMember = DataTable1.TableName + Relation1.RelationName;

    ....と言った形でバインドしてやると、
    フォーム操作時に、DataGridView1でRowの選択を変更すると、きちんとその子行がDataGridView2に反映されると思います。

    うまい言い回しが全く思いつかなかったので、説明後の本題なのですが、

    DataGridViewを絞り込み表示したい時は、大概DataViewを使え、とあると思います。
    ところが、上記のコードを
    DataGridView1.DataSource = DataView1;
    // DataGridView1.DataMember = DataTable1.TableName;
    とすると、
    初期表示時は子行が表示されていますが、DataGridView1のカレント行を変更しても、対応した子行をDataGridView2は表示してくれません。

    方法はいくらでもあるかと思いますが、そもそも.NetFramework(バージョンはいくつでもかまいません)では
    どのように実現するのが望ましいのか、ご教授下さると幸いです。

    2013年5月10日 18:06

回答

  • 質問内容がよく分かりませんのでハズレかもしれませんが・・・

    > 初期表示時は子行が表示されていますが、DataGridView1のカレント
    > 行を変更しても、対応した子行をDataGridView2は表示してくれません。

    例えば、以下のページのチュートリアルの図1のように、リレーションを持った 2 つのテーブルを Form に表示し、Customers の選択を変更した時に、それに該当する Orders の内容を表示できればいいのでしょうか?

    10 行でズバリ !! 非接続型のデータ アクセス (ADO.NET) (C#)
    http://code.msdn.microsoft.com/windowsdesktop/10-ADONET-C-cbfe7688

    であれば、上記のチュートリアルどおりに作って、自動生成されるコードがどうなっているか見てください。

    それとはやりたいことが違うのであれば、もっと詳しく、例えば上記のチュートリアルとは具体的にどう違うのかなど、やりたいことを説明してください。

    • 回答としてマーク 児玉 2013年5月11日 3:49
    2013年5月11日 1:06
  • .NEt Framework 2.0以降であれば、BindingSourceコンポーネントを使うのがお薦めでしょう。以下のページを参考にしてみて下さい。

    チュートリアル : Windows フォームの 2 つの DataGridView コントロールを使用したマスター/詳細形式のフォームの作成
    http://msdn.microsoft.com/ja-jp/library/y8c0cxey.aspx#Y385

    (追記)
    絞り込みは、BindingSourceのFilterプロパティで行えるはずです。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/


    2013年5月11日 2:31
    モデレータ
  • > DataGridView2.DataMember = DataTable1.TableName + Relation1.RelationName;

    「= DataTable1.TableName + "." + Relation1.RelationName;」にしなくて良いのかな…。

    > 大概DataViewを使え、とあると思います。

    DataView で絞り込むのではなく、リレーションを管理する BindingSource を用意するのが良いと思います。
    BindingSource での具体例は SurferOnWww さんが紹介されているサイトを見ていただくとして、おおむね、以下のような流れとなります。

    1. 型付DataSetに、親テーブルと子テーブルを用意。
    2. 両者にリレーションを張る。
    3. FormにBindingSourceを用意し、DataSourceに1の親テーブルのインスタンスをセット。
    4. FormにBindingSourceを用意し、DataSourceに3のBindingSourceを、DataMemberに2のリレーション名を。
    5. FormにDataGridViewを用意し、DataSourceに3のBindingSourceを。(親レコード用)
    6. FormにDataGridViewを用意し、DataSourceに4のBindingSourceを。(子レコード用)ひ
    7. 必要に応じて、これらのBindingSourceのFilterに絞り込み条件や、Sortに並び順を記述する。

    もちろん、型付DataSetでなくとも構いません。たとえばこんな感じ。
    デザイナコードも含めてあるので、フォームに何も置かない状態で張り付けてみてください。

    public partial class Form1 : Form
    {
        DataGridView dgv0, dgv1, dgv2;
        DataSet ds;
        BindingSource bs0, bs1, bs2;
        public Form1()
        {
            InitializeComponent();
    
            // 画面部品
            dgv0 = new DataGridView();  // 全子レコード表示
            dgv1 = new DataGridView();  // 親レコード表示
            dgv2 = new DataGridView();  // 子レコード表示
            #region この部分は本題とは無関係
            dgv0.TopLeftHeaderCell.Value = "全";
            dgv1.TopLeftHeaderCell.Value = "親";
            dgv2.TopLeftHeaderCell.Value = "子";
            dgv0.BackgroundColor = Color.Pink;
            dgv1.BackgroundColor = Color.LightGreen;
            dgv2.BackgroundColor = Color.LightSalmon;
            dgv0.EditMode = dgv1.EditMode = dgv2.EditMode = DataGridViewEditMode.EditOnEnter;
            SplitContainer scNS = new SplitContainer();
            SplitContainer scWE = new SplitContainer();
            dgv0.Dock = dgv1.Dock = dgv2.Dock = DockStyle.Fill;
            scNS.Dock = scWE.Dock = DockStyle.Fill;
            scNS.Orientation = Orientation.Horizontal;
            scWE.Orientation = Orientation.Vertical;
            Controls.Add(scNS);
            scNS.Panel1.Controls.Add(dgv0);
            scNS.Panel2.Controls.Add(scWE);
            scWE.Panel1.Controls.Add(dgv1);
            scWE.Panel2.Controls.Add(dgv2);
            scNS.SplitterDistance = scNS.Height / 2;
            scWE.SplitterDistance = scWE.Width / 2;
            #endregion
    
    
            // データセット
            ds = new DataSet("Sample");
            #region Sampleデータセット
            DataTable tblP = ds.Tables.Add("親");
            tblP.PrimaryKey = new DataColumn[] {
                tblP.Columns.Add("Id", typeof(int))
            };
            tblP.Columns.Add("Name");
            tblP.Rows.Add(100, "AAAAA");
            tblP.Rows.Add(200, "BBBBB");
            tblP.Rows.Add(300, "CCCCC");
            tblP.Rows.Add(400, "DDDDD");
            
            DataTable tblC = ds.Tables.Add("子");
            tblC.PrimaryKey = new DataColumn[] {
                tblC.Columns.Add("Id", typeof(int)),
                tblC.Columns.Add("SubId", typeof(int))
            };
            tblC.Columns.Add("Name");
            tblC.Rows.Add(100, 11, "A1");
            tblC.Rows.Add(100, 12, "A2");
            tblC.Rows.Add(100, 13, "A3");
            tblC.Rows.Add(200, 21, "B1");
            tblC.Rows.Add(200, 22, "B2");
            tblC.Rows.Add(400, 41, "D1");
            tblC.Rows.Add(400, 42, "D2");
            tblC.Rows.Add(400, 43, "D3");
            tblC.Rows.Add(400, 44, "D4");
            #endregion
    
            // リレーション
            ds.Relations.Add("親子", tblP.Columns["ID"], tblC.Columns["ID"]);
            tblP.Columns.Add("子数", typeof(int), "COUNT(Child.ID)"); // ←無くても良い
    
            // データバインドの中継器
            #region BindingSourceの設定
            bs0 = new BindingSource();
            bs1 = new BindingSource();
            bs2 = new BindingSource();
            bs0.DataSource = ds;
            bs1.DataSource = bs0;
            bs2.DataSource = bs1;
            bs0.DataMember = "親";
            bs2.DataMember = "親子";
            #endregion
    
            // 親を表示(選択すると子レコードが連動表示)
            dgv1.DataSource = bs1;
    
            // 親に連動して子を表示
            dgv2.DataSource = bs2;
    
            // 全ての子を表示
            dgv0.DataSource = tblC;
    
            // 親を表示(選択しても子レコードは連動しない)
            // dgv0.DataSource = tblP;
    
            // 必要に応じて、各BindingSourceに条件をセット
            bs1.Filter = "ID >= 250";
         }
    }
    • 編集済み 魔界の仮面弁士MVP 2013年5月11日 3:44 肝心の絞り込みの件を書き忘れていたので追記
    • 回答としてマーク 児玉 2013年5月11日 3:49
    2013年5月11日 3:28

すべての返信

  • 質問内容がよく分かりませんのでハズレかもしれませんが・・・

    > 初期表示時は子行が表示されていますが、DataGridView1のカレント
    > 行を変更しても、対応した子行をDataGridView2は表示してくれません。

    例えば、以下のページのチュートリアルの図1のように、リレーションを持った 2 つのテーブルを Form に表示し、Customers の選択を変更した時に、それに該当する Orders の内容を表示できればいいのでしょうか?

    10 行でズバリ !! 非接続型のデータ アクセス (ADO.NET) (C#)
    http://code.msdn.microsoft.com/windowsdesktop/10-ADONET-C-cbfe7688

    であれば、上記のチュートリアルどおりに作って、自動生成されるコードがどうなっているか見てください。

    それとはやりたいことが違うのであれば、もっと詳しく、例えば上記のチュートリアルとは具体的にどう違うのかなど、やりたいことを説明してください。

    • 回答としてマーク 児玉 2013年5月11日 3:49
    2013年5月11日 1:06
  • .NEt Framework 2.0以降であれば、BindingSourceコンポーネントを使うのがお薦めでしょう。以下のページを参考にしてみて下さい。

    チュートリアル : Windows フォームの 2 つの DataGridView コントロールを使用したマスター/詳細形式のフォームの作成
    http://msdn.microsoft.com/ja-jp/library/y8c0cxey.aspx#Y385

    (追記)
    絞り込みは、BindingSourceのFilterプロパティで行えるはずです。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/


    2013年5月11日 2:31
    モデレータ
  • > DataGridView2.DataMember = DataTable1.TableName + Relation1.RelationName;

    「= DataTable1.TableName + "." + Relation1.RelationName;」にしなくて良いのかな…。

    > 大概DataViewを使え、とあると思います。

    DataView で絞り込むのではなく、リレーションを管理する BindingSource を用意するのが良いと思います。
    BindingSource での具体例は SurferOnWww さんが紹介されているサイトを見ていただくとして、おおむね、以下のような流れとなります。

    1. 型付DataSetに、親テーブルと子テーブルを用意。
    2. 両者にリレーションを張る。
    3. FormにBindingSourceを用意し、DataSourceに1の親テーブルのインスタンスをセット。
    4. FormにBindingSourceを用意し、DataSourceに3のBindingSourceを、DataMemberに2のリレーション名を。
    5. FormにDataGridViewを用意し、DataSourceに3のBindingSourceを。(親レコード用)
    6. FormにDataGridViewを用意し、DataSourceに4のBindingSourceを。(子レコード用)ひ
    7. 必要に応じて、これらのBindingSourceのFilterに絞り込み条件や、Sortに並び順を記述する。

    もちろん、型付DataSetでなくとも構いません。たとえばこんな感じ。
    デザイナコードも含めてあるので、フォームに何も置かない状態で張り付けてみてください。

    public partial class Form1 : Form
    {
        DataGridView dgv0, dgv1, dgv2;
        DataSet ds;
        BindingSource bs0, bs1, bs2;
        public Form1()
        {
            InitializeComponent();
    
            // 画面部品
            dgv0 = new DataGridView();  // 全子レコード表示
            dgv1 = new DataGridView();  // 親レコード表示
            dgv2 = new DataGridView();  // 子レコード表示
            #region この部分は本題とは無関係
            dgv0.TopLeftHeaderCell.Value = "全";
            dgv1.TopLeftHeaderCell.Value = "親";
            dgv2.TopLeftHeaderCell.Value = "子";
            dgv0.BackgroundColor = Color.Pink;
            dgv1.BackgroundColor = Color.LightGreen;
            dgv2.BackgroundColor = Color.LightSalmon;
            dgv0.EditMode = dgv1.EditMode = dgv2.EditMode = DataGridViewEditMode.EditOnEnter;
            SplitContainer scNS = new SplitContainer();
            SplitContainer scWE = new SplitContainer();
            dgv0.Dock = dgv1.Dock = dgv2.Dock = DockStyle.Fill;
            scNS.Dock = scWE.Dock = DockStyle.Fill;
            scNS.Orientation = Orientation.Horizontal;
            scWE.Orientation = Orientation.Vertical;
            Controls.Add(scNS);
            scNS.Panel1.Controls.Add(dgv0);
            scNS.Panel2.Controls.Add(scWE);
            scWE.Panel1.Controls.Add(dgv1);
            scWE.Panel2.Controls.Add(dgv2);
            scNS.SplitterDistance = scNS.Height / 2;
            scWE.SplitterDistance = scWE.Width / 2;
            #endregion
    
    
            // データセット
            ds = new DataSet("Sample");
            #region Sampleデータセット
            DataTable tblP = ds.Tables.Add("親");
            tblP.PrimaryKey = new DataColumn[] {
                tblP.Columns.Add("Id", typeof(int))
            };
            tblP.Columns.Add("Name");
            tblP.Rows.Add(100, "AAAAA");
            tblP.Rows.Add(200, "BBBBB");
            tblP.Rows.Add(300, "CCCCC");
            tblP.Rows.Add(400, "DDDDD");
            
            DataTable tblC = ds.Tables.Add("子");
            tblC.PrimaryKey = new DataColumn[] {
                tblC.Columns.Add("Id", typeof(int)),
                tblC.Columns.Add("SubId", typeof(int))
            };
            tblC.Columns.Add("Name");
            tblC.Rows.Add(100, 11, "A1");
            tblC.Rows.Add(100, 12, "A2");
            tblC.Rows.Add(100, 13, "A3");
            tblC.Rows.Add(200, 21, "B1");
            tblC.Rows.Add(200, 22, "B2");
            tblC.Rows.Add(400, 41, "D1");
            tblC.Rows.Add(400, 42, "D2");
            tblC.Rows.Add(400, 43, "D3");
            tblC.Rows.Add(400, 44, "D4");
            #endregion
    
            // リレーション
            ds.Relations.Add("親子", tblP.Columns["ID"], tblC.Columns["ID"]);
            tblP.Columns.Add("子数", typeof(int), "COUNT(Child.ID)"); // ←無くても良い
    
            // データバインドの中継器
            #region BindingSourceの設定
            bs0 = new BindingSource();
            bs1 = new BindingSource();
            bs2 = new BindingSource();
            bs0.DataSource = ds;
            bs1.DataSource = bs0;
            bs2.DataSource = bs1;
            bs0.DataMember = "親";
            bs2.DataMember = "親子";
            #endregion
    
            // 親を表示(選択すると子レコードが連動表示)
            dgv1.DataSource = bs1;
    
            // 親に連動して子を表示
            dgv2.DataSource = bs2;
    
            // 全ての子を表示
            dgv0.DataSource = tblC;
    
            // 親を表示(選択しても子レコードは連動しない)
            // dgv0.DataSource = tblP;
    
            // 必要に応じて、各BindingSourceに条件をセット
            bs1.Filter = "ID >= 250";
         }
    }
    • 編集済み 魔界の仮面弁士MVP 2013年5月11日 3:44 肝心の絞り込みの件を書き忘れていたので追記
    • 回答としてマーク 児玉 2013年5月11日 3:49
    2013年5月11日 3:28
  • 皆様、ありがとうございます。

    取り急ぎ、BindingSourceでコーディングして見ましたところ、確かにFilterで絞っても問題なく連携することが確認出来ました。
    なるほどDataSourceにBindingSourceをつなげていくわけですね。

    どうもありがとうございました。

    2013年5月11日 3:49