none
DataTableをLINQしてDataGridViewにバインドしたときの、列の指定 RRS feed

  • 質問

  • 失礼します。別で質問していた件の、続きとなります。

    「ID」「NAME」の2つの列をもつDataTableに対しLINQでデータを抽出し、DataGridViewのDataSourceにバインドし、表示させます。

    この部分はアドバイスいただき、できました。

    Dim filterDt = From row In dt.AsEnumerable Where row("ID") >= 4

    DataGridView1.DataSource = filterDt.CopyToDataTable

    今回質問するのは、実際にDataGridViewに表示させる列を指定するのは、です。

    いろいろ調べると、LINQの際にSELECT句を使うんじゃないかな、と思いましたが、以下のようになってしまいます。

    Dim filterDt = From row In dt.AsEnumerable Where row("ID") >= "4" Select row("NAME")

    エラー 1 'CopyToDataTable' は 'System.Data.EnumerableRowCollection(Of Object)' のメンバーではありません。

    ・・・

    どうすれば、表示させる列を指定できるのでしょうか?(またはできない??)

    2012年5月31日 14:11

回答

  • Select row("NAME")でNAMEを返すようにした時点で、DataRowを返さなくなりますから、CopyToDataTableは使用できません。DataGridViewにバインドすることを考慮して、代わりにToListを使います。
    しかし、まだこれだけではダメです。最終的にToListはstring型の配列を返します。これをDataGridViewにバインドした場合、DataGridViewは最初のパブリックプロパティにバインドしようとしますから、string型配列の最初のパブリックプロパティであるLengthにバインドします。結果的にDataGridViewにはLengthが表示されてしまします。

    これを防ぐにはToListの結果がstring配列にならないようにすることです。よって、以下のように匿名型でラップして返すようにすればうまく行きます。

    Dim filterDt = From row In dt.AsEnumerable Where row("ID") >= 4 Select New With {.NAMEという列名にするよ= row("NAME")}
    DataGridView1.DataSource = filterDt.ToList()

    ちなみに、以下のように2列以上を指定すると勝手に匿名型でラップしてくれますから、以下でうまく行きます。

    Dim filterDt = From row In dt.AsEnumerable Where row("ID") >= 4 Select IDという列名にするよ = row("ID"), Nameという列名にするよ = row("Name")


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

    • 回答の候補に設定 山本春海 2012年6月22日 4:59
    • 回答としてマーク 山本春海 2012年6月28日 8:34
    2012年6月1日 1:56
    モデレータ
  • DataGridView1にバインドされているのはListなので、DataTableには代入できません。
    何らかの方法で変換しても良いでしょうが、予めfilterDtをコピーしたインスタンスを用意しておく方が楽かな、と思います。
    • 回答としてマーク 山本春海 2012年6月28日 8:34
    2012年6月1日 3:00
  • 少々力技ですが、こんなのも有りかなと・・・

    Dim filterDt = From row In dt.AsEnumerable Where row("ID") >= 4

    Dim dt2 As DataTable = filterDt.CopyToDataTable()
    dt2.Columns.Remove("不要列名") 'ここで不要な列を全て1列ずつ削除する。

    DataGridView1.DataSource = dt2

    • 回答としてマーク 山本春海 2012年6月28日 8:34
    2012年6月1日 4:48
  • Listではなく、あくまでDataTableとして扱いたいということですね。

    こっちも力技になりますが、ListからDataTableへの変換メソッド定義して利用するっててもあります。

    http://www.extensionmethod.net/Details.aspx?ID=249

    • 回答としてマーク 山本春海 2012年6月28日 8:34
    2012年6月1日 10:28
  • SQLっぽく単純にはいかないんですね。。。列を削除する案も検討したいと思います。

    おそらく、以下のスレッドの続きですよね?

    DataTableをLINQしてDataGridViewのDataSourceに設定
    http://social.msdn.microsoft.com/Forums/ja-JP/vbexpressja/thread/6c98b6d3-aca6-418e-a874-081b032df404

    上のスレッドで絞り込み、DataGridViewにバインドしているのはDataTableです。ここから表示したい列だけDataGridViewに表示するのであれば、DataGridViewのAutoGenerateColumnsプロパティをFalseにし、表示したい列のみ指定すれば可能です。これであれば、DataGridViewのDataSourceにはDataTableがバインドしていますから、そのままでDataTableを取得することができます。

    (参考)
    DataGridViewに列を手動で追加する
    http://dobon.net/vb/dotnet/datagridview/addcolumn.html

    LINQの質問はピンポイントであり、それに対する回答が得られたとしても、最終的にどのようなことをされたいのかがわからないので、全体として適切なアドバイスになっていないかもしれません。この辺りを整理されて、あまりスレッドのタイトルとかけ離れた話題が続くようであれば、一旦このスレッドを閉めて、新たにスレッドを開くようにお願いします。


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

    2012年6月2日 6:10
    モデレータ

すべての返信

  • Select row("NAME")でNAMEを返すようにした時点で、DataRowを返さなくなりますから、CopyToDataTableは使用できません。DataGridViewにバインドすることを考慮して、代わりにToListを使います。
    しかし、まだこれだけではダメです。最終的にToListはstring型の配列を返します。これをDataGridViewにバインドした場合、DataGridViewは最初のパブリックプロパティにバインドしようとしますから、string型配列の最初のパブリックプロパティであるLengthにバインドします。結果的にDataGridViewにはLengthが表示されてしまします。

    これを防ぐにはToListの結果がstring配列にならないようにすることです。よって、以下のように匿名型でラップして返すようにすればうまく行きます。

    Dim filterDt = From row In dt.AsEnumerable Where row("ID") >= 4 Select New With {.NAMEという列名にするよ= row("NAME")}
    DataGridView1.DataSource = filterDt.ToList()

    ちなみに、以下のように2列以上を指定すると勝手に匿名型でラップしてくれますから、以下でうまく行きます。

    Dim filterDt = From row In dt.AsEnumerable Where row("ID") >= 4 Select IDという列名にするよ = row("ID"), Nameという列名にするよ = row("Name")


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

    • 回答の候補に設定 山本春海 2012年6月22日 4:59
    • 回答としてマーク 山本春海 2012年6月28日 8:34
    2012年6月1日 1:56
    モデレータ
  • ありがとうございます!ご指摘の方法で、表示できました。

    が、いただいたソースをコピペしただけでは成長しません。「匿名型」・・・。勉強します。

    さて、ついでなのですが、やりたいことには続きがあります。これは今まで以上に愚問かもしれませんが・・・、

    DataGridView1.DataSource = filterDt.ToList()

    でバインドしたDataGridView1の「表示されているデータ」を別のDataTable(=dt2)にコピーしたいと考えています。で、以下を書いたのですが、

    Dim dt2 As DataTable
    dt2= DataGridView1.DataSource

    型 'VB$AnonymousType_0`2[System.Object,System.Object][]' のオブジェクトを型 'System.Data.DataTable' にキャストできません。

    となってしまいます。エラーになる理由はなんとなくわかるような気がします、が、はっきりとはわかりません。。。

    List型?をDataTableに変換しないといけないのかな。。。
    2012年6月1日 2:33
  • DataGridView1にバインドされているのはListなので、DataTableには代入できません。
    何らかの方法で変換しても良いでしょうが、予めfilterDtをコピーしたインスタンスを用意しておく方が楽かな、と思います。
    • 回答としてマーク 山本春海 2012年6月28日 8:34
    2012年6月1日 3:00
  • 少々力技ですが、こんなのも有りかなと・・・

    Dim filterDt = From row In dt.AsEnumerable Where row("ID") >= 4

    Dim dt2 As DataTable = filterDt.CopyToDataTable()
    dt2.Columns.Remove("不要列名") 'ここで不要な列を全て1列ずつ削除する。

    DataGridView1.DataSource = dt2

    • 回答としてマーク 山本春海 2012年6月28日 8:34
    2012年6月1日 4:48
  • みなさん、ありがとうございます。

    SQLっぽく単純にはいかないんですね。。。列を削除する案も検討したいと思います。

    で、もはやLINQではないんですが、DataTableのSelectメソッドで、絞ることは可能なんでしょうか?

    とあるサイトに

    ---

    DataTable.Selectメソッドを用いて
    Select("ID, name,…")として取得したDataRow配列をDataTableにセットすることで

    ---

    というのがありました。ためしに

    dt.Select("ID,NAME")

    としてみたんですが、「式に構文エラーがあります」とでてしまいます。。。

    SQLのSELECTとは違う?

    2012年6月1日 6:59
  • SQLのSELECTとは違います。

    filterExpression パラメータは、SELECT句というよりWHERE句です。

    ※厳密には同じ構文は使えないので別物ですが・・・

    2012年6月1日 7:32
  • datagridviewにて表示したくない列のvisibleをfalseにするだけでは、不足でしょうか?

    DataGridView1.Columns("表示したくない列").Visible = False


    • 編集済み nplaceazure 2012年6月1日 10:30 誤字
    2012年6月1日 10:21
  • Listではなく、あくまでDataTableとして扱いたいということですね。

    こっちも力技になりますが、ListからDataTableへの変換メソッド定義して利用するっててもあります。

    http://www.extensionmethod.net/Details.aspx?ID=249

    • 回答としてマーク 山本春海 2012年6月28日 8:34
    2012年6月1日 10:28
  • SQLっぽく単純にはいかないんですね。。。列を削除する案も検討したいと思います。

    おそらく、以下のスレッドの続きですよね?

    DataTableをLINQしてDataGridViewのDataSourceに設定
    http://social.msdn.microsoft.com/Forums/ja-JP/vbexpressja/thread/6c98b6d3-aca6-418e-a874-081b032df404

    上のスレッドで絞り込み、DataGridViewにバインドしているのはDataTableです。ここから表示したい列だけDataGridViewに表示するのであれば、DataGridViewのAutoGenerateColumnsプロパティをFalseにし、表示したい列のみ指定すれば可能です。これであれば、DataGridViewのDataSourceにはDataTableがバインドしていますから、そのままでDataTableを取得することができます。

    (参考)
    DataGridViewに列を手動で追加する
    http://dobon.net/vb/dotnet/datagridview/addcolumn.html

    LINQの質問はピンポイントであり、それに対する回答が得られたとしても、最終的にどのようなことをされたいのかがわからないので、全体として適切なアドバイスになっていないかもしれません。この辺りを整理されて、あまりスレッドのタイトルとかけ離れた話題が続くようであれば、一旦このスレッドを閉めて、新たにスレッドを開くようにお願いします。


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

    2012年6月2日 6:10
    モデレータ