新聞中心
簡介

創(chuàng)新互聯(lián)是一家專注于成都網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)與策劃設(shè)計(jì),崖州網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)10余年,網(wǎng)設(shè)計(jì)領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:崖州等地區(qū)。崖州做網(wǎng)站價(jià)格咨詢:028-86922220
我們?nèi)绾卧诓煌膱鼍跋率褂眠@些方法來實(shí)現(xiàn)Undo/Redo。這些方法是使用單個(gè)對(duì)象表示變化,命令模式和備忘錄模式。
正如我們所知,Undo/Redo沒有通用的解決方案,而Undo/Redo在每個(gè)應(yīng)用程序中非常具體。處于這個(gè)原因,在該系列文章的開始部分,將討論如何使用該方法建模任意的應(yīng)用程序,然后展示一個(gè)簡單應(yīng)用程序的實(shí)現(xiàn)。
關(guān)于Undo/Redo實(shí)現(xiàn)的基本思想
正如我們所知,應(yīng)用程序在每次操作后改變其狀態(tài)。當(dāng)操作應(yīng)用程序時(shí),它的狀態(tài)會(huì)發(fā)生改變。所以,若有人想要做撤銷,他不得不回到先前的狀態(tài)。因此,為了能夠回到先前狀態(tài),我們需要在應(yīng)用程序運(yùn)行時(shí)存儲(chǔ)它的狀態(tài)。要支持重做,我們不得不從目前狀態(tài)跳到下一個(gè)狀態(tài)。
為了實(shí)現(xiàn)Undo/Redo,我們不得不存儲(chǔ)應(yīng)用程序的狀態(tài)并在撤銷時(shí)跳到前一個(gè)狀態(tài)而在重做時(shí)跳到下一個(gè)狀態(tài)。因此我們需要維護(hù)應(yīng)用程序的狀態(tài)來支持Undo/Redo。在所有三種方法中,應(yīng)用程序狀態(tài)的維護(hù)用到了兩個(gè)棧。一個(gè)棧包含用于撤銷操作的狀態(tài),第二個(gè)包含用于重做的狀態(tài)。撤銷操作彈出撤銷棧以獲取前一個(gè)狀態(tài)并將其設(shè)置給應(yīng)用程序。同樣的,重做操作彈出重做棧以獲取下一個(gè)狀態(tài)并將其設(shè)置給應(yīng)用程序。
現(xiàn)在,我們知道了Undo/Redo的實(shí)現(xiàn)操作都是關(guān)于保持應(yīng)用程序每次操作后的狀態(tài)?,F(xiàn)在的問題是該方法如何保存狀態(tài)。本方法中,單個(gè)操作的改變被保存在一個(gè)對(duì)象中,有些屬性為該操作作為狀態(tài)是多余的,因?yàn)檫@里,單個(gè)對(duì)象被用于包含所有類型的動(dòng)作數(shù)據(jù)。
什么是單個(gè)對(duì)象表示改變的方法?
首先,我對(duì)這是由我命名表示抱歉。這里,單個(gè)對(duì)象表示了應(yīng)用程序中所有操作的所有改變。因此,當(dāng)你準(zhǔn)備了一個(gè)關(guān)于操作更改的類型的對(duì)象,在執(zhí)行一次操作后,你僅使用了該對(duì)象屬性的子集,而剩余屬性仍舊沒有被使用。例如,你在一個(gè)應(yīng)用程序中有兩個(gè)操作;它們是高度的改變和寬度的改變。因此,對(duì)象類型包含兩個(gè)屬性:高度和寬度。當(dāng)你準(zhǔn)備了變化對(duì)象,在執(zhí)行高度更改方法后,你僅需設(shè)置變化對(duì)象的高度字段,而其他字段仍舊沒有被使用。
如何應(yīng)用單個(gè)對(duì)象表示變化的方法對(duì)任意應(yīng)用程序Undo/Redo操作建模?
單個(gè)對(duì)象表示變化的方法如何對(duì)任意應(yīng)用程序Undo/Redo操作建模將在以下步驟中討論:
步驟1
首先識(shí)別出你希望哪些操作能支持Undo/Redo。然后,識(shí)別出你將在哪個(gè)容器下支持Undo/Redo以及你希望哪些對(duì)象支持Undo/Redo。
步驟2
為了進(jìn)一步處理每個(gè)Undo/Redo操作,識(shí)別出需要被保存的屬性。
步驟3
然后創(chuàng)建一個(gè)類(ChangeRepresentationObject),它包含支持全部操作Undo/Redo的所有屬性。同樣,準(zhǔn)備一個(gè)動(dòng)作類型enum,它將代表全部操作。這個(gè)動(dòng)作類型enum是ChangeRepresentationObject類的一部分。
步驟4
然后創(chuàng)建一個(gè)名為UndoRedo的類,它包含兩個(gè)類型的ChangeRepresentationObject棧。一個(gè)用于撤銷操作,一個(gè)用于重做操作。該類將實(shí)現(xiàn)以下接口:
步驟5
然后實(shí)現(xiàn)具體方法:Undo、 Redo、InsertObjectforUndoRedo。
在每個(gè)Undo操作中:
◆首先檢查Undo棧是否為空。
◆如果不是,則彈出一個(gè)ChangeRepresentationObject并將其壓入重做棧。
◆檢查動(dòng)作類型。
◆然后基于動(dòng)作類型,利用ChangeRepresentationObject的屬性完成撤銷操作。
在每個(gè)Redo操作中,你幾乎做與Undo同樣的事。
◆首先檢查Redo棧是否為空。
◆如果不是,彈出一個(gè)ChangeRepresentationObject,然后將其壓入撤銷棧。
◆檢查動(dòng)作類型。
◆然后基于動(dòng)作的類型,利用ChangeRepresentationObject屬性完成重做操作。
在InsertObjectforUndoRedo操作中,你只要把數(shù)據(jù)對(duì)象插入U(xiǎn)ndo棧并清空Redo棧中。
步驟6
然后,在完成每次操作前,調(diào)用InsertObjectforUndoRedo方法以對(duì)所有操作提供Undo/Redo支持。在用戶界面上點(diǎn)擊Undo時(shí),只需調(diào)用UndoRedo類的Undo方法,而在用戶界面上點(diǎn)擊Redo時(shí),只需調(diào)用UndoRedo類的redo方法。
#p#
示例應(yīng)用程序說明
這個(gè)示范WPF繪制應(yīng)用程序用來作為結(jié)合Undo/Redo操作的案例。該WPF應(yīng)用程序示例支持四種操作:插入對(duì)象、刪除對(duì)象、移動(dòng)對(duì)象和調(diào)整對(duì)象的尺寸,它還有兩種類型的幾何對(duì)象:矩形和多邊形。它使用畫布作為包含這些幾何對(duì)象的容器。
現(xiàn)在,在此系列文章中,我們可以看到如何讓這四個(gè)操作支持Undo/Redo。在第一部分,使用單個(gè)對(duì)象表示變化的方法實(shí)現(xiàn)。在第二部分,使用命令模式實(shí)現(xiàn)而在第三部分,使用備忘錄模式實(shí)現(xiàn)。
使用單個(gè)對(duì)象表示變化的方法實(shí)現(xiàn)示范應(yīng)用程序的Undo/Redo
利用單個(gè)對(duì)象表示變化的方法對(duì)示范應(yīng)用程序Undo/Redo的實(shí)現(xiàn)將在以下步驟中討論
步驟1
我們將識(shí)別出那些需要支持Undo/Redo的操作。這里有四個(gè)操作支持Undo/Redo。它們是::插入對(duì)象、刪除對(duì)象、移動(dòng)對(duì)象和調(diào)整對(duì)象的尺寸。我們將對(duì)矩形和橢圓支持Undo/Redo,這里的容器是畫布。
步驟2
現(xiàn)在我們將識(shí)別出那些進(jìn)一步處理Undo/Redo所需的保存的參數(shù)。幾何對(duì)象移動(dòng)時(shí)其邊距改變,因此要支持對(duì)象移動(dòng)的Undo/Redo,要保存邊距。當(dāng)對(duì)象改變尺寸時(shí),它的高度、寬度和邊距改變。因此為支持對(duì)象尺寸調(diào)整的Undo/Redo,我們需要保存高度、寬度和邊距。為了支持插入和刪除的Undo/Redo操作,我們需要保存幾何對(duì)象的引用。
步驟3
現(xiàn)在我們得到包含邊距、高度、寬度、動(dòng)作類型、幾何對(duì)象引用的ChangeRepresentationObject以支持所有操作的Undo/Redo。這里的幾何對(duì)象引用被保存以便我們在對(duì)其進(jìn)行Undo/Redo時(shí)獲取。同樣使動(dòng)作類型enum代表插入、刪除、移動(dòng)和調(diào)整尺寸操作。此動(dòng)作類型enum被用作ChangeRepresentationObject的一部分。
這里,已附上使用單個(gè)對(duì)象表示變化的方法實(shí)現(xiàn)Undo/Redo的項(xiàng)目。
步驟4&5
然后我們將包含兩個(gè)ChangeRepresentationObject類型的棧的類命名為UndoRedo。一個(gè)棧用于撤銷操作而另一個(gè)用于重做操作。類的代碼如下:
CollapseCopy Code
public partial class UnDoRedo : IUndoRedo
{
private Stack _UndoActionsCollection =
new Stack ();
private Stack _RedoActionsCollection =
new Stack ();#region IUndoRedo Members
public void Undo(int level)
{
for (int i = 1; i <= level; i++)
{
if (_UndoActionsCollection.Count == 0) return;ChangeRepresentationObject Undostruct = _UndoActionsCollection.Pop();
if (Undostruct.Action == ActionType.Delete)
{
Container.Children.Add(Undostruct.UiElement);
this.RedoPushInUnDoForDelete(Undostruct.UiElement);
}
else if (Undostruct.Action == ActionType.Insert)
{
Container.Children.Remove(Undostruct.UiElement);
this.RedoPushInUnDoForInsert(Undostruct.UiElement);
}
else if (Undostruct.Action == ActionType.Resize)
{
if (_UndoActionsCollection.Count != 0)
{
Point previousMarginOfSelectedObject = new Point
(((FrameworkElement)Undostruct.UiElement).Margin.Left,
((FrameworkElement)Undostruct.UiElement).Margin.Top);
this.RedoPushInUnDoForResize(previousMarginOfSelectedObject,
Undostruct.UiElement.Width,
Undostruct.UiElement.Height, Undostruct.UiElement);
Undostruct.UiElement.Margin = new Thickness
(Undostruct.Margin.X, Undostruct.Margin.Y, 0, 0);
Undostruct.UiElement.Height = Undostruct.height;
Undostruct.UiElement.Width = Undostruct.Width;
}
}
else if (Undostruct.Action == ActionType.Move)
{
Point previousMarginOfSelectedObject = new Point
(((FrameworkElement)Undostruct.UiElement).Margin.Left,
((FrameworkElement)Undostruct.UiElement).Margin.Top);
this.RedoPushInUnDoForMove(previousMarginOfSelectedObject,
Undostruct.UiElement);
Undostruct.UiElement.Margin = new Thickness
(Undostruct.Margin.X, Undostruct.Margin.Y, 0, 0);
}
}
}public void Redo(int level)
{
for (int i = 1; i <= level; i++)
{
if (_RedoActionsCollection.Count == 0) return;ChangeRepresentationObject Undostruct = _RedoActionsCollection.Pop();
if (Undostruct.Action == ActionType.Delete)
{
Container.Children.Remove(Undostruct.UiElement);
this.PushInUnDoForDelete(Undostruct.UiElement);
}
else if (Undostruct.Action == ActionType.Insert)
{
Container.Children.Add(Undostruct.UiElement);
this.PushInUnDoForInsert(Undostruct.UiElement);
}
else if (Undostruct.Action == ActionType.Resize)
{
Point previousMarginOfSelectedObject = new Point
(((FrameworkElement)Undostruct.UiElement).Margin.Left,
((FrameworkElement)Undostruct.UiElement).Margin.Top);
this.PushInUnDoForResize(previousMarginOfSelectedObject,
Undostruct.UiElement.Width,
Undostruct.UiElement.Height, Undostruct.UiElement);
Undostruct.UiElement.Margin = new Thickness
(Undostruct.Margin.X, Undostruct.Margin.Y, 0, 0);
Undostruct.UiElement.Height = Undostruct.height;
Undostruct.UiElement.Width = Undostruct.Width;
}
else if (Undostruct.Action == ActionType.Move)
{
Point previousMarginOfSelectedObject = new Point
(((FrameworkElement)Undostruct.UiElement).Margin.Left,
((FrameworkElement)Undostruct.UiElement).Margin.Top);
this.PushInUnDoForMove(previousMarginOfSelectedObject,
Undostruct.UiElement);
Undostruct.UiElement.Margin = new Thickness
(Undostruct.Margin.X, Undostruct.Margin.Y, 0, 0);
}
}
}
public void InsertObjectforUndoRedo(ChangeRepresentationObject dataobject)
{
_UndoActionsCollection.Push(dataobject);_RedoActionsCollection.Clear();
}
#endregion
步驟6
在完成每個(gè)操作前,調(diào)用InsertObjectforUndoRedo方法。當(dāng)用戶界面上Undo被點(diǎn)擊,我們調(diào)用UndoRedo類的Undo方法,而當(dāng)用戶界面上Redo被點(diǎn)擊,我們調(diào)用UndoRedo類的redo方法。
這里,我們沒有明確設(shè)置Undo棧和Redo棧的大小,因此,應(yīng)用程序能具有的狀態(tài)數(shù)目取決于系統(tǒng)的內(nèi)存。
使用單個(gè)對(duì)象表示變化的方法時(shí)的變更管理
如果你想要用單個(gè)對(duì)象表示變化的方法來為新的操作支持Undo/Redo時(shí),你不得不作一些改變。你不得不修改表示變化的對(duì)象,動(dòng)作類型Enum并改變Undo/Redo方法的代碼。所以,它的可維護(hù)性很低。
使用單個(gè)對(duì)象表示變化的方法的優(yōu)缺點(diǎn)
它的優(yōu)點(diǎn)是實(shí)現(xiàn)簡單,而不需要知道任何的設(shè)計(jì)模式,你就可以實(shí)現(xiàn)Undo/Redo。
可維護(hù)性很低。代表該方法的對(duì)象包含很多額外信息,因?yàn)檫@里,單個(gè)對(duì)象用來容納所有動(dòng)作類型的數(shù)據(jù)。例如,對(duì)移動(dòng)而言,我們只需保存移動(dòng)相關(guān)的數(shù)據(jù),而對(duì)調(diào)整尺寸,我們應(yīng)該僅保存該操作相關(guān)的數(shù)據(jù)。所以,我們在保存冗余的數(shù)據(jù)。隨著操作數(shù)目的增加,冗余也在增加。這并不是好的面向?qū)ο蟮脑O(shè)計(jì)。
【編輯推薦】
- C#實(shí)用基礎(chǔ)教程
- 如何使用C#代碼實(shí)現(xiàn)DataTemplate
- 詳解C# 4.0中必選參數(shù)與可選參數(shù)混合的問題
文章題目:C#中使用單個(gè)對(duì)象的方法實(shí)現(xiàn)Undo/Redo
轉(zhuǎn)載來于:http://m.fisionsoft.com.cn/article/djcohhh.html


咨詢
建站咨詢
