本次实验在第三次的基础上加上了undo/redo功能,主要用了undo栈和redo栈保存状态的变化,而对于全局控件的变化采用一个state类的对象来保存各个控件的属性。每次操作进undo栈,点击undo的时候弹出栈顶元素并进入redo栈,取此时的栈顶元素(因为最初栈顶的元素是当前的状态,而undo要撤销到上一状态)将当前状态置为该状态。Redo的时候从redo栈顶取出undo是压入栈的元素,将当前状态置为该状态。实现思路是这样,但是实现过程中也遇到了许多问题,如下:
1、 监听了状态变化,并把变化的操作入栈,但是undo/redo的时候也会影响状态的变化,而此时状态的变化是不应该进栈的。所以设置了标记变量,判断当前状态的变化是否是undo/redo导致的,只有是用户主动改变状态的时候才应该进栈。
2、 设计数据结构的时候没有考虑到listBox的状态,当前选中的可能是多选,无法用String类型来保存属性。但只有这个是特殊的,没有必要修改数据结构,所以采用了字符串拼接的方式,对每次选中的index用分隔符分割并保存为字符串
Stringtmp = "";
for(int i = 0; i <listBox1.SelectedIndices.Count; i++)
{
tmp +=(listBox1.SelectedIndices[i] +"#");
}
恢复的时候再把该字符串解开,依次listBox的select赋值。
string[] s= last.Value.Split(new char[]{ '#' });
listbox_flag =true;//必须在clear, setIndex的函数之前
((ListBox)control).ClearSelected();
for(int i=0; i<s.Length-1; i++)
{
//MessageBox.Show(s[i]);
((ListBox)control).SelectedIndex =Int32.Parse(s[i]);
}
listbox_flag =true;
3、关于listBox确实很麻烦,进行了大量的修改。之前把状态变化写在selectChange的函数里,undo/redo中每次给select赋值都会调用这个函数,而只有第一次能判断是点击了undo/redo,之后无法判断。后来改写在了click函数中,避免此类问题。
关键的数据结构:
保存全局的状态,后来增加的,为了全局的undo/redo,使用属性操作成员变量。
class State
{
String_username;
String_pwd;
String_pwd2;
bool_check1;
bool_check2;
bool_check3;
String_listbox;
publicString username
{
get{ return _username; }
set{ _username = value; }
}
publicString pwd
{
get{ return _pwd; }
set{ _pwd = value; }
}
publicString pwd2
{
get{ return _pwd2; }
set{ _pwd2 = value; }
}
publicbool check1
{
get{ return _check1; }
set{ _check1 = value; }
}
publicbool check2
{
get{ return _check2; }
set{ _check2 = value; }
}
publicbool check3
{
get{ return _check3; }
set{ _check3 = value; }
}
publicString listbox
{
get{ return _listbox; }
set{ _listbox = value; }
}
}
每次进栈的操作,使用属性对成员变量操作。由于对一个控件来说操作是确定的,所以使用一个String类型的value来保存属性,String类型的name来保存控件的名称,可以通过名称找到该控件。Type属性是int类型,代码中有注释,为了区分不同类型的控件,将通过名称找到的控件强制转化为真实的类型。
class item
{
privateString _Name;
publicString Name
{
get{ return _Name; }