samy
samy

Reputation: 1969

Undo feature for Textbox

I'm working on a very simple Undo feature for a TextBox and I've got a weird problem. When I try to take strings from the Stackthat holds all the changes and put them inside the Textbox I don't see any changes.

I made a little Debug Label to check if this is really working or not. I found out that it is working in the label, but in the Textbox it uses its own Undo functions.

Is there a way to cancel or override the Textbox Undo and use my own function?

Here is sample code from the change I made:

 private void Form1_KeyDown(object sender, KeyEventArgs e)
        if (e.KeyCode == Keys.Z && (ModifierKeys & Keys.Control) == Keys.Control)
            {
                nameTextBox.Text = undoName.GetLastChange(); //--> not working

                undoDebuglabel.Text = undoName.GetLastChange(); --> working
            }
}

The GetLastChange() is getting the info from a Stack inside the class.

It's like the Textbox is not letting me to see the changes. Could it be because I'm using the same shortcut, CTRL + Z to do it?

Upvotes: 5

Views: 9694

Answers (3)

Billy Willoughby
Billy Willoughby

Reputation: 856

I expanded on Geregor's and Keyboard's answer. I couldn't get the code to work as posted in Visual Studio 2017, so made a minor tweak. Then I wanted a Redo function as well, so I added a few more lines of code. This seems to work fine in my testing and I thought I would share since it answers the question and expands.

This code adds undo, redo, and you can hold down the Ctrl-Z or Ctrl-Y to have it "run" through the list.

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace CodeBuilder
{
    public class HistoryTextBox : System.Windows.Forms.TextBox
    {
        bool ignoreChange = true;
        List<string> storageUndo = null;
        List<string> storageRedo = null;


        protected override void OnCreateControl()
        {
            base.OnCreateControl();
            storageUndo = new List<string>();
            storageRedo = new List<string>();
            ignoreChange = false;
        }

        protected override void OnTextChanged(EventArgs e)
        {
            base.OnTextChanged(e);
            if (!ignoreChange)
            {
                this.ClearUndo();
                if (storageUndo.Count > 2048) storageUndo.RemoveAt(0);
                if (storageRedo.Count > 2048) storageRedo.RemoveAt(0);

                storageUndo.Add(this.Text);
            }
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {

            if (e.KeyCode == Keys.Z && (ModifierKeys & Keys.Control) == Keys.Control)
            {
                this.ClearUndo();
                ignoreChange = true;
                this.Undo();
                ignoreChange = false;
            }
            else if (e.KeyCode == Keys.Y && (ModifierKeys & Keys.Control) == Keys.Control)
            {
                this.ClearUndo();
                ignoreChange = true;
                this.Redo();
                ignoreChange = false;
            }
            else
            {
                base.OnKeyDown(e);
            }
        }

        public void Redo()
        {
            if (storageRedo.Count > 0)
            {
                this.ignoreChange = true;
                this.Text = storageRedo[storageRedo.Count - 1];
                storageUndo.Add(this.Text);
                storageRedo.RemoveAt(storageRedo.Count - 1);
                this.ignoreChange = false;
            }
        }

        public new void Undo()
        {
            if (storageUndo.Count > 0)
            {
                this.ignoreChange = true;
                storageRedo.Add(this.Text);
                this.Text = storageUndo[storageUndo.Count - 1];
                storageUndo.RemoveAt(storageUndo.Count - 1);
                this.ignoreChange = false;
            }
        }
    }
}

Upvotes: 2

keyboardP
keyboardP

Reputation: 69372

Clear the Textbox's own stack by using the ClearUndo method. Try this:

nameTextBox.ClearUndo();
nameTextBox.Text = undoName.GetLastChange();

Upvotes: 4

Gregor Primar
Gregor Primar

Reputation: 6805

You can create your own TextBox to handle history by inheriting from System.Windows.Forms.TextBox. Take a look at my sample:

public class HistoryTextBox: System.Windows.Forms.TextBox
{
    bool ignoreChange = false;
    List<string> storage = null;


    protected override void OnCreateControl()
    {
        base.OnCreateControl();
        //init storage...
        storage = new List<string>();
    }

    protected override void OnTextChanged(EventArgs e)
    {
        base.OnTextChanged(e);
        //save change to storage...
        if (!ignoreChange)
        {
            storage.Add(this.Text);
        }
    }

    public void Undo()
    {
        if (storage.Count > 0)
        {
            this.ignoreChange = true;
            this.Text = storage[storage.Count - 1];
            storage.RemoveAt(storage.Count - 1);
            this.ignoreChange = false;
        }
    }
}

Everytime you need to undo just call:

historyTextBox1.Undo();

This class will give you multiple histoy records.

Upvotes: 3

Related Questions