3流プログラマのメモ書き

元開発職→社内SE→派遣で営業支援→開発戻り浦島太郎状態の三流プログラマのIT技術メモ書き。 このメモが忘れっぽい自分とググってきた技術者の役に立ってくれれば幸いです。

(VB.Net)DataObject.GetText()の引数にCSV(TextDataFormat.CommaSeparatedValu)指定時のバグ!?

(VB.Net)DataGridViewで現在選択されているセルの値を簡単にタブ区切りやCSV文字列にする方法の続きです。

次のようなデータをDataGridViewに表示するとします。

┌────┬───────┐
│備 考  │備 考2       │
├────┼───────┤
│今日は  │              │
│寒い    │カンマ,カンマ │
└────┴───────┘


ポイントは「今日は」の後に改行があることと、値の中にCSVの区切り文字である , をいれてることです。
CSV本来のフォーマットでこれを表そうとするとこうなるはずです。

備 考,備 考2
"今日は
寒い","カンマ,カンマ"


要は、改行と,があるところはダブルクォートで囲むわけです。
これを前の記事の方法でCSV形式で文字列を取得したらどうなるでしょうか。
こうなりました。

備 考,備 考2
今日は
寒い,"カンマ,カンマ"


つまり、本来データの値に改行がある場合は、ダブルクォートで囲まないといけないのに、そうしてないのです。

おかげで、このCSVをそのままエクセルとかで開くと列と行の対応がぐちゃぐちゃになります。
とういことで、改行した値もほしい時は、前回紹介したGetClipboardContent()を使った方法はとれないということになりそうです。
ネットで「DataGridView CSV」と検索すると、CSV形式で吐き出す時のいろいろなサンプルが出てますが、やはりDataGridViewを行、列でループして、云々という方法しかないようです。

ちなみに、CSVでなくてタブ区切り時でも改行が含まれていてもダブルクォートが含まれません。

今回のシステムは、CSV出力時にはセルの値の改行はなくしてもよいという仕様だったので、クリップボード時に改行が含まれる可能性があるセルの値を一時退避し、改行除去、GetClipboardContent()の後退避していたセルの値を戻すという荒技にすることにしました。

下記のそれのソースです。"Remarks"列が改行が含まれる可能性があるので、この列のみ改行コードをのけ、タブ区切り、もしくはCSV化された文字列を返します。

''' <summary>
    ''' グリッドの値を出力する際の文字列区切り形式
    ''' </summary>
    ''' <remarks></remarks>
    Private Enum OutputTextDataFormatEnum
        ''' <summary>CSV区切り</summary>
        CSV = 1
        ''' <summary>Tab区切り</summary>
        TAB = 2
    End Enum
 
    ''' <summary>
    ''' グリッドの選択されているセルの値を文字列と取得する
    ''' </summary>
    ''' <param name="fmt">呼び出し元に返す文字列のフォーマット CSVかTab区切りか</param>
    ''' <param name="blnSelectAllCells">True:すべてのセルを選択  False:現在選択されているセルを対象</param>
    ''' <param name="grd">操作対象のDataGirdViewコントロール</param>
    ''' <returns>CSV、もしくはタブ区切り化された文字列。選択されたセルがない時は空文字を返す。</returns>
    ''' <remarks></remarks>
    Private Function GetClipboard(ByVal fmt As OutputTextDataFormatEnum, ByVal blnSelectAllCells As Boolean, ByVal grd As DataGridView) As String
        If blnSelectAllCells Then
            'すべての列行を選択
            grd.SelectAll()
        End If
 
        '出力する文字列
        Dim strOutput As String = ""
 
        If grd.SelectedCells.Count > 0 Then
 
            'いったん選択されているセルの値(文字列)を一時退避
            Dim lstStrBuf As New List(Of String)
            For Each cell As DataGridViewCell In grd.SelectedCells
                '"Remarks"列のみ改行コード除去
                If cell.ColumnIndex = grd.Columns("Remarks").Index Then
                    If IsDBNull(cell.Value) Then
                        lstStrBuf.Add(Nothing)
                    Else
                        lstStrBuf.Add(cell.Value)
                        'セルの値中の改行コードをのける(エクセル出力時に、おかしくなるため)
                        cell.Value = (cell.Value.ToString()).Replace(vbNewLine, "")
                    End If
                End If
            Next
 
            'グリッドの選択範囲の値を取得
            Dim returnValue As DataObject
            returnValue = grd.GetClipboardContent()
 
            If fmt = OutputTextDataFormatEnum.CSV Then
                'CSV形式でグリッドの値取得
                strOutput = returnValue.GetText(TextDataFormat.CommaSeparatedValue)
            ElseIf fmt = OutputTextDataFormatEnum.TAB Then
                'タブ区切り形式でグリッドの値取得
                strOutput = returnValue.GetText()
            End If
 
            '一時退避していたセルの値をもとに戻す
            Dim i As Integer = 0
            For Each cell As DataGridViewCell In grd.SelectedCells
                '"Remarks"列のみ改行コード除去
                If cell.ColumnIndex = grd.Columns("Remarks").Index Then
                    cell.Value = lstStrBuf(i)
                    i += 1
                End If
            Next
 
        Else
            'セルが選択されていないときは空文字を返す
            strOutput = String.Empty
        End If
 
        Return strOutput
    End Function


このメソッドを使って上記の例のデータは下記のようになります。(まあただ改行コードがないだけですが。。)

備 考,備 考2
今日は寒い,"カンマ,カンマ"



まったくDataObject.GetText()の仕様はバグとしか言いようがないのですが、MSは直してくれないのですかね。

追記(2010/01/19)
Microsoft Connectにてこのバグを報告してる人いました。
下記URLになります。
Microsoft Connect:DataGridViewのGetClipboardContentでCSVがおかしい
ただ、MSの回答としてはバグとは認めるが、修正する気に無しだそうです。
おいおい。。って感じですね。