简体   繁体   English

如何使用 MVVM 在 WPF DataGrid 上实现剪贴板副本?

[英]How do I implement a clipboard copy on a WPF DataGrid using MVVM?

I have a DataGrid backed by an ObservableCollection of objects in my ViewModel.我的 ViewModel 中有一个由 ObservableCollection 对象支持的 DataGrid。 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:我希望能够从 DataGrid 复制数据,但是当我单击 Copy 菜单项时,WPF 会引发此异常:

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 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.我使用的是 Visual Studio 2017,而不是 2019。
  • I'm not using SetText, or SetDataObject, or calling anything for that matter.我没有使用 SetText 或 SetDataObject,也没有为此调用任何东西。
  • The exception happens every time, not intermittently, no matter if I'm running from within Visual Studio or not.异常每次都会发生,而不是间歇性发生,无论我是否在 Visual Studio 中运行。
  • I'm not interested in code-behind such as "myDataGrid_CopyingRowClipboardContent" since I'm using MVVM.因为我使用的是 MVVM,所以我对诸如“myDataGrid_CopyingRowClipboardContent”之类的代码隐藏不感兴趣。

More Information - 10/17/2019更多信息 - 2019 年 10 月 17 日

I did some more digging after Abin Mathew posted his answer .Abin Mathew发布他的答案后,我做了更多的挖掘工作。 While his answer is an excellent one, and it works--it doesn't use WPF Commanding , but rather uses RelayCommand.虽然他的答案是一个很好的答案,而且它有效——它不使用WPF Commanding ,而是使用 RelayCommand。 That's fine.没关系。 This question doesn't specify that WPF Commanding must be used.此问题未指定必须使用 WPF 命令。

However, I still wanted to know why WPF Commanding Copy wasn't working for a DataGrid.但是,我仍然想知道为什么 WPF 命令副本不适用于 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:如果您运行我上面发布的确切代码,但在System.Windows.Clipbard.FlushSystem.Windows.Controls.DataGrid.OnExecutedCopy处放置断点,然后每次点击断点时单击运行按钮,复制将成功:

使用断点成功复制

So in conclusion:所以总而言之:

  1. RelayCommand works for performing a copy. RelayCommand 用于执行复制。
  2. WPF Commanding also "works". WPF 命令也“有效”。
  3. WPF Commanding manages getting the data destined for the clipboard from the selected DataGrid rows itself. WPF 命令管理从选定的 DataGrid 行本身获取发往剪贴板的数据。
  4. 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.从 DataGrid 复制时,出现某种竞争情况导致 WPF 命令无法访问刷新剪贴板。
  5. Either I need more code to prevent this race condition, or Microsoft has introduced a bug breaking WPF Commanding Copy from a DataGrid.要么我需要更多代码来防止这种竞争条件,要么微软引入了一个错误,该错误打破了 WPF 命令从 DataGrid 复制。

ApplicationCommands like Cut & Copy only act on Selection .if you are not able to select text like in TextBox then it will throw exception.

I am afraid that WPF introduced a new Bug恐怕WPF引入了一个新Bug

for example like below例如像下面

    <TextBox>
        <TextBox.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Paste" Command="ApplicationCommands.Paste" />                    
                <MenuItem Header="Copy" Command="ApplicationCommands.Copy" />
            </ContextMenu>
        </TextBox.ContextMenu>
    </TextBox>

You can use copy on DataGrid without selection by adding ICommand and Binding the Command Like below您可以通过添加ICommandBinding Command来在DataGrid上使用复制而不选择,如下所示

<MenuItem Header="Copy" Command="{Binding CopyCommand}" CommandParameter="{Binding}"

And the ViewModel will be而 ViewModel 将是

        public ICommand CopyCommand => new RelayCommand<object>(Copy);

        private static void Copy(object obj)
        {
            Clipboard.SetDataObject(((ViewModel)obj).People);
        }

This will copy the collection of People to the Clipboard .这会将People的集合复制到Clipboard If that is what you are trying to do.如果那是你想要做的。

Hope this helps.希望这可以帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM