Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TextBox TextChanged event on programmatic versus user change of text contents

Tags:

I would like to differentiate between changing the text programmatically (for example in a button click handler event) and user input (typing, cutting and pasting text).
Is it possible?

like image 532
Greg Avatar asked Sep 01 '11 13:09

Greg


People also ask

Which event is generated when TextBox text is changed?

The event handler is called whenever the contents of the TextBox control are changed, either by a user or programmatically. This event fires when the TextBox control is created and initially populated with text.

What event's for a TextBox can determine when the text inside the control has been modified?

To see all changes to the text in a TextBox, you can handle the TextChanged event. This event fires after the text has changed. In the TextChanged event, the easiest way to inspect the text being entered is to just look at the value of the Text property.

What is text change event in Visual Basic?

The TextChanged event occurs whenever the text is changed, by the user or programmatically. In your case your A_TextChanged eventhandler is changing B's text, so that B_TextChanged is called … Adding some Debug.


2 Answers

User input in a TextBox can be identified with

  • Typing : PreviewTextInput event
  • Backspace, Delete, Enter : PreviewKeyDown event
  • Pasting : DataObject.PastingEvent

Combining these three events with a bool flag to indicate if any of the above occured before the TextChanged event and you'll know the reason for the update.

Typing and Pasting are easy, but Backspace doesn't always trigger TextChanged (if no text is selected and the cursor is at position 0 for example). So some logic is needed in PreviewTextInput.

Here is an Attached Behavior that implements the logic above and executes a command with a bool flag when TextChanged is raised.

<TextBox ex:TextChangedBehavior.TextChangedCommand="{Binding TextChangedCommand}" /> 

And in code you can find out the source for the update like

private void TextChanged_Executed(object parameter) {     object[] parameters = parameter as object[];     object sender = parameters[0];     TextChangedEventArgs e = (TextChangedEventArgs)parameters[1];     bool userInput = (bool)parameters[2];      if (userInput == true)     {         // User input update..     }     else     {         // Binding, Programatic update..     } } 

Here is a small sample project demonstrating the effect: SourceOfTextChanged.zip

TextChangedBehavior

public class TextChangedBehavior {     public static DependencyProperty TextChangedCommandProperty =         DependencyProperty.RegisterAttached("TextChangedCommand",                                             typeof(ICommand),                                             typeof(TextChangedBehavior),                                             new UIPropertyMetadata(TextChangedCommandChanged));      public static void SetTextChangedCommand(DependencyObject target, ICommand value)     {         target.SetValue(TextChangedCommandProperty, value);     }      // Subscribe to the events if we have a valid command     private static void TextChangedCommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)     {         TextBox textBox = target as TextBox;         if (textBox != null)         {             if ((e.NewValue != null) && (e.OldValue == null))             {                 textBox.PreviewKeyDown += textBox_PreviewKeyDown;                 textBox.PreviewTextInput += textBox_PreviewTextInput;                 DataObject.AddPastingHandler(textBox, textBox_TextPasted);                 textBox.TextChanged += textBox_TextChanged;             }             else if ((e.NewValue == null) && (e.OldValue != null))             {                 textBox.PreviewKeyDown -= textBox_PreviewKeyDown;                 textBox.PreviewTextInput -= textBox_PreviewTextInput;                 DataObject.RemovePastingHandler(textBox, textBox_TextPasted);                 textBox.TextChanged -= textBox_TextChanged;             }         }     }      // Catches User input     private static void textBox_PreviewTextInput(object sender, TextCompositionEventArgs e)     {         TextBox textBox = sender as TextBox;         SetUserInput(textBox, true);     }     // Catches Backspace, Delete, Enter     private static void textBox_PreviewKeyDown(object sender, KeyEventArgs e)     {         TextBox textBox = sender as TextBox;         if (e.Key == Key.Return)         {             if (textBox.AcceptsReturn == true)             {                 SetUserInput(textBox, true);             }         }         else if (e.Key == Key.Delete)         {             if (textBox.SelectionLength > 0 || textBox.SelectionStart < textBox.Text.Length)             {                 SetUserInput(textBox, true);             }         }         else if (e.Key == Key.Back)         {             if (textBox.SelectionLength > 0 || textBox.SelectionStart > 0)             {                 SetUserInput(textBox, true);             }         }     }     // Catches pasting     private static void textBox_TextPasted(object sender, DataObjectPastingEventArgs e)     {         TextBox textBox = sender as TextBox;         if (e.SourceDataObject.GetDataPresent(DataFormats.Text, true) == false)         {             return;         }         SetUserInput(textBox, true);     }     private static void textBox_TextChanged(object sender, TextChangedEventArgs e)     {         TextBox textBox = sender as TextBox;         TextChangedFired(textBox, e);         SetUserInput(textBox, false);     }      private static void TextChangedFired(TextBox sender, TextChangedEventArgs e)     {         ICommand command = (ICommand)sender.GetValue(TextChangedCommandProperty);         object[] arguments = new object[] { sender, e, GetUserInput(sender) };         command.Execute(arguments);     }      #region UserInput      private static DependencyProperty UserInputProperty =         DependencyProperty.RegisterAttached("UserInput",                                             typeof(bool),                                             typeof(TextChangedBehavior));     private static void SetUserInput(DependencyObject target, bool value)     {         target.SetValue(UserInputProperty, value);     }     private static bool GetUserInput(DependencyObject target)     {         return (bool)target.GetValue(UserInputProperty);     }      #endregion // UserInput } 
like image 145
Fredrik Hedblad Avatar answered Sep 23 '22 16:09

Fredrik Hedblad


Depending on your exact demands you can use TextBox.IsFocused in the TextChanged event to determine manual input. This will obviously not cover all ways of programmatical changes, but works for a lot of examples just fine and is a pretty clean and save way of doing so.

Basically this works if:
...the programmatical changes are all based on a manual change (e.g. a Button press).
It will not work if:
...the programmatical changes are completely based on code (e.g. a Timer).

Code example:

textBox.TextChanged += (sender, args) =>     if (textBox.IsFocused)     {         //do something for manual input     }     else     {         //do something for programmatical input     } } 
like image 40
Tim Pohlmann Avatar answered Sep 23 '22 16:09

Tim Pohlmann