在WPF DataGrid中实现自定义复制和粘贴,当它没有行时可以工作

too*_*too 6 c# wpf routed-commands commandbinding wpfdatagrid

我需要为WPF应用程序中的网格之间的数据实现数据(非文本或CSV)的自定义复制+剪切+粘贴.使用标准ApplicationCommands并定义CommandBinding非常有效,但前提是DataGrid包含至少1行数据以及何时选择.当没有行或焦点不在任何行上时,所有命令都被禁用.

为了解决这个问题,我尝试在DataGrid上调用CommandManager.InvalidateRequerySuggested()并设置Focusable = true和/或FocusManager.IsFocusScope = true,但似乎内部DataGrid作为一个整体在处理复制/粘贴操作时"不感兴趣",只有它的行正在重新查询命令CanExecute状态并相应地调用Execute.它也忽略了KeyBindings.

如何让DataGrid处理重新查询ApplicationCommands?

请找到我在下面测试问题的示例:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <DataGrid x:Name="TheGrid">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Number" Binding="{Binding}"/>
        </DataGrid.Columns>
        <DataGrid.InputBindings>
            <KeyBinding Key="A" Command="{x:Static ApplicationCommands.New}"/>
        </DataGrid.InputBindings>
        <DataGrid.CommandBindings>
            <CommandBinding Command="{x:Static ApplicationCommands.Paste}" CanExecute="CanPaste" Executed="Paste"/>
            <CommandBinding Command="{x:Static ApplicationCommands.Copy}" CanExecute="CanCopy" Executed="Copy"/>
            <CommandBinding Command="{x:Static ApplicationCommands.New}" CanExecute="CanAddNew" Executed="AddNew"/>
        </DataGrid.CommandBindings>
        <DataGrid.ContextMenu>
            <ContextMenu>
                <MenuItem Command="{x:Static ApplicationCommands.Copy}" Header="Copy"/>
                <MenuItem Command="{x:Static ApplicationCommands.Paste}" Header="Paste"/>
                <MenuItem Command="{x:Static ApplicationCommands.New}" Header="New row"/>
            </ContextMenu>
        </DataGrid.ContextMenu>
    </DataGrid>
</Window>
Run Code Online (Sandbox Code Playgroud)

而背后的代码:

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;

namespace WpfApplication1
{
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();
            TheGrid.ItemsSource = numbers;
            // Following line enables commands when row is selected
            numbers.Add(0);
        }

        private void Copy(object sender, ExecutedRoutedEventArgs e)
        {
            Clipboard.SetData(DataFormats.Text, string.Join(",", numbers));
        }

        private void CanCopy(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = numbers.Count > 0;
        }

        private void CanPaste(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = numbers.Count > 0;
            e.Handled = true;
        }

        private void Paste(object sender, ExecutedRoutedEventArgs e)
        {
            Close();
        }

        private void CanAddNew(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
            e.Handled = true;
        }

        private void AddNew(object sender, ExecutedRoutedEventArgs e)
        {
            numbers.Add(numbers.Count);
        }

        private readonly ICollection<int> numbers = new ObservableCollection<int>();
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑

Ayyappan Subramanian的代码解决方案是最接近它将使用的应用程序的匹配.最终,因为我已经继承了网格,因为它有自定义复制+粘贴格式,我添加了一些代码,确保焦点位于网格3例:

  1. 显示上下文菜单
  2. 当用户的视觉效果没有焦点时,用户点击网格(空)区域
  3. (我们的应用程序特定情况)用户单击网格导航TreeView,然后将焦点带到网格,因此快捷方式将立即生效.

相关代码:

public class MyDataGrid: DataGrid
{
        protected override void OnContextMenuOpening(ContextMenuEventArgs e)
        {
            base.OnContextMenuOpening(e);
            Focus();
        }

        protected override void OnMouseUp(MouseButtonEventArgs e)
        {
            base.OnMouseUp(e);
            if(e.ChangedButton == MouseButton.Left && !IsKeyboardFocusWithin)
            {
                Focus();
            }
        }
}
Run Code Online (Sandbox Code Playgroud)

Ayy*_*ian 7

当ContextMenu打开时,您可以将焦点设置为网格,这将启用所有菜单项.这里给出了很好的解释http://www.wpftutorial.net/RoutedCommandsInContextMenu.html

另外,为了实现粘贴,请参考SO post WPF datagrid粘贴

WPF:

<DataGrid x:Name="TheGrid" CanUserAddRows="True" 
          ContextMenuOpening="TheGrid_ContextMenuOpening">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Number" Binding="{Binding}"/>
    </DataGrid.Columns>
    <DataGrid.InputBindings>
        <KeyBinding Key="A" Command="{x:Static ApplicationCommands.New}"/>
    </DataGrid.InputBindings>
    <DataGrid.CommandBindings>                
        <CommandBinding Command="{x:Static ApplicationCommands.Paste}" 
                        CanExecute="CanPaste" Executed="Paste"/>
        <CommandBinding Command="{x:Static ApplicationCommands.Copy}" 
                        CanExecute="CanCopy" Executed="Copy"/>
        <CommandBinding Command="{x:Static ApplicationCommands.New}" 
                        CanExecute="CanAddNew" Executed="AddNew"/>
    </DataGrid.CommandBindings>
    <DataGrid.ContextMenu>
        <ContextMenu>                   
            <MenuItem Command="{x:Static ApplicationCommands.Copy}" Header="Copy"/>
            <MenuItem Command="{x:Static ApplicationCommands.Paste}" Header="Paste"/>
            <MenuItem Command="{x:Static ApplicationCommands.New}" Header="New row"/>
        </ContextMenu>
    </DataGrid.ContextMenu>
</DataGrid>
Run Code Online (Sandbox Code Playgroud)

C#代码:

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent(); 
        TheGrid.ItemsSource = numbers;
    }

    private void Copy(object sender, ExecutedRoutedEventArgs e)
    {
        Clipboard.SetData(DataFormats.Text, string.Join(",", numbers));
    }

    private void CanCopy(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }

    private void CanPaste(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
        e.Handled = true;
    }

    private void Paste(object sender, ExecutedRoutedEventArgs e)
    {
        Close();
    }

    private void CanAddNew(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
        e.Handled = true;
    }

    private void AddNew(object sender, ExecutedRoutedEventArgs e)
    {
        numbers.Add(numbers.Count);
    }

    private readonly ICollection<int> numbers = new ObservableCollection<int>();

    private void TheGrid_ContextMenuOpening(object sender, ContextMenuEventArgs e)
    {
        TheGrid.Focus();
    }

}
Run Code Online (Sandbox Code Playgroud)