2010-04-21 64 views
2

我有一個我的窗體上的TextBox.TextChanged事件的事件處理程序。爲了支持撤銷,我想弄清楚在TextBox中發生了什麼變化,以便在用戶請求時可以撤銷更改。 (我知道內置文本框支持撤消,但我希望有一個單一的撤消堆棧爲整個應用程序)C#和Winforms TextBox控件:如何獲取文本更改?

有沒有合理的方式來做到這一點?如果沒有,是否有更好的方法來支持這種撤銷功能?

編輯:像下面這樣的東西似乎工作 - 有沒有更好的想法? (它的幾次都是這樣,我真的希望.NET有這樣的事情了STL的std::mismatch算法...

class TextModification 
    { 
     private string _OldValue; 
     public string OldValue 
     { 
      get 
      { 
       return _OldValue; 
      } 
     } 
     private string _NewValue; 
     public string NewValue 
     { 
      get 
      { 
       return _NewValue; 
      } 
     } 
     private int _Position; 
     public int Position 
     { 
      get 
      { 
       return _Position; 
      } 
     } 
     public TextModification(string oldValue, string newValue, int position) 
     { 
      _OldValue = oldValue; 
      _NewValue = newValue; 
      _Position = position; 
     } 
     public void RevertTextbox(System.Windows.Forms.TextBox tb) 
     { 
      tb.Text = tb.Text.Substring(0, Position) + OldValue + tb.Text.Substring(Position + NewValue.Length); 
     } 
    } 

    private Stack<TextModification> changes = new Stack<TextModification>(); 
    private string OldTBText = ""; 
    private bool undoing = false; 

    private void Undoit() 
    { 
     if (changes.Count == 0) 
      return; 
     undoing = true; 
     changes.Pop().RevertTextbox(tbFilter); 
     OldTBText = tbFilter.Text; 
     undoing = false; 
    } 

    private void UpdateUndoStatus(TextBox caller) 
    { 
     int changeStartLocation = 0; 
     int changeEndTBLocation = caller.Text.Length; 
     int changeEndOldLocation = OldTBText.Length; 
     while (changeStartLocation < Math.Min(changeEndOldLocation, changeEndTBLocation) && 
      caller.Text[changeStartLocation] == OldTBText[changeStartLocation]) 
      changeStartLocation++; 
     while (changeEndTBLocation > 1 && changeEndOldLocation > 1 && 
      caller.Text[changeEndTBLocation-1] == OldTBText[changeEndOldLocation-1]) 
     { 
      changeEndTBLocation--; 
      changeEndOldLocation--; 
     } 
     changes.Push(new TextModification(
      OldTBText.Substring(changeStartLocation, changeEndOldLocation - changeStartLocation), 
      caller.Text.Substring(changeStartLocation, changeEndTBLocation - changeStartLocation), 
      changeStartLocation)); 
     OldTBText = caller.Text; 
    } 

    private void tbFilter_TextChanged(object sender, EventArgs e) 
    { 
     if (!undoing) 
      UpdateUndoStatus((TextBox)sender); 
    } 

回答

8

使用Enter和Leave事件可能會更好。輸入時,將當前文本存儲在類變量中,然後在離開時比較新文本和舊文本。

+0

嗯..這可能工作。 – 2010-04-21 16:54:30

+0

您將在進入和離開時遇到問題。有一種反模式指的是事件的過度使用。 – 2010-04-21 23:44:17

+1

@Daniel Dolz:「過度使用事件」?兩件事很難「過度」 – 2010-04-22 03:07:14

0

是,不要直接比分扳成文本框。您的形式狀態應該是在一些模型對象某處不直接與表單綁定(MVC是實現此目的的一種方式,而MVVM則是另一種方式)。通過將它們解耦,可以在發生更改請求時將新的文本框值與當前模型值進行比較。

+0

是的,我知道。問題在於做實際的比較。考慮到一個TextChanged事件可以在A.鍵入一個字符,B.一個字符被刪除,或者C.一段文本被替換(即粘貼一個hilight)時觸發,沒有通用的方法來解決這個問題。如果可以,我寧願避免啓發式。 – 2010-04-21 16:43:31

0

實際上,我所能想到的是存儲某種類型的集合,您可以在其中存儲不同的字符串版本(因此可以撤消很多次,而不只是一次)。 我會在TextBox.Tag中存儲對TextBox集合的引用,因此可以直接存儲/使用它。

最後但並非最不重要的一點,您在事件TextChange期間更新字符串集合。沒有太多的工作,你可以保持完整的歷史,從你自己的結構中獲得以前的價值。

+0

但是,每當有人輸入一個字符時,我需要存儲文本框的全部內容。這將是*巨大*。 – 2010-04-21 16:52:47

+0

注意:如果我不需要堅持把它的大部分保存到磁盤上,巨大的功能不會影響我 – 2010-04-21 17:16:08

+0

你可以丟棄最老的... – 2010-04-21 23:43:22

0

這可能是你想要完成的任務,但是CSLA支持n級撤銷。 CSLA是由Rocky Lhotka編寫的一個偉大的業務對象框架。業務對象處理撤消歷史記錄,並通過數據綁定將其流向UI。

將您的應用程序切換爲使用CSLA將是一個大的承諾,但另一種選擇是查看免費的源代碼以查看他是如何實現它的。

相關問題