Click here to Skip to main content
Click here to Skip to main content
Go to top

An alternate implementation to Paste data into the WPF DataGrid

, 28 Aug 2011
Rate this:
Please Sign up or sign in to vote.
Howto Paste and not make a mess

Introduction

I was inspired by the article Implementing Copy & Paste for WPF DataGrid .NET 4[^] to have a go at an alternate implementation.

The solution in the above article parses the clipboard and copies the data into the model.

That technique has several drawbacks. The first is that it easily gets confused when column/rows are filtered or sorted. The second drawback is that it needs converters to convert the string data from the clipboard to the proper type required by the model.

The function presented here tries to circumvent those types of problems by copying the data back into the grid instead of the model. The tricky part of the implementation is to figure out how the cells are laid out on the screen so that the data from the clipboard gets pasted where expected.

Using the code

I’ll start with the code and then try to explain what the function does.
// 2-dim array containing clipboard data
string[][] clipboardData =
    ((string)Clipboard.GetData(DataFormats.Text)).Split('\n')
    .Select(row =>
        row.Split('\t')
        .Select(cell =>
            cell.Length > 0 && cell[cell.Length - 1] == '\r' ?
            cell.Substring(0, cell.Length - 1) : cell).ToArray())
    .Where(a => a.Any(b => b.Length > 0)).ToArray();
 
// the index of the first DataGridRow
int startRow = dataGrid.ItemContainerGenerator.IndexFromContainer(
    (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromItem
    (dataGrid.CurrentCell.Item));
 
// the destination rows 
//  (from startRow to either end or length of clipboard rows)
DataGridRow[] rows =
    Enumerable.Range(
        startRow, Math.Min(dataGrid.Items.Count, clipboardData.Length))
    .Select(rowIndex =>
        dataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex) as DataGridRow)
    .Where(a => a != null).ToArray();
 
// the destination columns 
//  (from selected row to either end or max. length of clipboard colums)
DataGridColumn[] columns =
    dataGrid.Columns.OrderBy(column => column.DisplayIndex)
    .SkipWhile(column => column != dataGrid.CurrentCell.Column)
    .Take(clipboardData.Max(row => row.Length)).ToArray();
 
for (int rowIndex = 0; rowIndex < rows.Length; rowIndex++)
{
    string[] rowContent = clipboardData[rowIndex];
    for (int colIndex = 0; colIndex < columns.Length; colIndex++)
    {
        string cellContent =
            colIndex >= rowContent.Length ? "" : rowContent[colIndex];
        columns[colIndex].OnPastingCellClipboardContent(
            rows[rowIndex].Item, cellContent);
    }
}
Note that the codeblock above needs a DataGrid named dataGrid to compile. The most sensible thing to do with above code is to pack it into a handler for the Ctrl-V key or into a handler for ApplicationCommands.Paste. I’m not showing that code here because it can be easily looked up elsewhere on the net.

 
The first step is to parse the clipboard and create a 2-dimensional array of strings (string[][] clipboardData) we want to copy into the grid.

Next we need the first index (as on the screen) for the row currently selected. The result is stored in startRow for later use.

The function ContainerFromIndex is then used to create the destination rows (DataGridRow[] rows). The indexed function is used because we need the array ordered as laid out on the screen.

Now the same for the columns is necessary (DataGridColumn[] columns). Here the important part is the sorting by DisplayIndex. By doing that, invisible columns are dropped (because they have an index of -1). The Take filter function is called because we need no more columns then we have in the clipboard.

The only thing left to do is to iterate over all cells and copy the content from the parsed clipboard (clipboardData) into the DataGrid cell. The function that does that for us is OnPastingCellClipboardContent. The nice thing about that function is that is uses normal WPF binding (including type converters) to convert the string passed in to the type required by the model.

Wrap-Up

The code above is far from finished and is definitely not capable of handling all expected situations. It’s more of starting point to show that there is an alternate way of implementing a Paste function for the WPF DataGrid.

While already finished writing that post, I found
http://blogs.msdn.com/b/vinsibal/archive/2008/09/19/wpf-datagrid-clipboard-paste-sample.aspx[^] which uses the same technique for handling columns and copying but handles rows differently. I'll post the article anyway because I think it has less bugs Wink | ;) and some people might find the functional approach more readable.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Reto Ravasio

Switzerland Switzerland
No Biography provided

Comments and Discussions

 
GeneralReason for my vote of 5 Saved me a lot of work - thanks. PinmemberPeter_Smithson30-Aug-11 23:42 
GeneralRe: thanks, appreciate that PinmemberReto Ravasio31-Aug-11 11:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 28 Aug 2011
Article Copyright 2011 by Reto Ravasio
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid