C# と VB.NET の質問掲示板

わんくま同盟

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

ツリー一括表示

多階層のxmlのDataTableへの読み書きについて /なと (17/05/17(Wed) 18:28) #84124
Re[1]: 多階層のxmlのDataTableへの読み書きについて /WebSurfer (17/05/17(Wed) 18:54) #84125
Re[1]: 多階層のxmlのDataTableへの読み書きについて /魔界の仮面弁士 (17/05/17(Wed) 19:47) #84126
  └ Re[2]: 多階層のxmlのDataTableへの読み書きについて /なと (17/05/17(Wed) 20:24) #84127
    └ Re[3]: 多階層のxmlのDataTableへの読み書きについて /魔界の仮面弁士 (17/05/17(Wed) 20:46) #84128
      └ Re[4]: 多階層のxmlのDataTableへの読み書きについて /なと (17/05/18(Thu) 21:40) #84132
        └ Re[5]: 多階層のxmlのDataTableへの読み書きについて /魔界の仮面弁士 (17/05/18(Thu) 23:54) #84134
          └ Re[6]: 多階層のxmlのDataTableへの読み書きについて /なと (17/05/19(Fri) 09:44) #84135 解決済み


親記事 / ▼[ 84125 ] ▼[ 84126 ]
■84124 / 親階層)  多階層のxmlのDataTableへの読み書きについて
□投稿者/ なと (5回)-(2017/05/17(Wed) 18:28:05)

分類:[C#] 

下記のような感じで、親→子→孫のツリー構造のXMLデータを作って、
親テーブルで名前をダブルクリックしたら子のテーブルが出てきて、
子テーブルで名前をクリックしたらその子の孫テーブルが出て来る
ように実装したいと考えております。(テーブルはDataGridViewで表示)

ネットの海ではDataTableのXML読み書きはDataSet.ReadXml()を使った方法が多いようで、
このままだと<Grandsons>〜</Grandsons>部分にしか使えませんよね。

xmlファイルをバラバラにしてしまう方法とXPathかXmlSerializer
を使ってDataTableへ変換するコードを書く案を考えているのですが
このような場合に良い方法があればアドバイスを頂けませんか。

<Parent>
 <Name>親1</Name>
 <Age>70</Age>
 <Childs>
  <Child>
   <Name>子1</Name>
   <Age>35</Age>
   <Grandsons>
    <Grandson><Name>孫1</Name><Age>10</Age></Grandson>
    <Grandson><Name>孫2</Name><Age>8</Age></Grandson>
   </Grandsons>
  </Child>
  <Child>
   <Name>子2</Name>
   <Age>30</Age>
   <Grandsons>
    <Grandson><Name>孫1</Name><Age>5</Age></Grandson>
    <Grandson><Name>孫2</Name><Age>4</Age></Grandson>
   </Grandsons>
  </Child>
 </Childs>
</Parent>

以上、よろしくお願い致します。

[ □ Tree ] 返信 編集キー/

▲[ 84124 ] / 返信無し
■84125 / 1階層)  Re[1]: 多階層のxmlのDataTableへの読み書きについて
□投稿者/ WebSurfer (1238回)-(2017/05/17(Wed) 18:54:26)
No84124 (なと さん) に返信

DataSet.ReadXml() を使えるように xml ファイルの構造を見直す、それがダメなら XDcoument
を使う・・・というのは見当違いですか?
[ 親 84124 / □ Tree ] 返信 編集キー/

▲[ 84124 ] / ▼[ 84127 ]
■84126 / 1階層)  Re[1]: 多階層のxmlのDataTableへの読み書きについて
□投稿者/ 魔界の仮面弁士 (1283回)-(2017/05/17(Wed) 19:47:18)
No84124 (なと さん) に返信
> ネットの海ではDataTableのXML読み書きはDataSet.ReadXml()を使った方法が多いようで、
> このままだと<Grandsons>〜</Grandsons>部分にしか使えませんよね。

ReadXml メソッドで、子階層も含めて読み込まれるはずですよ。


private DataSet ds = new DataSet();
private DataGrid grid = new DataGrid();
private void Form1_Load(object sender, EventArgs e)
{
  ds.ReadXml(@"C:\temp\test.xml");
  grid.Dock = DockStyle.Fill;
  grid.DataSource = ds;
  grid.DataMember = "Parent";
  Controls.Add(grid);
}


> (テーブルはDataGridViewで表示)
リレーションを繋ぐのが面倒だったので、
上記では DataGrid を使っていますが、
もちろん DataGridView で実装しても良いと思います。
DataSet の中身に差がでるわけではないですし。
[ 親 84124 / □ Tree ] 返信 編集キー/

▲[ 84126 ] / ▼[ 84128 ]
■84127 / 2階層)  Re[2]: 多階層のxmlのDataTableへの読み書きについて
□投稿者/ なと (6回)-(2017/05/17(Wed) 20:24:03)
No84126 (魔界の仮面弁士 さん) に返信
> ReadXml メソッドで、子階層も含めて読み込まれるはずですよ。
DataGridViewだとParentしか読まなかったので、気が付きませんでした!

DataMember = "Grandson";とかに変えれば子とか孫も読み込むんですね。
しかしながら、子1の孫1,2と、子2の1,2全部読み込まれてしまいました。

> リレーションを繋ぐのが面倒だったので
ds.Relations.Addを使えばいいのかな・・・ちょっと調べてみます。
[ 親 84124 / □ Tree ] 返信 編集キー/

▲[ 84127 ] / ▼[ 84132 ]
■84128 / 3階層)  Re[3]: 多階層のxmlのDataTableへの読み書きについて
□投稿者/ 魔界の仮面弁士 (1284回)-(2017/05/17(Wed) 20:46:16)
2017/05/18(Thu) 12:07:08 編集(投稿者)

No84127 (なと さん) に返信
> DataGridViewだとParentしか読まなかったので、気が付きませんでした!

.DataSource = ds;
.DataMember = tableName;

の代わりに、

.DataSource = ds.Tables[tableName];

でも OK です。これは DataGridView であっても同じことです。


ただ、階層問い合わせにするのであれば、
DataSet/DataTable を DataSource に直接バインドするのではなく、
BindingSource 経由で渡した方が良いかもしれません。


>>リレーションを繋ぐのが面倒だったので
> ds.Relations.Addを使えばいいのかな・・・ちょっと調べてみます。

ReadXml を呼び出した時点で、リレーションは自動的に貼られています。


『ds.Tables["Parent"].Rows[0].GetChildRows("Parent_Childs")[0]』とすれば
『ds.Tables["Childs"].Rows[0]』を指し示します。
これは <Childs> のタグに相当します。(Children ではないのですね)


『ds.Tables["Childs"].Rows[0].GetChildRows("Childs_Child")[0]』なら、
『ds.Tables["Child"].Rows[0]』(すなわち 35歳の子1 の行)です。


もしも DataGridView に表示させるのであれば、
それぞれの DataGridViewRow から DataBoundItem プロパティ経由で
DataRowView オブジェクトが得られるので、その Row プロパティから、
該当の DataRow オブジェクトが得られるようになっています。

DataRow さえ得られれば、あとは上記と同様に、
GetChildRows メソッドあるいは GetParentRow/GetParentRows メソッドで
階層を辿ることができるでしょう。
[ 親 84124 / □ Tree ] 返信 編集キー/

▲[ 84128 ] / ▼[ 84134 ]
■84132 / 4階層)  Re[4]: 多階層のxmlのDataTableへの読み書きについて
□投稿者/ なと (7回)-(2017/05/18(Thu) 21:40:02)
No84128 (魔界の仮面弁士 さん) に返信

頂いた情報を元に、いろいろ検索して勉強中です・・
仰る通り複数形はChildrenでしたね(笑)

XMLから読んだDataSetをBindingSourceとDataMemberに割り当てるときのプロパティがよく分からず、
ウォッチでプロパティ漁っていますが中々当たりません。取り敢えず、BindingSourceは置いといて

Parentはそのまま
bsp = ds.Tables[tableName];
dgvp.DataSurce = bsp;
dgvp.DataMember = "Parent";
ですよね。

Childは
ds.Tables["Parent"].Rows[0].GetChildRows("Parent_Children")[0]をそのままDataSurceに入れても関係のない列が出てきてしまったので
DataTable tblChild = ds.Tables["Parent"].Rows[0].GetChildRows("Parent_Children")[0].GetChildRows("Children_Child")[0].Table;
dgvc.DataSource = tblChild ;
としたらそれっぽいのが出ました。

しかし実際には
DataTable tblGrand = tblChild.Rows[0].GetChildRows("Child_Grandsons")[0].GetChildRows("Grandsons_Grandson")[0].Table;
dgvg.DataSource = tblGrand ;
とすると孫1,孫2、孫1、孫2と出て来てしまいました。

なにがダメなんでしょう・・・?


検索していたら過去に魔界の仮面弁士さんが書かれているコレが目指している動きと同じような気がします。
https://social.msdn.microsoft.com/Forums/ja-JP/7440f652-65c3-499c-aacc-4fb6684a6d15/dataviewdatagridviewdatagridview?forum=csharpgeneralja
とりあえずコレをXML化すれば緒が見つかるかなと・・・頑張ってみます。

[ 親 84124 / □ Tree ] 返信 編集キー/

▲[ 84132 ] / ▼[ 84135 ]
■84134 / 5階層)  Re[5]: 多階層のxmlのDataTableへの読み書きについて
□投稿者/ 魔界の仮面弁士 (1286回)-(2017/05/18(Thu) 23:54:03)
No84132 (なと さん) に返信
> Parentはそのまま
> bsp = ds.Tables[tableName];
> dgvp.DataSurce = bsp;
> dgvp.DataMember = "Parent";
> ですよね。

違います。
それだと『フィールド Parent の子リストを作成できません。』などのエラーとなる可能性があります。

上記の dgvp というのは、Parent 用の DataGridView を
指しているのだと思いますが(DataSurce → DataSource というのはさておき)、
Parent 行を DataGridView に表示するだけなら、
  // DataMember は空のまま
  dataGridView1.DataSource = ds.Tables["Parent"];
もしくは
  dataGridView1.DataSource = ds;
  dataGridView1.DataMember = "Parent" ;
です。



> XMLから読んだDataSetをBindingSourceとDataMemberに割り当てるときのプロパティがよく分からず、

とりあえずこんな感じ。空のフォームに貼って試してみてください。
ここでは説明のため、コントロールの配置やイベントの割り当ても、サンプルコード中で行っています。
(実際の開発には、コントロール等をデザイン時に配置しておくことができます)

親・子・孫という 3 つの DataGridView を並べてありますが、それぞれは
BindingSource を通じてリレーションが貼られているため、
「子1」を選択すれば、長男の子供達(10歳と8歳)が自動的に表示され、
「子2」を選択すれば、次男の子供達(5歳と4歳)が自動的に表示されます。


public partial class Form1 : Form
{
  private DataSet ds = new DataSet("Sample");
  private DataGridView dgvParent, dgvChild, dgvGrandson;

  private void Form1_Load(object sender, EventArgs e)
  {
    Sample();
  }

  private void Sample()
  {
    ds.Clear();
    ds.ReadXml(@"C:\temp\test.xml");

    // 各 DataGridView とのバインドはデザイン時に終わらせてあるので、
    // ReadXml した後で、DataSource を再割り当てする必要はありません。
    //
    // しかしながら、型指定のない「素の DataSet」の場合には、
    // テーブル情報やリレーション情報が無いため、
    // DataMember の割り当ては、ReadXml 後に行う必要があります。
    // (事前に割り当ててしまうと、メンバー名が見つからずエラーになります)
    //
    bndParent.DataMember = "Parent";
    bndChildren.DataMember = "Parent_Childs";
    bndChild.DataMember = "Childs_Child";
    bndGrandsons.DataMember = "Child_Grandsons";
    bndGrandson.DataMember = "Grandsons_Grandson";
    //
    // ただし、DataSet デザイナを用いて「型指定された DataSet」をあらかじめ用意しておき、
    // それをデザイン時に bndParent.DataSource に割り当てていた場合には、
    // 各種 DataMember の割り当てさえもデザイン時に済ませておくことができます。
    //
  }


  public Form1()
  {
    InitializeComponent();

    #region コントロールの配置(実際はフォームデザイナで配置)
    // Load イベントの割り当てです。
    this.Load += Form1_Load;

    // フォームに BindingSource を貼ると、自動的に components がセットされるのですが
    // 今回はデザイン時処理もコードで書いていますので、下記では手動でセットしています。
    if(components == null)
    {
      components = new System.ComponentModel.Container();
    }

    // リレーションごとに BindingSource を用意しておきます。
    bndParent = new BindingSource(components);
    bndChildren = new BindingSource(components);
    bndChild = new BindingSource(components);
    bndGrandsons = new BindingSource(components);
    bndGrandson = new BindingSource(components);

    bndParent.DataSource = ds;
    bndChildren.DataSource = bndParent;
    bndChild.DataSource = bndChildren;
    bndGrandsons.DataSource = bndChild;
    bndGrandson.DataSource = bndGrandsons;

    // 下記では、親・子・孫の DataGridView を縦に3つ並べて配置しています。
    // また、フォームのリサイズに合わせて大きさが変わるよう、
    // それらを TableLayoutPanel の上に配置させています。
    //
    var panel = new TableLayoutPanel() { Dock = DockStyle.Fill, ColumnCount = 1, RowCount = 3 };
    dgvParent = new DataGridView() { Dock = DockStyle.Fill, AllowUserToAddRows = false };
    dgvChild = new DataGridView() { Dock = DockStyle.Fill, AllowUserToAddRows = false };
    dgvGrandson = new DataGridView() { Dock = DockStyle.Fill, AllowUserToAddRows = false };
    panel.RowStyles.Add(new RowStyle(SizeType.Percent, 100F / 3F));
    panel.RowStyles.Add(new RowStyle(SizeType.Percent, 100F / 3F));
    panel.RowStyles.Add(new RowStyle(SizeType.Percent, 100F / 3F));
    panel.Controls.Add(dgvParent);
    panel.Controls.Add(dgvChild);
    panel.Controls.Add(dgvGrandson);
    Controls.Add(panel);

    // データバインドの設定を行います。
    // ここでは DataSource のみを設定していますが、元データに型付き DataSet を指定してある場合は、
    // DataMember プロパティも事前に指定しておくことができます。
    //
    dgvParent.DataSource = bndParent;
    dgvChild.DataSource = bndChild;
    dgvGrandson.DataSource = bndGrandson;

    StartPosition = FormStartPosition.WindowsDefaultBounds;
    #endregion
  }
  private BindingSource bndParent;
  private BindingSource bndChildren, bndGrandsons;
  private BindingSource bndChild, bndGrandson;
}
[ 親 84124 / □ Tree ] 返信 編集キー/

▲[ 84134 ] / 返信無し
■84135 / 6階層)  Re[6]: 多階層のxmlのDataTableへの読み書きについて
□投稿者/ なと (8回)-(2017/05/19(Fri) 09:44:51)
No84134 (魔界の仮面弁士 さん) に返信

コード全体を書いて頂きありがとうございます!

BindingSourceはそのように使うのですね。
DataSourceに続けてDataMember書いてはいけないとは・・自力でやっていたら一生解決しなかったかもしれません。

頂いたコードを改良して、親を多数にした場合や、他のタグも含めた状態で読み書きすることも出来るようになりました。

また詰まった時はよろしくお願い致します。
解決済み
[ 親 84124 / □ Tree ] 返信 編集キー/


管理者用

- Child Tree -