在WPF DataGrid中实现自定义复制和粘贴,当其中没有行时可以使用 [英] Implement custom Copy and Paste in WPF DataGrid which works when there are no rows in it
问题描述
我需要为WPF应用程序中的网格之间复制的数据(不是文本或CSV)实现自定义复制+剪切+粘贴.使用标准ApplicationCommands并定义CommandBinding确实可以很好地工作,但前提是DataGrid至少包含1行数据并且处于选中状态.当没有行或焦点不在任何行上时,将禁用所有命令.
I need to implement a custom copy + cut + paste for data (not text or CSV) to be copied between grids in a WPF application. Using standard ApplicationCommands and defining CommandBinding works really well but only if the DataGrid contains at least 1 row of data and when it's selected. When there are no rows or focus is not on any of them, all commands are disabled.
为解决此问题,我尝试调用CommandManager.InvalidateRequerySuggested()并在DataGrid上设置Focusable = true和/或FocusManager.IsFocusScope = true,但似乎内部DataGrid整体上对处理复制/粘贴操作不感兴趣" ,只有它的行会重新查询命令CanExecute状态并相应地调用Execute.它还会忽略KeyBindings.
To fix the problem I tried calling CommandManager.InvalidateRequerySuggested() and setting Focusable=true and/or FocusManager.IsFocusScope=true on the DataGrid but it seems internally DataGrid as a whole is "not interested" in handling Copy/Paste operations, only it's rows are re-querying commands CanExecute state and calling Execute accordingly. It also ignores KeyBindings.
如何使DataGrid处理重新查询ApplicationCommand?
How to make DataGrid handle requerying ApplicationCommands?
请在下面找到我测试问题的示例:
Please find the example on which I tested the problem below:
<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>
以及背后的代码:
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>();
}
}
修改
Ayyappan Subramanian的仅代码解决方案与将要使用的应用程序最匹配.最终,由于我已经继承了网格,因为它具有自定义的copy + paste格式,因此我添加了一些代码来确保重点在3种情况下位于网格内:
The code only solution by Ayyappan Subramanian is the closest match to the application it will be used in. Eventually as I already inherit the grid because it has custom copy+paste format to work on, I added some code which ensures that focus is within a grid in 3 cases:
- 显示上下文菜单
- 当用户的子视觉对象没有焦点时,用户单击网格(空白)区域
- (特定于我们的应用程序的情况)用户单击网格导航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();
}
}
}
推荐答案
当ContextMenu打开时,您可以将焦点设置到网格上,这将启用所有菜单项. http://www.wpftutorial.net/RoutedCommandsInContextMenu.html
When the ContextMenu is openning then you can set the focus to the grid which will enable all the menu items. Good explanation is given in here http://www.wpftutorial.net/RoutedCommandsInContextMenu.html
有关实现粘贴的信息,请参阅SO帖子 WPF数据网格粘贴
Also for implementing the paste refer the SO post WPF datagrid pasting
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>
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();
}
}
这篇关于在WPF DataGrid中实现自定义复制和粘贴,当其中没有行时可以使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!