簡體   English   中英

在 WPF 文本框中粘貼事件

[英]Paste Event in a WPF TextBox

我創建了一個繼承TextBox的自定義控件。 這個自定義控件是一個數字TextBox ,只支持數字。

我正在使用OnPreviewTextInput檢查輸入的每個新字符,以查看該字符是否為有效輸入。 這很好用。 但是,如果我將文本粘貼到TextBox ,則不會觸發OnPreviewTextInput

TextBox捕獲粘貼文本的最佳方法是什么?

另外,按下退格鍵時出現問題,我不知道這會觸發什么事件。 OnPreviewTextInput不會被觸發!

任何想法如何在 WPF TextBox捕獲粘貼的文本和退格事件?

這里有一些代碼,以防我需要它。 可能會幫助你。

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.Text 屬性更改的單個事件的麻煩在於有很多這樣的事件:

  • 文本輸入:用戶類型
  • KeyDown:刪除、退格、回車、輸入法
  • 命令手勢:Ctrl-X、Ctrl-Y、Ctrl-V、Ctrl-X
  • MouseDown:粘貼按鈕、剪切按鈕、撤消按鈕、...
  • 單擊:當粘貼、剪切、撤消按鈕具有局部焦點時按下空格鍵
  • RaiseEvent:代碼引發粘貼、剪切、撤消、重做命令
  • 輔助功能:語音命令、盲文鍵盤等

試圖可靠地攔截所有這些是徒勞的。 更好的解決方案是監視 TextBox.TextChanged 並拒絕您不喜歡的更改。

這個答案中,我展示了如何為所詢問的特定場景實現 TextBoxRestriction 類。 這種相同的技術可以推廣用於您想要在 TextBox 控件上設置的任何限制。

例如,在您的情況下,您可能會在該代碼中實現類似於RestrictDeleteTo屬性的RestrictValidChars附加屬性。 除了內部循環將檢查插入而不是刪除之外,它是相同的。 它將像這樣使用:

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

這只是如何處理它的一個想法。 根據您的需要,有多種方法可以構建代碼。 例如,您可以更改 TextBoxRestriction 以調用您自己的代碼以使用帶有委托或包含事件的對象的附加屬性進行驗證。

有關如何在使用 TextBoxRestriction 類時綁定 Text 屬性的詳細信息,請參閱其他答案,以便在您不希望時不會觸發限制。

對於退格,請檢查PreviewKeyDown事件

對於粘貼命令,將命令綁定添加到 ApplicationCommands.Paste,並將參數設置為已處理,如果您不想對其進行任何操作:

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

在后面的代碼中:

private void PasteExecuted(object sender, ExecutedRoutedEventArgs e)
{
    e.Handled = 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 事件。

您可以使用PreviewKeyDown事件和TextChanged事件來實現這一點。

PreviewKeyDown捕獲 Paste 操作

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方法驗證輸入的文本是否為數字

private bool IsNumber(string text)
{
   int number;

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

這對我來說非常好。 當用戶更改內容時,我想更改文本框的顏色。

  • 接受數字,包括句號和負數
  • 鍵入的鍵:刪除、退格、ctrl-V(粘貼)、ctrl-X(剪切)
  • 鼠標右鍵單擊粘貼和剪切

我能夠通過以下 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;
        }
    }

下面的代碼對我有用。 我希望,它會幫助某人。

如果您使用 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;

對於 TextBlock,其余所有代碼也可以保持與上述相同。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM