我试图拦截粘贴到WPF文本框中的数据.
例如,用户使用Windows剪切工具创建屏幕捕获,该工具会自动将图像数据放在剪贴板上.这里的想法是允许用户在TextBox上简单地按CTRL + V,这样我就可以拦截它,检查它是否是数据,然后随意做任何事情.
public class PasteBehavior : Behavior<UIElement>
{
protected override void OnAttached()
{
base.OnAttached();
DataObject.AddPastingHandler(AssociatedObject, new DataObjectPastingEventHandler(OnPaste));
}
protected override void OnDetaching()
{
base.OnDetaching();
}
private void OnPaste(object sender, DataObjectPastingEventArgs e)
{
if (e.SourceDataObject.GetDataPresent(DataFormats.Text))
return;
var formats = e.SourceDataObject.GetFormats();
foreach (var format in formats)
Console.WriteLine(format);
}
}
Run Code Online (Sandbox Code Playgroud)
使用上面的行为,当文本粘贴到TextBox中时,代码会被触发,但看起来TextBox不允许粘贴任何其他内容,因此如果它不是文本,它甚至都不会到达此代码.
我想知道,是否有需要在TextBox上设置的属性,或者其他允许粘贴数据的属性(即使TextBox永远不能显示该数据)
如果没有,那么哪些UI元素允许粘贴数据,因为我也可以将其用于我的优势.
更新
有人告诉我,我必须使用RichTextBox来允许这样粘贴
,这不是我可以使用的东西,所以我决定采取一种不同的(有些hacky)方法:
public class PasteBehavior : Behavior<UIElement>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewKeyDown += AssociatedObject_PreviewKeyDown;
}
void AssociatedObject_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (Keyboard.Modifiers == ModifierKeys.Control && e.Key == Key.V)
{
if (Clipboard.ContainsData(DataFormats.Dib))
{
using (var stream = new MemoryStream())
{
var image = Clipboard.GetImage();
var message = new ImagePastedMessage()
{
ImageData = GetImagePngData(image)
};
Messenger.Default.Send(message);
}
e.Handled = true;
}
else if (Clipboard.ContainsFileDropList())
{
var results = Clipboard.GetFileDropList();
var filenames = new string[results.Count];
results.CopyTo(filenames, 0);
var message = new FilesDroppedMessage()
{
Filenames = filenames
};
Messenger.Default.Send(message);
e.Handled = true;
}
}
}
protected override void OnDetaching()
{
base.OnDetaching();
}
private byte[] GetImagePngData(BitmapSource source)
{
using (var stream = new MemoryStream())
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.Save(stream);
return stream.ToArray();
}
}
}
Run Code Online (Sandbox Code Playgroud)
这允许我将图像和文件粘贴到TextBox中,但仅使用CTRL + V键,而不是使用TextBox的默认上下文菜单.
所以我仍然有兴趣知道是否有更好/更简单的方法
更新2 基于Daniel的解决方案,该方法非常有效,我已经更新了OnAttached:
protected override void OnAttached()
{
base.OnAttached();
CommandManager.AddPreviewCanExecuteHandler(AssociatedObject, onPreviewCanExecute);
CommandManager.AddPreviewExecutedHandler(AssociatedObject, onPreviewExecuted);
}
Run Code Online (Sandbox Code Playgroud)
并删除了PreviewKeyDownHandler.
您可以使用CommandManager.PreviewExecuted和CommandManager.PreviewCanExecute路由事件来处理粘贴逻辑.
例如,假设您想要在用户尝试将其粘贴到TextBox中时从剪贴板接受图像.首先,定义将处理这两个事件的方法:
private void onPreviewCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
// In this case, we just say it always can be executed (only for a Paste command), but you can
// write some checks here
if (e.Command == ApplicationCommands.Paste)
{
e.CanExecute = true;
e.Handled = true;
}
}
private void onPreviewExecuted(object sender, ExecutedRoutedEventArgs e)
{
// If it is a paste command..
if (e.Command == ApplicationCommands.Paste)
{
// .. and the clipboard contains an image
if (Clipboard.ContainsImage())
{
// proccess it somehow
e.Handled = true;
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后,您必须将这些方法与路由事件相关联(例如,这可以在构造函数中):
CommandManager.AddPreviewExecutedHandler(myTextBox, onPreviewExecuted);
CommandManager.AddPreviewCanExecuteHandler(myTextBox, onPreviewCanExecute);
Run Code Online (Sandbox Code Playgroud)
它应该与键盘快捷键和菜单"按钮"一起使用.
处理PreviewCanExecute事件非常重要.默认情况下,TextBox仅接受文本作为"可粘贴"内容,因此您需要以某种方式标记该内容以便粘贴它.
编辑:此外,如果可以的话,最好从活动中删除"听众".当您使用行为时,可以通过覆盖行为中的"OnDetaching"方法来实现.如果事件不是弱事件,这可以防止内存泄漏:
protected override void OnDetaching()
{
base.OnDetaching();
CommandManager.RemovePreviewExecutedHandler(myTextBox, onPreviewExecuted);
CommandManager.RemovePreviewCanExecuteHandler(myTextBox, onPreviewCanExecute);
}
Run Code Online (Sandbox Code Playgroud)