【C#】DataGridViewにDataTable反映したり変換して取得したりする

みなさんこんにちは!ひろぽんです!!

つい先日後輩にDataGridViewでDataTableを取得するのってどうすればいいんですか??と聞かれまして、ん?普通にDatasourceからとればよくない?と思ったものです。

ですが、おそらくC#初心者の方はDataGridViewにDataTableをどのように反映させるのか?

また表示されているデータをDataTableに変換して取得するにはどうすれば良いのか?

という点で悩んでしまう方も多いのではないでしょうか?

ということで、今回は「DataGridViewにDataTable反映したり変換して取得したりする」と題しDataGridViewとDataTableの扱いについていろいろ書いていこうかと思います。

[s_ad]

目次

まずは簡単にFormを作ります

Formロード時には左にデータを表示するようにします。

その後真ん中のボタンを押せば右のDataGridViewに左のデータが移るようにする感じでつくりました。

DataGridViewにDataTableを反映させるには?

💡 DataGridView の DataSource を後から差し替えたいケースは C# DataGridView の DataSource を後から変更する全パターン でまとめてます。バインディング後の更新タイミング問題まで踏み込んでます。

では次にDataGridViewにDataTableを反映させていきましょう

まずはDataTableを定義します。

DataTableの定義

IDと氏名と年齢と住所という列が存在するテーブルを定義します。

        private DataTable getDataTable()
        {
            var dt = new DataTable();
            var idCol = dt.Columns.Add("ID");
            var nameCol = dt.Columns.Add("氏名");
            var ageCol = dt.Columns.Add("年齢");
            var addressCol = dt.Columns.Add("住所");

        var newRow = dt.NewRow();
        newRow.SetField(idCol,1);
        newRow.SetField(nameCol, "鈴木 おさむ");
        newRow.SetField(ageCol, 22);
        newRow.SetField(addressCol, "東京都世田谷区");
        dt.Rows.Add(newRow);

        var newRow2 = dt.NewRow();
        newRow2.SetField(idCol, 2);
        newRow2.SetField(nameCol, "高橋 つよし");
        newRow2.SetField(ageCol, 25);
        newRow2.SetField(addressCol, "東京都葛飾区");
        dt.Rows.Add(newRow2);

        return dt;
    }</code></pre></div>

DataGridViewのDataSourceに反映させる

反映させるのは1行で出来ます。

        private void Form1_Load(object sender, EventArgs e)
        {
            dataGridView1.DataSource = getDataTable();
        }

これで実行するとこんな感じになります。

いい感じにデータが入りました!

余談1:列名を変更したいならColumnNameのプロパティを触る

DataSourceにDataTableを反映させた後列名を任意の文字列にしたいなら、DataGridViewColumnのHeaderTextプロパティを設定すれば自由に列名は触れます。

        private void Form1_Load(object sender, EventArgs e)
        {
            dataGridView1.DataSource = getDataTable();

        // 列名を自由に変更する。
        var col = dataGridView1.Columns[&quot;ID&quot;];
        col.HeaderText = &quot;コード&quot;;
    }</code></pre></div>

これで実行するとこんな感じ!

余談2:CustomClassもDataSourceにできます

でもう一つ余談。

DataGridViewのDataSouceには自身で作ったCustomClassも設定できます。

例えばこんなクラスを用意して

    public class User
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        public string Address { get; set; }

    public User(int id, string name, int age, string address)
    {
        ID = id;
        Name = name;
        Age = age;
        Address = address;
    }
}</code></pre></div>

こんな感じでIEnumerableのUserを返すようにしておいて

        private IEnumerable<User> GetUsers()
        {
            var user1 = new User(1, "鈴木 おさむ",22, "東京都世田谷区");
            var user2 = new User(2, "高橋 つよし",25, "東京都葛飾区");

        return new List&lt;User&gt;()
        {
            user1,user2
        };
    }</code></pre></div>

これをdatasourceに反映させると。

        private void Form1_Load(object sender, EventArgs e)
        {
            //dataGridView1.DataSource = getDataTable();

        //// 列名を自由に変更する。
        //var col = dataGridView1.Columns[&quot;ID&quot;];
        //col.HeaderText = &quot;コード&quot;;
        dataGridView1.DataSource = GetUsers().ToList();
    }</code></pre></div>

こんな感じでデータが入っています。

[s_ad]

DataGridViewをDataTableに変換して取得するには?

これは数行で取得できます。

        private void button1_Click(object sender, EventArgs e)
        {
            var data = (DataTable)dataGridView1.DataSource;
            dataGridView2.DataSource = data;
        }

これを実行するとこんな感じになります。

真ん中のボタンを押すと!

右に同じデータが表示されました!

仮に左のデータを操作して右に投げると!

3行目にデータを追加した
おなじデータが表示されている。

このままだと同じインスタンスになってるので、左右で同じ動きになってしまう。

この状態で鈴木たかひろを鈴木よしろうにする
すると両方とも同時に変わって同期的な動きになってしまう

この現象は同じDatatableを参照しているため。

ボタンを押したタイミングで、DataGridView1で触ったデータとDataGridView2に表示されているデータは同じインスタンスを参照するようになっている。

そのために上記のような現象が起こる。

これを回避するために下記のようにDataTableをコピーして別インスタンスをDataGridView2に反映させる。

        private void button1_Click(object sender, EventArgs e)
        {
            var data = (DataTable)dataGridView1.DataSource;
            var newDt = data.Copy();

        dataGridView2.DataSource = newDt;
    }</code></pre></div>

こうするとさっきのような事は起こらない。

左にデータを追加しても右に反映されなくなった

今回のソースコードはGithubに公開しています。

今回サンプルで紹介したコードはGithubに「DataGridViewWithDataTable」というフォルダ名で公開しています!

Githubのリポジトリについては下記を参照ください!

あわせて読みたい
GitHubリポジトリについて 【このブログで紹介しているコードをまとめてアップしています】 下記リポジトリで、このブログで紹介している全てのコードを公開しています。 是非ローカルにクローン...

💡 補足: 業務系の現場でよくハマるパターン

俺もこの DataGridView × DataTable のパターン、客先で実装するたびに毎回どこかで詰まるんですよね。ありがちなやつを3つ書いておきます。

① DataSource を後から差し替えると CellValueChanged が二重発火する

新しい DataTable を dgv.DataSource = newTable で割り当てたあと、行を選択すると CellValueChanged が呼ばれてしまうんです。これ、初期化時の発火を弾く仕組みを入れてないと、保存処理が暴走します。回避は「DataSource 差し替え時にイベントを一度デタッチ → 差し替え → 再アタッチ」の3手順。詳細パターンは DataGridView の DataSource を後から変更する全パターン に書いてます。

② DataTable.AcceptChanges を呼び忘れて「変更検知できない」事故

DB から Fill した DataTable をそのままバインドして、ユーザーが編集 → DataAdapter.Update() で保存、というフロー。ここで AcceptChanges() を呼ぶタイミングを間違えると、変更が検知されなかったり、逆に古い行まで Update に走ったりします。 DataAdapter で Fill と Update してみる の方で順序をコード付きで解説してます。

③ 列の自動生成と手動定義の混在で列順が崩れる

AutoGenerateColumns = true のままコードで列を追加すると、デザイナで定義した列と自動生成された列が二重に並ぶことがあります。業務画面で「あれ、列が増えてる」となる典型パターン。実装の鉄則は「自動生成は false にして、コードで明示的に追加するか、デザイナで全部定義する」のどちらかに統一です。

❓ よくある質問

Q1. DataGridView の列をコードで動的に作るにはどうする?

A. AutoGenerateColumns = false にしてから dgv.Columns.Add(new DataGridViewTextBoxColumn { ... }) で1列ずつ作るのが鉄板です。DataPropertyName を DataTable のカラム名と一致させるとバインディングが効きます。並び順も DisplayIndex で固定できる。 DataSource 差し替えのパターン もこの前提で書いてます。

Q2. DataTable と DataGridView の値を Linq でフィルタするには?

A. DataTable.AsEnumerable() を使えば Linq に流せます。 C# DataTable を Linq でフィルタ・GroupBy・分割する3パターン でコード付きでまとめてますが、Where 結果を CopyToDataTable() で再度 DataTable に戻すと DataGridView バインドのまま動きます。

Q3. DataGridView のセル編集を即座に DB に反映するには?

A. CellEndEdit イベントを拾って、その場で DataAdapter.Update(table) を呼ぶのが定石です。ただし大量編集される業務画面だと DB アクセスが頻発するので、「行抜けでまとめて Update」の方がパフォーマンス良いことが多いです。実装は DataAdapter で Fill と Update 参照。

Q4. DataGridView を Excel に出力する定石は?

A. DataTable に変換 → ClosedXml or EPPlus で .xlsx に書き出す、が業務系の鉄板です。 DataGridView から直接 Range.CopyFromRecordset 的に書き出す方法もありますが、Excel が起動するので避けたい。DataTable に変換するパターンは Form 間で値を持ち回る時の Form と Form の間で値の共有・受け渡しをする パターンと同じ発想が使えます。

Q5. 大量データ表示時に DataGridView が重いです

A. 1万行を超えるなら仮想モード VirtualMode = true + CellValueNeeded イベントで描画を遅延させるのが王道。さらに DoubleBuffered を有効にすると体感が明確に変わります。データ自体は DataTable に持ったまま、表示だけ仮想化する形ですね。

📚 関連記事

この記事が気に入ったら
いいねしてね!

どんどんシェア待ってるぜ!!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次