這是我的第一個C#應用程序,完全自學,沒有任何先前的軟件編程背景。我做了撤銷/重做的一些研究,但找不到任何有用的(或易於理解)。因此,我希望有人可以幫助我設計我的程序(winforms應用程序)的撤銷/重做功能。該應用程序包含一個主窗體,其中後續的子窗體將被調用以在特定事件(按鈕點擊等)期間記錄用戶指定的值。處理完每個事件之後,位圖將在緩衝區中繪製,然後在主窗體的OnPaint事件期間加載到主窗體中的圖片框。每個輸入分成自定義類對象並添加到單獨的List和BindingList中。 List中包含的對象用於圖形(用於指示座標等),而BindingList中的對象用於在DataGridView上顯示一些重要的值。只是做一個簡短的介紹,代碼如下所示:需要一些簡單的撤銷/重做指南
public partial class MainForm : form
{
public class DataClass_1
{
public double a { get; set; }
public double b { get; set; }
public SubDataClass_1 { get; set; }
}
public class SubDataClass_1
{
public double x { get; set; }
public double y { get; set; }
public string SomeString { get; set; }
public CustomEnum Enum_SubDataClass_1 { get; set; }
}
public class DisplayDataClass
{
public string SomeString { get; set; }
public double e { get; set; }
public double f { get; set; }
}
public enum CustomEnum { Enum1, Enum2, Enum3 };
// Lists that contain objects which hold the necessary values to be drawn and displayed
public List<DataClass_1> List_1 = new List<DataClass_1>();
public List<DataClass_2> List_2 = new List<DataClass_2>(); // Object has similar data types as DataClass_1
public BindingList<DisplayDataClass> DisplayList = new BindingList<DisplayDataClass>();
Bitmap buffer;
public MainForm()
{
InitializeComponent();
dgv.DataSource = DisplayList;
}
private void DrawObject_1()
{
// some drawing codes here
}
private void DrawObject_2()
{
// some drawing codes here
}
protected override void OnPaint(PaintEventArgs e)
{
DrawObject_1();
DrawObject_2();
pictureBox1.Image = buffer;
}
// Event to get input
private void action_button_click(object sender, EventArgs e)
{
ChildForm form = new ChildForm(this);
form.ShowDialog();
Invalidate();
}
}
子窗體代碼是這個樣子:
public partial class ChildForm : form
{
public ChildForm(MainForm MainForm)
{
InitializeComponent();
// Do something
}
private void ok_button_click(object sender, EventArgs e)
{
DataClass_1 Data_1 = new DataClass_1();
DisplayDataClass DisplayData = new DisplayDataClass();
// Parsing, calculations, set values to Data_1 and DisplayData
MainForm.List_1.Add(Data_1);
MainForm.DisplayList.Add(DisplayData);
this.DialogResult = System.Windows.Forms.DialogResult.OK;
this.Close();
}
}
由於所有必要的數據都存儲在列表中,只將在某些事件被觸發後(主要是點擊按鈕)被更改,因此我試圖在運行時使用這些列表來確定應用程序的狀態。我在執行撤銷/重做功能的方法是通過添加以下代碼:
public partial class MainForm : form
{
public class State()
{
public List<DataClass_1> List_1 { get; set; }
public List<DataClass_2> List_2 { get; set; }
public BindingList<DisplayDataClass> DisplayList { get; set; }
// and so on
public State()
{
List_1 = new List<DataClass_1>();
List_2 = new List<DataClass_2>();
DisplayList = new BindingList<DisplayDataClass>();
}
}
State currentState = new State();
Stack<State> undoStack = new Stack<State>();
Stack<State> redoStack = new Stack<State>();
private void MainForm_Shown(object sender, EventArgs e)
{
// Saves original state as first item in undoStack
undoStack.Push(currentState);
}
protected override void OnPaint(PaintEventArgs e)
{
// Update lists from currentState before drawing
List_1 = new List<DataClass_1>(currentState.List_1);
List_2 = new List<DataClass_2>(currentState.List_2);
DisplayList = new BindingList<DisplayDataClass>(currentState.DisplayList);
}
// When undo button is clicked
private void undo_button_Click(object sender, EventArgs e)
{
if (undoStack.Count > 0)
{
redoStack.Push(currentState);
undoStack.Pop();
currentState = undoStack.Last();
Invalidate();
}
}
// When redo button is clicked
private void redo_button_Click(object sender, EventArgs e)
{
// Have not thought about this yet, trying to get undo done first
}
// Events that trigger changes to values held by data objects
private void action_button_Click(object sender, EventArgs e)
{
// Replace the following code with previously stated version
if (form.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
ChildForm form = new ChildForm(this)
UpdateState();
undoStack.Push(currentState);
Invalidate();
}
}
// To update currentState to current values
private void UpdateState()
{
currentState.List_1 = new List<DataClass_1>(List_1);
currentState.List_2 = new List<DataClass_2>(List_2);
currentState.DisplayList = new BindingList<DisplayDataClass>(DisplayList);
// and so on
}
}
結果: 應用程序不正確地執行撤消功能。該程序在正常條件下顯示正確的輸出,但是當撤消事件被觸發時,無論已繪製了多少個對象,應用程序都會恢復到初始狀態(沒有記錄數據的狀態)。我在使用System.Diagnostics.Debug.WriteLine()時,在堆棧被更改的事件中檢查了undoStack中的計數數量,並且它似乎給出了正確的計數。我猜這些列表需要以不同的方式複製/克隆?或者我在這裏做錯了什麼?任何人都可以請指導我?性能,可讀性,資源管理,未來維護等都不需要考慮。
這是太多的代碼堆棧溢出後 – debracey 2012-04-26 23:29:48
除了這是太多的代碼,它讀取像一個大塊的文本與一堆代碼塞入英寸你可以在這裏使用分段符,所以你的文本不是一個大塊 - 它使閱讀和理解變得更容易。 (你可以在下面的地方立即預覽你的帖子,在那裏你用幾乎所見即所得的方式來書寫它,所以你可以在提交問題之前看到它是如何出現的。)我試圖瀏覽大量的文本,但它們太難了讀書。請編輯您的問題,正確編寫段落,並將其縮減爲*最少*可能的內容。)謝謝。 – 2012-04-26 23:33:55
對於它的價值,通常使用[Command pattern](http://en.wikipedia.org/wiki/Command_pattern)來實現撤銷/重做。不是將整個狀態存儲在撤消堆棧中,而是存儲導致此狀態的操作。 – 2012-04-26 23:57:17