在 WPF 文本框中粘贴事件

我已经创建了一个继承 TextBox的自定义控件。这个自定义控件是一个数字 TextBox,只支持数字。

我使用 OnPreviewTextInput检查每个新输入的字符,看看这个字符是否是有效的输入。效果很好。但是,如果我将文本粘贴到 TextBox,则不会触发 OnPreviewTextInput

TextBox中捕获粘贴文本的最佳方法是什么?

另外,当后面的空间被按下时,我有一个问题,我不知道这将触发什么事件。OnPreviewTextInput没有发射!

有什么想法如何捕获粘贴的文本和返回空间事件在 WPF TextBox

49431 次浏览

对于退格,请检查 PreviewKeyDown事件

对于粘贴命令,将命令绑定添加到 ApplicationCommand。粘贴,并将参数设置为处理,如果您不希望对其进行任何操作:

<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Paste"
Executed="PasteExecuted" />
</Window.CommandBindings>

在代码背后:

private void PasteExecuted(object sender, ExecutedRoutedEventArgs e)
{
e.Handled = true;
}

这里有些代码,我随身带着以防万一,也许能帮到你。

public Window1()
{
InitializeComponent();


// "tb" is a TextBox
DataObject.AddPastingHandler(tb, OnPaste);
}


private void OnPaste(object sender, DataObjectPastingEventArgs e)
{
var isText = e.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
if (!isText) return;


var text = e.SourceDataObject.GetData(DataFormats.UnicodeText) as string;
...
}

试图拦截和捕获所有可能导致 TextBox 的单个事件的麻烦。要更改的文本属性是有许多这样的事件:

  • TextInput: 用户类型
  • 键入: 删除,退格,回车,输入法
  • 命令手势: Ctrl-X,Ctrl-Y,Ctrl-V,Ctrl-X
  • 鼠标向下: 粘贴按钮,剪切按钮,撤销按钮,..。
  • 单击: 当粘贴、剪切、撤消按钮具有本地焦点时按下空格键
  • RaiseEvent: 代码引发粘贴、剪切、撤消、重做命令
  • 无障碍: 语音指令,盲文键盘等

试图可靠地拦截所有这些是徒劳的。一个更好的解决方案是监视 TextBox。TextChanged 并拒绝您不喜欢的更改。

这个答案中,我展示了如何为被询问的特定场景实现 TextBoxRestrtion 类。同样的技术可以通用化,以便用于对 TextBox 控件施加任何限制。

例如,在您的情况下,您可以实现与代码中的 RestrictDeleteTo属性类似的 RestrictValidChars附加属性。除了内部循环将检查插入而不是删除之外,其他操作都是相同的。它将被这样使用:

<TextBox my:TextBoxRestriction.RestrictValidChars="0123456789" />

这只是一个如何处理的想法。有许多方法可以根据您的需要来构造代码。例如,您可以更改 TextBox 限制来调用自己的代码,以使用接受委托或包含事件的对象的附加属性进行验证。

有关如何在使用 TextBox 限制类时绑定 Text 属性的详细信息,请参阅另一个答案,这样当您不希望它触发限制时,它就不会触发限制。

您可以通过 PreviewKeyDown事件和 TextChanged事件来实现这一点。

PreviewKeyDown中捕捉粘贴操作

if(Key.V == e.Key && Keyboard.Modifiers == ModifierKeys.Control)
{
strPreviousString = this.txtNumber.Text;
bIsPasteOperation = true;
}

TextChanged事件中

if (true == bIsPasteOperation)
{


if (false == this.IsNumber(this.txtNumber.Text))
{
this.txtNumber.Text = strPreviousString;
e.Handled = true;
}
bIsPasteOperation = false;
}

IsNumber方法验证输入的文本是否为 Number

private bool IsNumber(string text)
{
int number;


//Allowing only numbers
if (!(int.TryParse(text, out number)))
{
return false;
}
return true
}

这对我很有用。我想在用户更改内容时更改文本框的颜色。

  • 接受数字,包括句点和负字符
  • 键入: 删除,退格,ctrl-V (粘贴) ,ctrl-X (剪切)
  • 鼠标右键点击粘贴和剪切

我能够通过以下三个活动实现这一目标:

    public bool IsDirty {
set {
if(value) {
txtValue.Background = Brushes.LightBlue;
} else {
txtValue.Background = IsReadOnly ? Brushes.White : Brushes.LightYellow;
}
}
get {
return txtValue.Background == Brushes.LightBlue;
}
}


private void PreviewTextInput(object sender, TextCompositionEventArgs e) {
TextBox tb = ((TextBox)sender);
string originalText = tb.Text;
string newVal = "";


//handle negative
if (e.Text=="-") {
if(originalText.IndexOf("-") > -1 || tb.CaretIndex != 0 || originalText == "" || originalText == "0") {
//already has a negative or the caret is not at the front where the - should go
//then ignore the entry
e.Handled = true;
return;
}


//put it at the front
newVal = e.Text + originalText;
} else {
//normal typed number
newVal = originalText + e.Text;
}


//check if it's a valid double if so then dirty
double dVal;
e.Handled = !double.TryParse(newVal, out dVal);
if(!e.Handled) {
IsDirty = true;
}
}


private void PreviewKeyUp(object sender, KeyEventArgs e) {
//handle paste
if ((Key.V == e.Key || Key.X == e.Key) && Keyboard.Modifiers ==  ModifierKeys.Control) {
IsDirty = true;
}
//handle delete and backspace
if (e.Key == Key.Delete || e.Key == Key.Back) {
IsDirty = true;
}
}




private void PreviewExecuted(object sender, ExecutedRoutedEventArgs e) {
//handle context menu cut/paste
if (e.Command == ApplicationCommands.Cut || e.Command == ApplicationCommands.Paste) {
IsDirty = true;
}
}

这可能不是你想要的确切答案,但是下面是如何处理粘贴的文本(如果用户使用上下文菜单粘贴,也可以这样做) :

InitializeComponent();


// "DescriptionTextBox" is a TextBox
DataObject.AddPastingHandler(DescriptionTextBox, OnDescriptionPaste);


private void OnDescriptionPaste(object sender, DataObjectPastingEventArgs e)
{
if (!e.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true))
return;


var pastedText = e.SourceDataObject.GetData(DataFormats.UnicodeText) as string;
if (string.IsNullOrEmpty(pastedText))
return;


var txtBox = (TextBox) sender;


var before = ""; //Text before pasted text
var after = txtBox.Text; //Text after pasted text


//Get before and after text
if (txtBox.CaretIndex > 0)
{
before = txtBox.Text.Substring(0, txtBox.CaretIndex);
after = txtBox.Text.Substring(txtBox.CaretIndex);
}


//Do custom logic for handling the pasted text.
//Split sentences ending with . into new line.
var parts = pastedText.Split(new []{'.'}, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length > 1)
{
pastedText = parts.Select(x => x.Trim()).ToArray().ToStringX(".\r\n");
pastedText += ".";
}


var newCaretIndex = before.Length + pastedText.Length;


e.CancelCommand(); //Cancels the paste, we do it manually
txtBox.Text = $"{before}{pastedText}{after}"; //Set new text
txtBox.CaretIndex = newCaretIndex; //Set new caret index
}

对于处理退格,请使用 PreviewKeyDown 事件。

下面的代码对我很有用。我希望它能帮到别人。

如果使用的是 Xceed RichTextBox 控件,请使用下面的代码。

 <xctk:RichTextBox Name="Description" CommandManager.PreviewExecuted="CommandExecuted_PreviewExecuted">


private void CommandExecuted_PreviewExecuted(object sender, RoutedEventArgs e)
{
Xceed.Wpf.Toolkit.RichTextBox richTextBox =  (Xceed.Wpf.Toolkit.RichTextBox)sender;


string rtbtext = StringFromRichTextBox(richTextBox);
if ((e as ExecutedRoutedEventArgs).Command == ApplicationCommands.Paste)
{
// verify that the textbox handled the paste command
if (Clipboard.GetText() > 2500)//Get copied text from clipboard
{
e.Handled = true;// prevent paste if length is more than 2500.
return;
}
}
}

如果使用 TextBlock,则使用以下代码

TextBlock textBlock = (TextBlock)sender;

而不是这样

Xceed.Wpf.Toolkit.RichTextBox richTextBox =  (Xceed.Wpf.Toolkit.RichTextBox)sender;

其余所有的代码可以保持上述为文本块以及。