2011-10-13 24 views
3

我有我的WinForms應用程序的一些不錯的,工作的編輯撤消功能。它使用CommandStack類,這是兩個Stack<IStateCommand> s(一個用於撤消,一個用於重做)。每個命令都有一個Execute和一個Undo方法,並且對象本身有一個事件,當堆棧被更改時會觸發。C#檢測,如果電話都是在相同的UI操作

如果LogCommand方法是從其自己的Undo函數中調用的,並因此將其添加到重做堆棧而不是撤消堆棧,則CommandStack也會生效。這是通過簡單地將當前ManagingThreadId添加到List<int>對象中完成的,然後在撤消命令完成後將其刪除(與使用堆棧跟蹤相反,我認爲這會比較慢並且有點髒)。

在我的應用程序中有很多不同的命令,所以這個公式有一定的難度,因爲它需要我幾天的時間來重做所有這些實現。

這個唯一的問題,目前,內也有一些UI事件調用其他UI事件,這兩者的身份登錄到IStateCommand撤銷歷史。有沒有在C#中的任何方式,我可以檢測是否LogCommand功能已經從相同的UI事件(點擊,的DragDrop,的SelectedIndexChanged,框TextChanged等),然後我可以將命令組合成一個命令調用(使用我CommandList類,這也繼承了IStateCommand)?

我想保存時撤消事件被稱爲當前時間,那麼如果下一個命令比x毫秒以後登錄以下,將它們組合起來的歷史,但是這似乎有點草率。我也考慮過搜索堆棧跟蹤,但我不知道要查找根UI事件要尋找什麼,也不知道我是否會告訴一個按鈕單擊之間的差異,然後點擊不同的點擊按鈕。

這也可能是讓你知道,所有這些命令正在從事件處理(從自定義用戶控件主要來自事件)UI線程調用。使用另一個線程的應用程序的唯一部分在記錄撤消歷史記錄後,在大多數UI事件之後運行。

謝謝!

排序版

相同的方法正在從相同的UI事件調用兩次(例如,的MouseUp,的DragDrop)。第二次調用此方法時,如何檢查它是否已被相同的UI事件調用一次?

編輯:該解決方案(在某種程度上)

這是一個有點髒的一個,因爲我不必完全重新編寫這個系統的時間。不過,我已經以這樣的方式實現了它,使得選項在未來不會太髒。

該解決方案是基於艾爾諾對他的答覆意見(所以我將迎來自己的答案被接受),在那裏他建議添加的參數之一。我在CommandStackLogCommand(IStackCommand, string)中爲我的LogCommand(IStackCommand)方法增加了另一個過載。字符串是爲每個命令存儲的actionId,如果該字符串與最後一個字符串相同,則組合該命令。這給出了通過每個事件並給出唯一ID的選項。

然而,髒部分 - 得到它的工作之前,我們必須表現出客戶端,actionId默認爲System.Windows.Forms.Cursor.Position.ToString(),哎喲!由於UI線程正在執行時遊標位置不會更改,因此它會合並每個命令。它實際上甚至結合了TextChanged命令(只要它們不移動它們的鼠標!)

回答

4

這可能是一個將被調用命令的本地堆棧添加到命令的選項。

當一個命令執行其他命令時,將命令添加到本地堆棧,以便在命令必須撤消或重做時撤消此本地堆棧上的命令。

編輯

我不太清楚,你不明白。

我只是將一個CommandList屬性添加到StateCommand。每次StateCommand調用/觸發另一個StateCommand時,都應該將新的StateCommand添加到CommandList中。因此,全球CommandList跟蹤的命令,可以從UI可以撤銷,每個StateCommand跟蹤它調用的StateCommands(所以這些不是添加到全局撤消CommandList)

EDIT 2

的如果你不能或不想改變這個設置,你必須傳遞一個參數來執行將它們鏈接在一起的命令。

+0

似乎最好的解決方案,爲每個基本方法調用創建一個堆棧,並有任何結果方法調用寫入。然後,您可以將這些堆棧添加到主撤銷/重做堆棧。 –

+0

我不確定我是否理解,或者我的問題還不夠清楚:/ IStateCommand不調用其他IStateCommand,如果這就是你的意思。一個UI事件是a)記錄一個IStateCommand,和b)調用另一個動作,最終記錄另一個IStateCommand。 – Connell

+0

我已經有了一個CommandList,它也繼承了IStateCommand,並且可以同時執行/撤銷多個命令。你會如何建議我找到基礎方法調用? – Connell

0

你嘗試檢查方法堆棧和分析方法,通過法:

StackTrace st = new StackTrace(); 

    for (int i=0; i<st.FrameCount; i++) 
{ 
    StackFrame sf = st.GetFrame(i); 
    MethodBase mb = sf.GetMethod(); 

    // do whatever you want 
} 
+0

StackTrace只適用於通常不隨發行版部署的pdb文件版。 – baalazamon

+0

是的,但說我點擊一個記錄兩個命令的按鈕。然後我撤消,這將撤消該按鈕已記錄的兩個命令。如果有一個單獨的按鈕,它只記錄一個命令,並且我連續打了兩次,我只想撤消一次點擊。 – Connell

+0

@baalazamon,我也沒有意識到這一點,我認爲PDB文件的優勢在於堆棧跟蹤中的每個方法都可以包含代碼文件和行號。 – Connell

0

我不知道你需要什麼確切地實現,但我實現類似的東西,也許你可以得到一些想法...

總之,你可以存儲一些信息在ThreadStatic變量。然後,任何時候你想記錄一個命令,檢查線程靜態變量來找出你記錄命令的上下文。如果它是空的,則開始一個新的命令記錄序列。如果不是,你就在一個序列中。

也許您可以存儲輸入事件(例如Click,DragDrop,...)或命令本身......這取決於您的需要。 當初始事件回調完成時,清除靜態變量以表示序列已完成。

我成功實現了一個類似的策略,在對象模型上執行跟蹤命令。我將邏輯封裝在IDisposable類中,該類還實現了引用計數以處理嵌套使用。第一個使用開始序列,後續使用語句增加並減少了參考計數以知道序列何時完成。最外層的上下文處理會觸發一個包含所有嵌套命令的事件。在我的具體情況下,它已完美工作,我不知道它是否可以滿足您的需求...

+0

如何「存儲入口事件」,如果我知道如何做到這一點,我可以存儲lastEntryEvent的靜態變量,然後在記錄命令時檢查入口事件是否與此變量相同,如果所以,在撤消歷史堆棧中結合這些'IStateCommand's。 – Connell

+1

也許你可以做一個方面。然後你可以用一個屬性修飾事件處理方法,並在該方面執行存儲和引用計數器。這將保持代碼清潔。我使用PostSharp來做一些這樣的事情。 – Haplo

相關問題