DataGridViewでのクリップボード処理

選択されているセルを、クリップボードへコピー

クリップボードへのコピーはDataGridViewの機能として提供されており、既定で有効となっています。そしてその処理は、DataGridView.GetClipboardContent()を用いてプログラムから実行することも可能です。DataGridView.GetClipboardContent Method (System.Windows.Forms) | Microsoft Learn

DataObject data = dataGridView.GetClipboardContent();
if (data != null) // 選択されているセルがないと、nullが返される
{
    Clipboard.SetDataObject(data);
}

コピーする内容の変更

クリップボードへコピーするときの内容を変更するには、 DataGridViewCellを継承するクラスを定義し、GetClipboardContent()をオーバーライドします。なおヘッダーを含めるかどうかを変更するだけならば、ClipboardCopyModeを設定します。

public class CustomDataGridViewTextBoxCell : DataGridViewTextBoxCell
{
    protected override object GetClipboardContent(int rowIndex, bool firstCell, bool lastCell, bool inFirstRow, bool inLastRow, string format)
    {
        bool csv = String.Equals(format, DataFormats.CommaSeparatedValue, StringComparison.OrdinalIgnoreCase);
        if (csv
        || String.Equals(format, DataFormats.Text, StringComparison.OrdinalIgnoreCase)
        || String.Equals(format, DataFormats.UnicodeText, StringComparison.OrdinalIgnoreCase))
        {
            StringBuilder sb = new StringBuilder(64);

            string formattedValue = GetEditedFormattedValue(rowIndex, DataGridViewDataErrorContexts.Formatting | DataGridViewDataErrorContexts.ClipboardContent) as string;
            if (formattedValue != null)
            {
                if (csv)
                {
                    if (formattedValue.IndexOf('"') != -1)
                    {
                        // 二重引用符を含むならば、二重引用符でエスケープし二重引用符で囲む
                        sb.AppendFormat("\"{0}\"", formattedValue.Replace("\"", "\"\""));
                    }
                    else if (formattedValue.IndexOf(',') != -1 || formattedValue.IndexOf("\r\n") != -1)
                    {
                        // カンマか改行を含むならば、二重引用符で囲む
                        sb.AppendFormat("\"{0}\"", formattedValue);
                    }
                    else
                    {
                        sb.Append(formattedValue);
                    }
                }
                else
                {
                    sb.Append(formattedValue);
                }
            }

            if (!lastCell)
            {
                // 最後の列でなければ、区切り文字を追加
                sb.Append(csv ? ',' : '\t');
            }
            else if (!inLastRow)
            {
                // 最後の列だが最後の行でなければ、改行文字を追加
                sb.Append("\r\n");
            }

            return sb.ToString();
        }
        return null;
    }
}
GetClipboardContent - DataGridViewCell.cs

そしてDataGridViewColumnを継承するクラスを定義し、そのコンストラクタでDataGridViewCellを継承したクラスのインスタンスを渡します。

public class CustomDataGridViewColumn : DataGridViewColumn
{
    public CustomDataGridViewColumn() : base(new CustomDataGridViewTextBoxCell())
    {
        this.SortMode = DataGridViewColumnSortMode.Automatic;
    }

    public override DataGridViewCell CellTemplate
    {
        get
        {
            return base.CellTemplate;
        }
        set
        {
            if (value != null && !(value is CustomDataGridViewTextBoxCell))
            {
                throw new InvalidCastException();
            }
            base.CellTemplate = value;
        }
    }
}
DataGridViewTextBoxColumn.cs

これを利用するときは、DataGridViewColumnを継承したクラスを作成し、それを列に追加します。

DataGridViewColumn column = new CustomDataGridViewColumn();
column.Name = "";
column.HeaderText = "";

dataGridView.Columns.Add(column);

もしくは定義したDataGridViewCellを継承するクラスをテンプレートとして、DataGridViewColumnを作成します。

DataGridViewColumn column = new DataGridViewColumn(new CustomDataGridViewTextBoxCell());

DataFormats.Html

HTML形式では、GetClipboardContent()から

protected override object GetClipboardContent(int rowIndex, bool firstCell, bool lastCell, bool inFirstRow, bool inLastRow, string format)
{
    if (String.Equals(format, DataFormats.Html, StringComparison.OrdinalIgnoreCase))
    {
        return "SAMPLE";
    }
    return null;
}

のように返すと、クリップボードには

Version:1.0
StartHTML:00000097
EndHTML:00000177
StartFragment:00000133
EndFragment:00000141
<HTML>
<BODY>
<!--StartFragment-->SAMPLE
<!--EndFragment-->
</BODY>
</HTML>

のように格納されるため、body要素内に記述したい内容だけを返すようにします。GetClipboardContentForHtml - DataGridViewMethods.cs

コピー時の例外

追加情報:FORMATETC 構造体が無効です (HRESULT からの例外:0x80040064 (DV_E_FORMATETC))

c# - Cryptic exception copy/pasting from DataGridView into Excel 2002 - Stack Overflow

クリップボードから、選択されているセルへコピー

クリップボードからセルへのコピーはDataGridViewの機能として提供されていないため、自力で実装する必要があります。

IDataObject iData = Clipboard.GetDataObject();

string text;
char delimiter;

if (iData.GetDataPresent(DataFormats.CommaSeparatedValue)) // CSV形式の場合
{
    object obj = iData.GetData(DataFormats.CommaSeparatedValue);
    if (obj is MemoryStream)
    {
        MemoryStream memoryStream = (MemoryStream)obj;
        StreamReader streamReader = new StreamReader(memoryStream, Encoding.Default);
        // ExcelからCSV形式でデータを取得するときには文字化けすることがあるため、文字エンコーディングを明示する
        // c# - Get CSV Data from Clipboard (pasted from Excel) that contains accented characters - Stack Overflow

        text = streamReader.ReadToEnd();
    }
    else
    {
        text = (string)obj;
    }

    delimiter = ',';
}
else if (iData.GetDataPresent(typeof(string))) // テキスト形式の場合
{
    text = (string)iData.GetData(typeof(string));
    delimiter = '\t';
}
else // その他の場合
{
    return;
}

// 改行文字で行ごとに分割する
string[] lines = text.Split(new string[] { "\n", "\r\n", "\r" }, StringSplitOptions.None);

// 区切り文字で要素ごとに分割する
List<string[]> elements = new List<string[]>();
foreach (string line in lines)
{
    elements.Add(line.Split(delimiter));
}

// ※CSV形式ではフィールドに改行や区切り文字が含まれていると、不適切に分割される


// 選択されている最初のセルを基準として、セルに値を設定する
DataGridViewSelectedCellCollection selectedCells = dataGridView.SelectedCells;
int columnIndex = selectedCells[0].ColumnIndex;
int rowIndex = selectedCells[0].RowIndex;

for (int y = 0; y < elements.Count; y++)
{
    for (int x = 0; x < elements[y].Length; x++)
    {
        dataGridView[columnIndex + x, rowIndex + y].Value = elements[y][x];
    }
}
Microsoft Learnから検索