【问题标题】:How do I implement a clipboard copy on a WPF DataGrid using MVVM?如何使用 MVVM 在 WPF DataGrid 上实现剪贴板副本?
【发布时间】:2020-02-12 09:31:04
【问题描述】:

我的 ViewModel 中有一个由 ObservableCollection 对象支持的 DataGrid。我还有一个上下文菜单,其中包含使用默认复制命令的复制条目。我希望能够copy data from the DataGrid,但是当我单击“复制”菜单项时,WPF 会抛出此异常:

OpenClipboard Failed (Exception from HRESULT: 0x800301D0 (CLIPBRD_E_CANT_OPEN))

视图模型

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>

我见过其他descriptions of this exception。然而:

  • 我使用的是 Visual Studio 2017,而不是 2019。
  • 我没有使用 SetText 或 SetDataObject,也没有为此调用任何东西。
  • 无论我是否在 Visual Studio 中运行,异常每次都会发生,而不是间歇性发生。
  • 我对诸如“myDataGrid_CopyingRowClipboardContent”之类的代码隐藏不感兴趣,因为我使用的是 MVVM。

更多信息 - 2019 年 10 月 17 日

Abin Mathew 发布his answer 之后,我做了更多的挖掘工作。虽然他的回答是一个很好的回答,而且它有效——它不使用WPF Commanding,而是使用RelayCommand。没关系。此问题未指定必须使用 WPF 命令。

但是,我仍然想知道为什么 WPF 命令复制不适用于 DataGrid。事实上,它确实有效——它只是取决于时间。如果你运行我上面贴的代码,但是在System.Windows.Clipbard.FlushSystem.Windows.Controls.DataGrid.OnExecutedCopy处设置断点,然后每次遇到断点都点击运行按钮,复制就会成功:

总之:

  1. RelayCommand 用于执行复制。
  2. WPF 命令也“有效”。
  3. WPF 命令管理从选定的 DataGrid 行本身获取发往剪贴板的数据。
  4. 当从 DataGrid 复制时,出现某种竞争条件导致 WPF 命令无法访问刷新剪贴板。
  5. 要么我需要更多代码来防止出现这种竞争状况,要么 Microsoft 引入了一个错误,该错误会破坏 WPF 从 DataGrid 中命令复制。

【问题讨论】:

  • 您能否分享您的命令代码Copy(应该是您班级的属性Person)?
  • @Corentin Pane - 我没有 Copy 的代码。这完全由 .NET 实现。

标签: c# wpf mvvm datagrid clipboard


【解决方案1】:

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

恐怕WPF引入了一个新的Bug

例如下图

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

您可以通过添加ICommandBinding Command 来在DataGrid 上使用复制,如下所示

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

ViewModel 将是

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

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

这会将People 的集合复制到Clipboard。如果那是你想要做的。

希望这会有所帮助。

【讨论】:

  • 那为什么this answer,特别是不使用自定义命令,而是声明,“......不需要任何额外的代码来让它发挥作用”?
  • @watkipet 看起来从 2016 年到 2019 年,WPF framework 发生了很多变化。但是,是的,我不确定它为什么以及如何工作。
  • 我愿意接受您的回答,因为这是使用 RelayCommand 解决问题的好方法。但是,在做了更多研究之后(请参阅我上面的问题编辑),我认为异常不是由选择问题引起的。如果你同意我的观点,你会编辑你的答案然后我会接受吗?
  • @watkipet 我确实删除了您的要求,但正如您的编辑所说,我认为这是 WPF 中的一个错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-17
  • 1970-01-01
  • 2013-10-17
  • 2012-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多