Разрешение "вставки данных" в текстовое поле 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);
}