I have a DataGrid backed by an ObservableCollection of objects in my ViewModel. I also have a context menu with a Copy entry which uses the default Copy command. I'd like to be able to copy data from the DataGrid, but when I click on the Copy menu item, WPF throws this exception:
OpenClipboard Failed (Exception from HRESULT: 0x800301D0 (CLIPBRD_E_CANT_OPEN))
ViewModel
public class ViewModel
{
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public ObservableCollection<Person> People { get; set; }
public ViewModel()
{
People = new ObservableCollection<Person>
{
new Person {FirstName = "Heir", LastName = "Band"},
new Person {FirstName = "Rose", LastName = "Anne"},
new Person {FirstName = "Tim", LastName = "Poral"}
};
}
}
XAML
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Grid>
<DataGrid ItemsSource="{Binding Path=People}">
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy" Command="Copy" />
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
</Grid>
</Window>
I've seen other descriptions of this exception. However:
- I'm using Visual Studio 2017, not 2019.
- I'm not using SetText, or SetDataObject, or calling anything for that matter.
- The exception happens every time, not intermittently, no matter if I'm running from within Visual Studio or not.
- I'm not interested in code-behind such as "myDataGrid_CopyingRowClipboardContent" since I'm using MVVM.
More Information - 10/17/2019
I did some more digging after Abin Mathew posted his answer. While his answer is an excellent one, and it works--it doesn't use WPF Commanding, but rather uses RelayCommand. That's fine. This question doesn't specify that WPF Commanding must be used.
However, I still wanted to know why WPF Commanding Copy wasn't working for a DataGrid. In fact, it does work--it just depends on timing. If you run the exact code I posted above, but put breakpoints at System.Windows.Clipbard.Flush
and System.Windows.Controls.DataGrid.OnExecutedCopy
, and then click the run button each time the breakpoints are hit, the copy will succeed:
So in conclusion:
- RelayCommand works for performing a copy.
- WPF Commanding also "works".
- WPF Commanding manages getting the data destined for the clipboard from the selected DataGrid rows itself.
- There's some sort of race condition going on that is causing WPF Commanding to fail to get access to Flush the clipboard when copying from a DataGrid.
- Either I need more code to prevent this race condition, or Microsoft has introduced a bug breaking WPF Commanding Copy from a DataGrid.