How can I undo a TextBox's text changes caused by a binding?

16,244

Solution 1

OK, started to leave a comment and realized it was an answer :)

TextBox.Undo() is intended to undo a user's interaction with the text box not a value change in the property it's bound to. A change in the property the text box is bound to will just update the value of the TextBox, this is a different change than a user edit via focus/keyboard. If you need to Undo changes to your bound properties you probably need to investigate adding an Undo/Redo stack to your application.

Solution 2

I was facing the same issue (needed to accept input upon Enter and revert to original value upon Escape) and was able to handle it this way:

  1. Set UpdateSourceTrigger of your TextBox.Text binding to Explicit.
  2. Handle KeyDown event of your TextBox and put the following code in there:

    if (e.Key == Key.Enter || e.Key == Key.Escape)
    {
      BindingExpression be = ((TextBox)sender).GetBindingExpression(TextBox.TextProperty);
    
      if (e.Key == Key.Enter)
      {
        if (be != null) be.UpdateSource();
      }
      else if (e.Key == Key.Escape)
      {
        if (be != null) be.UpdateTarget(); //cancels newly supplied value and reverts to the original value
      }
    }
    

I found this solution to be very elegant because it can be used in DataTemplates too. For example in my case I used it to allow in-place editing of ListBox items.

Solution 3

Assign directly to the TextBox:

textBox.SelectAll();
textBox.SelectedText = newText;

Solution 4

The TextBox will apply the changes to the internal undo stack if they are applied in such a way that they appear to have come from the user, like so:

        Clipboard.SetText("NewTextHere");
        TextBox.Paste();

It's a terrible workaround, as it kills whatever the user has on the clipboard (the restoring of which is pessimistically discussed here: How do I backup and restore the system clipboard in C#?) but I thought it might be worth having posted nonetheless.

Share:
16,244
H.B.
Author by

H.B.

Updated on August 05, 2022

Comments

  • H.B.
    H.B. over 1 year

    I have a TextBox to which i bound a string, if i now edit the text manually i will be able to undo those changes via TextBox.Undo(), if however i change the string and the TextBox's text is updated, i cannot undo those changes and the TextBox.CanUndo property will always be false.
    I suppose this might have to do with the complete replacement of the text rather than a modification of it.

    Any ideas on how i can get this to work?

    • Paul Wheeler
      Paul Wheeler over 13 years
      Have you tried Mode=TwoWay in your binding string?
    • Paul Wheeler
      Paul Wheeler over 13 years
      Never mind, TwoWay binding didn't work...
  • H.B.
    H.B. over 13 years
    I suspected that there would not be a simple solution, then again, it was not really a good idea to try and handle undoing by reverting a change that occurred in a control, rather than the model. Thanks.
  • H.B.
    H.B. almost 13 years
    I think you misunderstood the question, this is about the undo functionality, i know how to overwrite existing text. There is no newText, i was trying to restore an old value that should have been stored in the TextBox's history.
  • H.B.
    H.B. over 12 years
    An interesting idea at the very least. Thanks for your contribution.
  • Darkhydro
    Darkhydro about 10 years
    @H.B. This works great! Doesn't overwrite the clipboard, and allows you to undo text assignments that weren't made by the user! Thanks Pixar! H.B. I would consider rewarding Pixar with the answer, since this does exactly what you asked for in the question.
  • H.B.
    H.B. about 10 years
    @Darkhydro: No, unfortunately the question is about text changes caused by bindings, i.e. the property that the TextBox.Text is bound to is changed, no interaction with the TextBox itself.
  • Mike Marynowski
    Mike Marynowski over 7 years
    This can be easily expanded to do what you want with an attached property, so you can do something like UserText.Value="{Binding Xyz}" which updates the text using the method in this answer when it changes instead of using the Text property directly. I know this answer is old, but might be of interest to someone coming by it now.
  • H.B.
    H.B. over 7 years
    This does not cover the case where immediate value propagation is desired. You probably would want to save on lost focus as well. Still, it might be a decent work around in some cases.