トップ回答者
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(バージョンはいくつでもかまいません)では
どのように実現するのが望ましいのか、ご教授下さると幸いです。
回答
-
質問内容がよく分かりませんのでハズレかもしれませんが・・・
> 初期表示時は子行が表示されていますが、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
-
.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/
- 編集済み trapemiyaModerator 2013年5月11日 2:32 追記
- 回答としてマーク 児玉 2013年5月11日 3:39
-
> DataGridView2.DataMember = DataTable1.TableName + Relation1.RelationName;
「= DataTable1.TableName + "." + Relation1.RelationName;」にしなくて良いのかな…。
> 大概DataViewを使え、とあると思います。
DataView で絞り込むのではなく、リレーションを管理する BindingSource を用意するのが良いと思います。
BindingSource での具体例は SurferOnWww さんが紹介されているサイトを見ていただくとして、おおむね、以下のような流れとなります。- 型付DataSetに、親テーブルと子テーブルを用意。
- 両者にリレーションを張る。
- FormにBindingSourceを用意し、DataSourceに1の親テーブルのインスタンスをセット。
- FormにBindingSourceを用意し、DataSourceに3のBindingSourceを、DataMemberに2のリレーション名を。
- FormにDataGridViewを用意し、DataSourceに3のBindingSourceを。(親レコード用)
- FormにDataGridViewを用意し、DataSourceに4のBindingSourceを。(子レコード用)ひ
- 必要に応じて、これらの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
すべての返信
-
質問内容がよく分かりませんのでハズレかもしれませんが・・・
> 初期表示時は子行が表示されていますが、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
-
.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/
- 編集済み trapemiyaModerator 2013年5月11日 2:32 追記
- 回答としてマーク 児玉 2013年5月11日 3:39
-
> DataGridView2.DataMember = DataTable1.TableName + Relation1.RelationName;
「= DataTable1.TableName + "." + Relation1.RelationName;」にしなくて良いのかな…。
> 大概DataViewを使え、とあると思います。
DataView で絞り込むのではなく、リレーションを管理する BindingSource を用意するのが良いと思います。
BindingSource での具体例は SurferOnWww さんが紹介されているサイトを見ていただくとして、おおむね、以下のような流れとなります。- 型付DataSetに、親テーブルと子テーブルを用意。
- 両者にリレーションを張る。
- FormにBindingSourceを用意し、DataSourceに1の親テーブルのインスタンスをセット。
- FormにBindingSourceを用意し、DataSourceに3のBindingSourceを、DataMemberに2のリレーション名を。
- FormにDataGridViewを用意し、DataSourceに3のBindingSourceを。(親レコード用)
- FormにDataGridViewを用意し、DataSourceに4のBindingSourceを。(子レコード用)ひ
- 必要に応じて、これらの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