简体   繁体   English

在 WPF 文本框中粘贴事件

[英]Paste Event in a WPF TextBox

I have created a custom control inheriting TextBox .我创建了一个继承TextBox的自定义控件。 This custom control is a numeric TextBox , only supporting numbers.这个自定义控件是一个数字TextBox ,只支持数字。

I am using OnPreviewTextInput to check each new character being typed to see if the character is a valid input.我正在使用OnPreviewTextInput检查输入的每个新字符,以查看该字符是否为有效输入。 This works great.这很好用。 However, if I paste the text into the TextBox , OnPreviewTextInput is not fired.但是,如果我将文本粘贴到TextBox ,则不会触发OnPreviewTextInput

What is the best way to capture pasted text in a TextBox ?TextBox捕获粘贴文本的最佳方法是什么?

Also, I have a problem when the back space is pressed, I can't figure out what event this will fire.另外,按下退格键时出现问题,我不知道这会触发什么事件。 OnPreviewTextInput is not fired! OnPreviewTextInput不会被触发!

Any ideas how to capture pasted text and back space events in WPF TextBox ?任何想法如何在 WPF TextBox捕获粘贴的文本和退格事件?

Here's some code I had lying around in case I ever needed it.这里有一些代码,以防我需要它。 Might help you.可能会帮助你。

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

The trouble with trying to intercept and trap all the individual events that might cause a TextBox.Text property to change is that there are many such events:试图拦截和捕获所有可能导致 TextBox.Text 属性更改的单个事件的麻烦在于有很多这样的事件:

  • TextInput: User types文本输入:用户类型
  • KeyDown: Delete, Backspace, Enter, IME KeyDown:删除、退格、回车、输入法
  • Command Gestures: Ctrl-X, Ctrl-Y, Ctrl-V, Ctrl-X命令手势:Ctrl-X、Ctrl-Y、Ctrl-V、Ctrl-X
  • MouseDown: Paste button, Cut button, Undo button, ... MouseDown:粘贴按钮、剪切按钮、撤消按钮、...
  • Click: Space bar pressed when Paste, Cut, Undo buttons have local focus单击:当粘贴、剪切、撤消按钮具有局部焦点时按下空格键
  • RaiseEvent: Code raises Paste, Cut, Undo, Redo commands RaiseEvent:代码引发粘贴、剪切、撤消、重做命令
  • Accessiblity: Voice commands, Braille keyboards, etc辅助功能:语音命令、盲文键盘等

Trying to reliably intercept all of these is an exercise in futility.试图可靠地拦截所有这些是徒劳的。 A much better solution is to monitor TextBox.TextChanged and reject changes that you don't like.更好的解决方案是监视 TextBox.TextChanged 并拒绝您不喜欢的更改。

In this answer I show how to implement a TextBoxRestriction class for the particular scenario being asked about.这个答案中,我展示了如何为所询问的特定场景实现 TextBoxRestriction 类。 This same technique can be generalized for use with any restrictions you want to place on your TextBox control.这种相同的技术可以推广用于您想要在 TextBox 控件上设置的任何限制。

For example, in your case you might implemnt a RestrictValidChars attached property similarly to the RestrictDeleteTo property in that code.例如,在您的情况下,您可能会在该代码中实现类似于RestrictDeleteTo属性的RestrictValidChars附加属性。 It would be the same except that the inner loop would check inserts, not deletes.除了内部循环将检查插入而不是删除之外,它是相同的。 It would be used like this:它将像这样使用:

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

This is just an idea of how it could be handled.这只是如何处理它的一个想法。 There are many ways to structure your code depending on what you want.根据您的需要,有多种方法可以构建代码。 For example you could change TextBoxRestriction to call your own code to validate using an attached property that takes a delegate or an object containing an event.例如,您可以更改 TextBoxRestriction 以调用您自己的代码以使用带有委托或包含事件的对象的附加属性进行验证。

See the other answer for details on how to bind the Text property when you are using the TextBoxRestriction class so it won't trigger the restriction when you don't want it to.有关如何在使用 TextBoxRestriction 类时绑定 Text 属性的详细信息,请参阅其他答案,以便在您不希望时不会触发限制。

For backspace, please check the PreviewKeyDown event对于退格,请检查PreviewKeyDown事件

For paste command, add a command binding to the ApplicationCommands.Paste, and set the argument to handled, if you do not wish to do anything with it:对于粘贴命令,将命令绑定添加到 ApplicationCommands.Paste,并将参数设置为已处理,如果您不想对其进行任何操作:

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

And in code behind:在后面的代码中:

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

This might not be the exact answer your looking for but here is how to handle pasted text (this also works if user pasted using a the context menu):这可能不是您正在寻找的确切答案,但这里是如何处理粘贴的文本(如果用户使用上下文菜单粘贴,这也适用):

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
        }

For handling backspace use PreviewKeyDown event.对于处理退格使用 PreviewKeyDown 事件。

You can achieve this with PreviewKeyDown event and TextChanged event.您可以使用PreviewKeyDown事件和TextChanged事件来实现这一点。

In PreviewKeyDown capture the Paste operationPreviewKeyDown捕获 Paste 操作

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

In TextChanged eventTextChanged事件中

if (true == bIsPasteOperation)
{

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

Where IsNumber method validates the entered text is Number or not其中IsNumber方法验证输入的文本是否为数字

private bool IsNumber(string text)
{
   int number;

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

This works pretty good for me.这对我来说非常好。 I wanted to changed the color of the textbox when the user made a change to the contents.当用户更改内容时,我想更改文本框的颜色。

  • accept numbers, including period and negative characters接受数字,包括句号和负数
  • keys typed: delete, backspace, ctrl-V (paste), ctrl-X (cut)键入的键:删除、退格、ctrl-V(粘贴)、ctrl-X(剪切)
  • right mouse click for paste and cut鼠标右键单击粘贴和剪切

I was able to achieve it with the 3 events below:我能够通过以下 3 个事件实现它:

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

The below code worked for me.下面的代码对我有用。 I hope, it will help someone.我希望,它会帮助某人。

Use the below code if you are using Xceed RichTextBox control.如果您使用 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;
            }
        }
    } 

If you are using TextBlock, then use below code如果您使用的是 TextBlock,请使用以下代码

TextBlock textBlock = (TextBlock)sender;

instead of this而不是这个

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

Rest all codes can remain the same as above for TextBlock as well.对于 TextBlock,其余所有代码也可以保持与上述相同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM