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 を使用する必要があると投稿しましたが
、これは私が使用できるものではないため、別の (ややハッキーな) アプローチを取ることにしました。

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();
        }
    }
}

これにより、画像とファイルを TextBox に貼り付けることができますが、TextBox のデフォルトのコンテキスト メニューを使用せずに CTRL+V キーのみを使用できます。

だから私はまだより良い/より簡単な方法があるかどうかを知りたいと思っています

更新 2 非常にうまく機能する Daniel のソリューションに基づいて、OnAttached を更新しました。

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

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

また、PreviewKeyDownHandler を削除しました。

4

1 に答える 1

9

You can use CommandManager.PreviewExecuted and CommandManager.PreviewCanExecute routed events to handle your pasting logic.

For example, let's suppose you want to accept an image from the clipboard when a user tries to paste it into your TextBox. So first, define the methods that will handle both events:

    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;
            }

        }
    }

Then, you have to associate those methods with the routed events (this could go in the constructor, for example):

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

And it should work with both the keyboard shortcut and the menu 'button'.

It is important to handle the PreviewCanExecute event. By default, the TextBox will only accept text as a 'pasteable' content, so you need to mark that content somehow in order to paste it.

EDIT: Also, it is a good practice to remove the 'listeners' from the event if you can. As you're using behaviors, you can do this by overriding the 'OnDetaching' method in your behavior. This could prevent memory leaks if the events are not Weak Events:

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