Разрешение "вставки данных" в текстовое поле WPF

Я пытаюсь перехватить данные, вставленные в текстовое поле WPF.

например, пользователь создает снимок экрана с помощью программы "ножницы" Windows, что автоматически помещает данные изображения в буфер обмена. Идея здесь в том, чтобы позвольте пользователю просто 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 не позволяет вставлять что-либо еще, поэтому он никогда не достигает этого кода, если это не текст.

мне интересно, есть ли свойство, которое нужно установить в текстовом поле, или что-то еще это позволит вставлять данные (даже если текстовое поле никогда не сможет отображать эти данные)

Если нет, то какие элементы пользовательского интерфейса позволяют вставлять данные, так как я мог бы использовать это в своих интересах.

обновление Кто-то отправлено мне, что мне придется использовать 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();
        }
    }
}

Это позволяет мне вставлять изображения и файлы в текстовое поле, но только используя клавиши CTRL+V, а не контекстное меню по умолчанию текстового поля.

поэтому мне все еще интересно узнать, есть ли лучший/более простой способ

обновление 2 На основании решение Даниэля, которое работает очень хорошо, я обновил OnAttached:

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

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

и удалил PreviewKeyDownHandler.

1 ответов


можно использовать CommandManager.PreviewExecuted и CommandManager.PreviewCanExecute маршрутизированные события для обработки логики вставки.

например, предположим, что вы хотите принять изображение из буфера обмена, когда пользователь пытается вставить его в текстовое поле. Поэтому сначала определите методы, которые будут обрабатывать оба события:

    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. По умолчанию текстовое поле будет принимать текст только как "пастируемый" контент, поэтому вам нужно как-то пометить этот контент, чтобы вставить его.

изменить: Кроме того, хорошей практикой является удаление "слушателей" из события, если вы можете. Когда вы используете поведение, вы можете сделать это, переопределив метод OnDetaching в своем поведении. Это может предотвратить утечку памяти, если события не Слабые События:

    protected override void OnDetaching()
    {
        base.OnDetaching();
        CommandManager.RemovePreviewExecutedHandler(myTextBox, onPreviewExecuted);
        CommandManager.RemovePreviewCanExecuteHandler(myTextBox, onPreviewCanExecute);
    }