Как реализовать копию буфера обмена в WPF DataGrid с использованием MVVM? - PullRequest
0 голосов
/ 15 октября 2019

У меня есть DataGrid, поддерживаемый ObservableCollection объектов в моей ViewModel. У меня также есть контекстное меню с записью «Копировать», в которой используется команда «Копировать» по умолчанию. Я хотел бы иметь возможность копировать данные из DataGrid , но когда я нажимаю на пункт меню Копировать, 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

<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 или нет.
  • Меня не интересует выделенный код, такой как "myDataGrid_CopyingRowClipboardContent", так как я использую MVVM.

Дополнительная информация - 10/17/2019

Я сделал еще несколько копаний после Абин Мэтью опубликовано его ответ . Хотя его ответ отличный, и он работает - он не использует WPF Commanding , а скорее использует RelayCommand. Все в порядке. Этот вопрос не указывает, что WPF Commanding должен использоваться.

Однако я все еще хотел знать, почему WPF Commanding Copy не работает для DataGrid. На самом деле, это работает - это зависит только от времени. Если вы запустили точный код, который я разместил выше, но поставили точки останова на System.Windows.Clipbard.Flush и System.Windows.Controls.DataGrid.OnExecutedCopy, а затем нажимали кнопку запуска при каждом нажатии на точки останова, копирование выполнится успешно:

Successful copy using breakpoints

Итак, в заключение:

  1. RelayCommand работает для выполнения копирования.
  2. Командование WPF также "работает".
  3. WPF Commanding управляет получением данных, предназначенных для буфера обмена, из самих выбранных строк DataGrid.
  4. Происходит какое-то состояние гонки, которое приводит к тому, что WPF Commanding не может получить доступ к очистке буфера обмена при копировании из DataGrid. .
  5. Либо мне нужно больше кода для предотвращения этого состояния гонки, либо Microsoft представила ошибку, нарушающую WPF Commanding Copy из DataGrid.

1 Ответ

1 голос
/ 15 октября 2019

<s><code>ApplicationCommands, как Cut & Copy действует только на Selection. Если вы не можете выделить текст, как в TextBox, он выдаст исключение.

Iбоюсь, что WPF ввел новый Bug

, например, как показано ниже

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

Вы можете использовать копию на DataGrid без выделения, добавив ICommand иBinding Command Как показано ниже

<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. Если это то, что вы пытаетесь сделать.

Надеюсь, это поможет.

...