これは私の最初のC#アプリケーションです。私は元に戻す/やり直しに関するいくつかの調査をしましたが、役に立たなかった(または分かりやすい)ものは何も見つかりませんでした。したがって、私は誰かが私のプログラム(winformsアプリケーション)のアンドゥ/リドゥ機能を設計する際に助けてくれることを願っています。アプリケーションは、特定のイベント(ボタンクリックなど)中にユーザー指定の値を記録するために後続の子フォームが呼び出されるメインフォームで構成されます。すべてのイベントが処理されると、ビットマップがバッファに描画され、メインフォームのOnPaintイベント中にメインフォーム内のピクチャボックスにロードされます。各入力はカスタムクラスオブジェクトに分割され、別々のListとBindingListに追加されます。 Listに含まれるオブジェクトはグラフィックス(座標などを示すため)に使用され、BindingListのオブジェクトはDataGridViewにいくつかの重要な値を表示するために使用されます。必要なすべてのデータをリストに格納されているので簡単な元に戻す/やり直しに関するガイドが必要
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 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 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
これはあまりにも多くのコードであることに加えて、大量のコードが埋め込まれた1つの大きなテキストのように読み込まれます。ここで段落区切りを使用できます大きなブロックではありません。読みやすく理解しやすくなります。 (あなたはあなたの記事をどこに書いているのかをWYSIWYGのファッションに書いているところでプレビューすることができるので、質問を出す前にそれがどのように表示されるか分かります)。読む。あなたの質問を編集し、段落を適切に書き、少なくとも*可能な内容にまで下げてください。)ありがとう。 –
価値があるのは、通常、[コマンドパターン](http://en.wikipedia.org/wiki/Command_pattern)を使用して元に戻す/やり直しを実装することです。スタック全体に状態全体を格納するのではなく、この状態に至ったアクションを格納します。 –