简体   繁体   中英

C# and Winforms TextBox control: How do I get the text change?

I have an event handler for the TextBox.TextChanged event on a form of mine. In order to support undo, I'd like to figure out exactly what has changed in the TextBox, so that I can undo the change if the user asks for it. (I know the builtin textbox supports undo, but I'd like to have a single undo stack for the whole application)

Is there a reasonable way to do that? If not, is there a better way of supporting such an undo feature?

EDIT: Something like the following seems to work -- are there any better ideas? (It's times like this that I really wish .NET had something like the STL's std::mismatch algorithm...

    class TextModification
    {
        private string _OldValue;
        public string OldValue
        {
            get
            {
                return _OldValue;
            }
        }
        private string _NewValue;
        public string NewValue
        {
            get
            {
                return _NewValue;
            }
        }
        private int _Position;
        public int Position
        {
            get
            {
                return _Position;
            }
        }
        public TextModification(string oldValue, string newValue, int position)
        {
            _OldValue = oldValue;
            _NewValue = newValue;
            _Position = position;
        }
        public void RevertTextbox(System.Windows.Forms.TextBox tb)
        {
            tb.Text = tb.Text.Substring(0, Position) + OldValue + tb.Text.Substring(Position + NewValue.Length);
        }
    }

    private Stack<TextModification> changes = new Stack<TextModification>();
    private string OldTBText = "";
    private bool undoing = false;

    private void Undoit()
    {
        if (changes.Count == 0)
            return;
        undoing = true;
        changes.Pop().RevertTextbox(tbFilter);
        OldTBText = tbFilter.Text;
        undoing = false;
    }

    private void UpdateUndoStatus(TextBox caller)
    {
        int changeStartLocation = 0;
        int changeEndTBLocation = caller.Text.Length;
        int changeEndOldLocation = OldTBText.Length;
        while (changeStartLocation < Math.Min(changeEndOldLocation, changeEndTBLocation) &&
            caller.Text[changeStartLocation] == OldTBText[changeStartLocation])
            changeStartLocation++;
        while (changeEndTBLocation > 1 && changeEndOldLocation > 1 &&
            caller.Text[changeEndTBLocation-1] == OldTBText[changeEndOldLocation-1])
        {
            changeEndTBLocation--;
            changeEndOldLocation--;
        }
        changes.Push(new TextModification(
            OldTBText.Substring(changeStartLocation, changeEndOldLocation - changeStartLocation),
            caller.Text.Substring(changeStartLocation, changeEndTBLocation - changeStartLocation),
            changeStartLocation));
        OldTBText = caller.Text;
    }

    private void tbFilter_TextChanged(object sender, EventArgs e)
    {
        if (!undoing)
            UpdateUndoStatus((TextBox)sender);
    }

You might be better off using the Enter and Leave events instead. When entering, store the current text in a class variable, then when leaving compare the new text to the old.

Yes, don't tie it directly to the textbox. Your forms' state should be in some model object somewhere that isn't directly tied to the form (MVC is one way to do this, MVVM is another). By decoupling them like that, you can compare the new textbox value to the current model value whenever a change request comes in.

Actually, all I can think of is having some kind of collection where you store different string versions (so you can undo many times, not just once). I would store the reference to TextBox's collections in TextBox.Tag, so it is straightforward to store/use it.

Last but not least, you update your collection of strings during the event TextChange. With no much work, you can maintain a full history, gettinjg the previous value from your own structure.

This is probably overkill for what you're trying to accomplish, but CSLA support n-level undo. CSLA is a great business objects framework written by Rocky Lhotka. The business objects handle the undo history and it flows to the UI through data binding.

Switching your app to use CSLA would be a big commitment, but another option would be to look through the freely available source code to see how he implemented it.

I am actually making an own Syntax-Highlight-System so I also need to know the changed text. My solution is to watch for an enter or space or an depositioning of the cursor. As WinForms provide the Keydown event I used the KeyEventArguments (e) and converted them to a char.

After that I storage the char into a string like :

string i=""; i+=convertedToChar; // convertedToChar = kc.ConvertToString(e.KeyData)

And as soon as there is a enter or space or depositioning - "event" I delete the string.

Result: If a user enters a few chars and hit space I am able to read the last chars (till the last space). An advantage would be the fact that you can use any delimiter char for that (as soon as they are storaged and provided by e.KeyCode)

However I hope that this is a solution for everybody watching this after 9years :D. It´s never too late.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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