views:

422

answers:

3

I have created a custom control inheriting TextBox. This custom control is a numeric TextBox, only supporting numbers.

I am using OnPreviewTextInput to check each new character being typed to see if the character is a valid input. This works great. However, if I paste the text into the TextBox, OnPreviewTextInput is not fired.

What is the best way to capture pasted text in a 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!

any ideas how to capture pasted text and back space events in WPF TextBox?

Thanks

+2  A: 

For backspace, please check the PreviewKeyDown event

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:

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

And in code behind:

private void PasteExecuted(object sender, ExecutedRoutedEventArgs e)
{
    e.Handled = true;
}
Arcturus
This disables paste rather than capturing the text. It also fails to address all the other ways text could be changed in a TextBox that code-zoop didn't ask about yet, such as accessibility, the Undo command, etc. I think it is better to use a general solution based on the TextChanged event because it cleanly handles all possible scenarios. I posted an answer that shows how to do this.
Ray Burns
+4  A: 

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, new DataObjectPastingEventHandler(OnPaste));
}

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

    var text = e.SourceDataObject.GetData(DataFormats.Text) as string;
    ...
}
Matt Hamilton
Wow, that worked far better than I imagined.
Jonathan Allen
+3  A: 

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:

  • TextInput: User types
  • KeyDown: Delete, Backspace, Enter, IME
  • Command Gestures: Ctrl-X, Ctrl-Y, Ctrl-V, Ctrl-X
  • MouseDown: Paste button, Cut button, Undo button, ...
  • Click: Space bar pressed when Paste, Cut, Undo buttons have local focus
  • RaiseEvent: Code raises Paste, Cut, Undo, Redo commands
  • 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.

In this answer I show how to implement a TextBoxRestriction class for the particular scenario being asked about. This same technique can be generalized for use with any restrictions you want to place on your TextBox control.

For example, in your case you might implemnt a RestrictValidChars attached property similarly to the RestrictDeleteTo property in that code. 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.

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.

Ray Burns