【キャストなし】DataGridの内容をCSVにエクスポート

C#

こんにちは。働くC#プログラマーのさんさめです。

DataGridって、見た目はかなりExcelライクですよね。
とは言っても機能性はExcelとは比ぶべくもありません。

そのため、CSV(やxlsx)にエクスポートしたい。
という需要が往々にしてあります。

ただ、検索して上位に出てくるものは、
View層で要素のクラスにキャストするものばかり…。

さんさめ
さんさめ

Model層知ってる=汎用性が無い
と言っているようなもの

というわけで本記事では、
キャストを使わずにDataGridの内容をCSVにエクスポートする方法
について解説したいと思います。

スポンサーリンク

CSVエクスポートのサンプルコード

早速ですが、CSVエクスポートするサンプルコードです。

xamlは名前付きのDataGridと、
エクスポートするButtonを置いただけのシンプルなものです。
また、キャストなしでView層のみで完結するコードのため、
要素のクラスなどはサンプルでは省略しています。

<Window x:Class="DataGridCsvExport.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DataGridCsvExport"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <DockPanel>
        <Button Content="csvエクスポート" DockPanel.Dock="Top"
                Click="Button_Click"/>
        <DataGrid x:Name="MainDataGrid"/>
    </DockPanel>
</Window>
private void Button_Click(object sender, RoutedEventArgs e)
{
    // ファイルパスは適当
    var csvPath = "a.csv";

    using (var sw = new StreamWriter(csvPath, false, Encoding.Unicode))
    {
        // 列ヘッダ
        sw.WriteLine(string.Join(",", MainDataGrid.Columns
            .Select(x =>
            {
                if (x.Header is string headerText)
                {
                    return headerText;
                }
                else
                {
                    // Contentを直刺ししている場合は
                    // 汎用的に希望の文字列を取れる手段はないので割愛
                    return string.Empty;
                }
            })));

        // セルの内容
        foreach (var item in MainDataGrid.Items)
        {
            sw.WriteLine(string.Join(",", MainDataGrid.Columns
                .Select(x => x.OnCopyingCellClipboardContent(item)?.ToString())));
        }
    }
}

コードを見て分かる通り、
中に格納されている要素へのキャストなどは行っていません。

CSVエクスポートの実行結果

実行結果も載せておきます。
先ほどのサンプルコードでボタンを押してみたところ…

こんな感じのcsvが出力されます。
ちゃんと出てますね。

全くの余談ですが、
EncodingはUnicodeやShitf_Jisを指定しておかないと、
日本語は文字化けすることに注意しましょう。

OnCopyingCellClipboardContentイベントを活用している

さて、サンプルコードは
string.JoinとLINQをフル活用しているため、
若干読みづらいかもしれませんね。

結局のところキモは以下の部分です。

sw.WriteLine(string.Join("\t", MainDataGrid.Columns
                .Select(x => x.OnCopyingCellClipboardContent(item)?.ToString())));

DataGridColumnには、
OnCopyingCellClipboardContentという
セルのコピー相当の呼出しが行えるメソッドがあります。

このメソッドの引数にはコピーしたい行のDataContextを渡せばOKです。
今回の目的は、全内容をCSVエクスポートすることなので、
DataGrid.Itemsを列挙すれば1つずつ取得できます。

この呼出しによってセルの表示内容を取得しているため、
特定のクラスへのキャストをせずにCSVエクスポートができているわけですね。

まとめ

本記事では、
キャストを使わずにDataGridの内容をCSVにエクスポートする方法
を解説しました。

ItemsSourceのクラスに関係なく
CSVにエクスポートできるため、
汎用性が高いコードとなっています。

このようにViewで完結しているコードであれば、
RoutedCommand化してしまうのも良いですね。

DataGridを継承してバリバリ改造しているぜ!
という人は一考の余地アリです
(そのためにはファイルパスや例外対応を入れなければなりませんが…)

RoutedCommandについては、
別記事で解説しているので良かったらご覧ください。
Viewにコマンドを作る?RoutedCommandを作ってみよう

参考になれば幸いです。
最後までお読みいただきありがとうございました。

コメント

タイトルとURLをコピーしました