8

我正在尝试拦截粘贴到 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);
    }
}

使用上面的行为,当文本粘贴到 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();
        }
    }
}

这允许我将图像和文件粘贴到文本框中,但只能使用 CTRL+V 键,而不是使用文本框的默认上下文菜单。

所以我仍然有兴趣知道是否有更好/更简单的方法

更新 2 基于 Daniel 的解决方案,效果非常好,我更新了 OnAttached:

protected override void OnAttached()
{
    base.OnAttached();

    CommandManager.AddPreviewCanExecuteHandler(AssociatedObject, onPreviewCanExecute);
    CommandManager.AddPreviewExecutedHandler(AssociatedObject, onPreviewExecuted);
}

并删除了 PreviewKeyDownHandler。

4

1 回答 1

9

您可以使用CommandManager.PreviewExecutedCommandManager.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;
            }

        }
    }

然后,您必须将这些方法与路由事件相关联(例如,这可以在构造函数中进行):

CommandManager.AddPreviewExecutedHandler(myTextBox, onPreviewExecuted);
CommandManager.AddPreviewCanExecuteHandler(myTextBox, onPreviewCanExecute);

它应该与键盘快捷键和菜单“按钮”一起使用。

处理 PreviewCanExecute 事件很重要。默认情况下,TextBox 只接受文本作为“可粘贴”内容,因此您需要以某种方式标记该内容才能粘贴它。

编辑:此外,如果可以的话,从事件中删除“听众”是一个好习惯。当您使用行为时,您可以通过在您的行为中覆盖“OnDetaching”方法来做到这一点。如果事件不是弱事件,这可以防止内存泄漏:

    protected override void OnDetaching()
    {
        base.OnDetaching();
        CommandManager.RemovePreviewExecutedHandler(myTextBox, onPreviewExecuted);
        CommandManager.RemovePreviewCanExecuteHandler(myTextBox, onPreviewCanExecute);
    }
于 2012-12-07T05:50:26.797 回答