Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Implementing Copy and Paste for a WPF DataGrid in .NET 4

0.00/5 (No votes)
31 Aug 2011 2  
Using the DataZGrid provided by .NET 4, this article shows how to copy and paste between cells.

Introduction

Update - See restriction 3 below.

Using the standard DataGrid that was added with .NET 4, I found that I needed to implement copy and paste. Searching the internet came up with solutions that either worked with Silverlight or with some sort of pre-release version that you could add to Visual Studio 2008 (.NET 3).

To my horror, I had to actually figure something out and write some code. I'm very new to WPF and have spent the last few months copying example code off the internet and modifying it for my use but I think I have finally written something that might be of use to other people, so here it is.

Credit goes to the following two pages that helped, and some code was taken from the second one (credited in comments):

The solution allows you to do some limited copy and pasting between cells. In the example, I have a table of people with fields first name, second name, and legs (a number - usually 2). In the application I was working on, it made no sense to copy from one column to another. So in this example, you can't copy the legs column to the first name column (for example). This is intentional and the restriction could be removed easily by deleting some checks.

Restrictions / problems to solve!

  1. It only deals with strings or integers. It should be easy to expand on the types allowed with more code but perhaps that can be avoided completely with a different implementation.
  2. The clipboard is only converted from one particular format. I need to see if copying from Excel works - otherwise I probably need to put some code back into DatagridHelper that I removed!
  3. Important If the rows are re-ordered, it breaks this code! For this reason, I suggest you don't use this code.

Reto Ravisio saw this article and provided a much better solution. I have taken that, changed it slightly, and provided a complete solution file that uses his code:

Hopefully this will help beginners like myself use that code.

Using the code

I will go over some of the code you have to copy into your project, but you'll have to see the entire solution to get all the required code. This is a tester to show you the main principles so you can decide if it's for you.

The DataGrid is declared like this:

<DataGrid Name="dataGrid1" ItemsSource="{Binding PeopleList}" 
                  SelectionUnit="Cell"
                  AutoGenerateColumns="True" 
                  KeyDown="dataGrid1_KeyDown" 
                  CopyingRowClipboardContent="dataGrid1_CopyingRowClipboardContent" 
                  ColumnReordered="dataGrid1_ColumnReordered"/>

Note that the SelectionUnit is set to Cell. This allows individual cells to be selected rather than the whole row. The items are taken from an ObserverableCollection and the columns are automatically generated.

The KeyDown event is set to detect the paste operation:

void dataGrid1_KeyDown(object sender, KeyEventArgs e)
{
  if (e.Key == Key.V &&
      (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
  {
    if (ValidatePaste() == false)
      return;

    viewModel.Paste(dataGrid1.CurrentColumn.DisplayIndex,
         GetTargetRow(),
         dataGrid1.SelectedCells.Count,
         Clipboard.GetData(DataFormats.Text) as string);
  }
}

As you can see, a View Model is used.

The View Model Paste function uses the ClipboardHelper class (taken from the MSDN blog mentioned in the introduction) to convert the string version of the copied cells into a list of string arrays representing the cells on each row. Then it loops through the target rows setting the ObservableCollection.

Reflection is used to set the properties in the collection having only a string representing the property name. This avoids having a switch statement checking the display index value and matching that up to a property. It would have made the code very tedious to write and not easily re-usable.

The SetProperty function implements that functionality:

private void SetProperty(Person person, string propertyName, string cellItem)
{
  // Use reflection to set the property to the value
  Type type = person.GetType();
  PropertyInfo prop = type.GetProperty(propertyName);
  if (prop == null)
  {
    Debug.WriteLine("Property not found: " + propertyName);
    return;
  }
  if (prop.PropertyType == typeof(string)) // only caters for string or integer types
    prop.SetValue(person, cellItem, null);
  else
    prop.SetValue(person, int.Parse(cellItem), null);
  person.OnPropertyChanged(propertyName);
}

You can see in the code above where the restriction on having only strings or integers comes from.

The People class is a simple container class that implements INotifyPropertyChanged:

class Person : INotifyPropertyChanged
{
    public Person(string first, string second)
    {
      FirstName = first;
      SecondName = second;
      Legs = 2;
    }
    public string FirstName { set; get; }
    public string SecondName { set; get; }
    public int Legs { set; get; }

#region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    public virtual void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

#endregion
}

The code which populates the map of property names and display indexes (used in the VM Paste function) relies on this bit of code in the code-behind:

private void dataGrid1_ColumnReordered(object sender, DataGridColumnEventArgs e)
{
    viewModel.ClearColumnMapping();
    foreach (DataGridColumn col in dataGrid1.Columns)
    {
        viewModel.AddColumnMapping(col.DisplayIndex, col.SortMemberPath);
    }
}

I'm assuming that the SortMemberPath will always be the same as the property name in the People class. It was in my case.

History

  • 2011-09-01: A new version which gets the column information when the columns are re-ordered rather than when loaded. Updated a downloadable complete solution based on Reto's article.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here