繁体   English   中英

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

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

我的 ViewModel 中有一个由 ObservableCollection 对象支持的 DataGrid。 我还有一个上下文菜单,其中包含使用默认复制命令的复制条目。 我希望能够从 DataGrid 复制数据,但是当我单击 Copy 菜单项时,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>

我已经看到了这个异常的其他描述 然而:

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

更多信息 - 2019 年 10 月 17 日

Abin Mathew发布他的答案后,我做了更多的挖掘工作。 虽然他的答案是一个很好的答案,而且它有效——它不使用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. 要么我需要更多代码来防止这种竞争条件,要么微软引入了一个错误,该错误打破了 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.

恐怕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 如果那是你想要做的。

希望这可以帮助。

暂无
暂无

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

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