Ok, I wrote this today. It is in C#. Basically you are going to want to keep track of the changes as they are happening. I wrote a seperate class (static) to do this. Look at the code. If you can't figure it out, I will convert it to VB for you.

Here is the form code:
Code:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace Undo_Sample
{
	public class Form1 : System.Windows.Forms.Form
	{
		private System.Windows.Forms.TextBox textBox1;
		private System.Windows.Forms.TextBox textBox2;
		private System.Windows.Forms.TextBox textBox3;
		private System.Windows.Forms.Button button1;
		private System.ComponentModel.Container components = null;

		public Form1()
		{
			InitializeComponent();
		}

		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		private void InitializeComponent()
		{
			this.textBox1 = new System.Windows.Forms.TextBox();
			this.textBox2 = new System.Windows.Forms.TextBox();
			this.textBox3 = new System.Windows.Forms.TextBox();
			this.button1 = new System.Windows.Forms.Button();
			this.SuspendLayout();
			// 
			// textBox1
			// 
			this.textBox1.Location = new System.Drawing.Point(8, 16);
			this.textBox1.Name = "textBox1";
			this.textBox1.TabIndex = 0;
			this.textBox1.Text = "";
			this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged);
			// 
			// textBox2
			// 
			this.textBox2.Location = new System.Drawing.Point(8, 40);
			this.textBox2.Name = "textBox2";
			this.textBox2.TabIndex = 1;
			this.textBox2.Text = "";
			this.textBox2.TextChanged += new System.EventHandler(this.textBox2_TextChanged);
			// 
			// textBox3
			// 
			this.textBox3.Location = new System.Drawing.Point(8, 64);
			this.textBox3.Name = "textBox3";
			this.textBox3.TabIndex = 2;
			this.textBox3.Text = "";
			this.textBox3.TextChanged += new System.EventHandler(this.textBox3_TextChanged);
			// 
			// button1
			// 
			this.button1.Location = new System.Drawing.Point(24, 104);
			this.button1.Name = "button1";
			this.button1.TabIndex = 3;
			this.button1.Text = "Undo";
			this.button1.Click += new System.EventHandler(this.button1_Click);
			// 
			// Form1
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(152, 190);
			this.Controls.AddRange(new System.Windows.Forms.Control[] {
																		  this.button1,
																		  this.textBox3,
																		  this.textBox2,
																		  this.textBox1});
			this.Name = "Form1";
			this.Text = "Form1";
			this.Load += new System.EventHandler(this.Form1_Load);
			this.ResumeLayout(false);

		}

		[STAThread]
		static void Main() 
		{
			Application.Run(new Form1());
		}

// All that above is Visual Studio form creation code.  Below is the code required
// to do what you want.

		// make some private variables to store some information.
		private string stringtext1;
		private string stringtext2;
		private string stringtext3;
		private bool undohappening;

		private void textBox1_TextChanged(object sender, System.EventArgs e)
		{
			if(undohappening == false)
			{
				Undo.AddChange(1,stringtext1);
				button1.Enabled = true;
				stringtext1 = textBox1.Text;
			}
		}

		private void textBox2_TextChanged(object sender, System.EventArgs e)
		{
			if(undohappening == false)
			{
				Undo.AddChange(2,stringtext2);
				button1.Enabled = true;
				stringtext2 = textBox2.Text;
			}
		}

		private void textBox3_TextChanged(object sender, System.EventArgs e)
		{
			if(undohappening == false)
			{
				Undo.AddChange(3,stringtext3);
				button1.Enabled = true;
				stringtext3 = textBox3.Text;
			}
		}

		private void button1_Click(object sender, System.EventArgs e)
		{
			int boxlastchanged;
			string textbeforechange;
			
			undohappening = true;
			// Get the last box changed and the text it was changed from.
			boxlastchanged = Undo.BoxLastChanged();
			textbeforechange = Undo.TextLastChanged();

			// If there is nothing to undo, then the method will return -1.
			if(boxlastchanged != -1)
			{
				switch (boxlastchanged)
				{
					case 1:
						textBox1.Text = textbeforechange;
						Application.DoEvents();
						break;
					case 2:
						textBox2.Text = textbeforechange;
						Application.DoEvents();
						break;
					case 3:
						textBox3.Text = textbeforechange;
						Application.DoEvents();
						break;
				}
			}
			else
			{
				// The boxes started empty, so empty them:
				textBox1.Text = "";
				textBox2.Text = "";
				textBox3.Text = "";
				button1.Enabled = false;
			}
			undohappening = false;
		}

		private void Form1_Load(object sender, System.EventArgs e)
		{
			Undo.Initialize();
			stringtext1 = "";
			stringtext2 = "";
			stringtext3 = "";
			undohappening = false;
			button1.Enabled = false;
		}
	}
}
Here is the code for the static class:
Code:
using System;
using System.Collections;

namespace Undo_Sample
{
	/// <summary>
	/// Summary description for Undo.
	/// </summary>
	public class Undo
	{
		private Undo()
		{
			
		}

		private static ArrayList thebox;
		private static ArrayList thestrings;

		public static void Initialize()
		{
			thebox = new ArrayList();
			thestrings = new ArrayList();
		}
		
		public static void AddChange(int BoxChanged, string text)
		{
			thebox.Add(BoxChanged);
			thestrings.Add(text);
		}

		public static int BoxLastChanged()
		{
			int x;
			int returnvalue;

			x = thebox.Count - 1;
			if(x == -1)
			{
				return x;
			}
			else
			{
				
				returnvalue = (int)thebox[x];
				thebox.RemoveAt(x);
				return returnvalue;
			}
		}

		public static string TextLastChanged()
		{
			int x;
			string returnvalue;

			x = thestrings.Count - 1;
			if(x == -1)
			{
				return "blank";
			}
			else
			{
				returnvalue = (string)thestrings[x];
				thestrings.RemoveAt(x);
				return returnvalue;
			}
		}
	}
}