觀察者模式的應用:Winform窗體之間傳值

觀察者模式的應用:Winform窗體傳值

觀察者模式的概念:

定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的所有依賴者都會收到通知並更新。

今天我們就學着用一下這個觀察者模式,先想象下這個場景:當一個窗體(主窗體)內的值發生變化時,另外幾個窗體內的值也會發生相應的變化。這個最簡單的實現方式是,在子窗體類內創建一個公共方法,在主窗體內創建子窗體的實例。當值發生變化時調用子窗體的公共方法。還有一種簡單方法是用靜態屬性,相當於全局變量,這個只能針對小型應用。第一種有一個缺點,當增加子窗體時,我們需要更改主窗體類的代碼。無法實現動態地增加刪除對子窗體類中值的更新。這時可以引入觀察者模式。
我們先創建一個winform項目,添加FrmMain,FrmSub1,FrmSub2三個窗體,每個窗體都添加一個textbox文本框,實現當主窗體中的文本框值發生變化時,其他兩個子窗體的值也會發生相應的變化。這裏我們參考《Head First設計模式》,先設計發佈者和訂閱者(這裏我感覺還是叫比較順口)。代碼如下:

 public interface ISubject
 {
     /// <summary>
     /// 註冊訂閱者
     /// </summary>
     /// <param name="observer"></param>
     void RegisterObserver(IObserver observer);

     /// <summary>
     /// 刪除訂閱者
     /// </summary>
     /// <param name="observer"></param>
     void RemoveObserver(IObserver observer);

     /// <summary>
     /// 通知訂閱者
     /// </summary>
     void NotifyObservers();
 }

public interface IObserver
{
    /// <summary>
    /// 觀察者對發佈者的響應方法
    /// </summary>
    /// <param name="message"></param>
    void Update(string message);
}

修改FrmMain類,使其實現ISubject接口,再給文本框增加change事件,代碼如下:

public partial class FrmMain : Form, ISubject
{
    private string Message { get; set; }
    private List<IObserver> _observers = new List<IObserver>();


    public FrmMain()
    {
        InitializeComponent();
    }


    private void FrmMain_Load(object sender, EventArgs e)
    {
        FrmSub1 frmSub1 = new FrmSub1(this);
        FrmSub2 frmSub2 = new FrmSub2(this);
        frmSub1.Show();
        frmSub2.Show();
    }

    /// <summary>
    /// 文本框改變事件,調用通知訂閱者方法
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void txtMain_TextChanged(object sender, EventArgs e)
    {
        this.Message = txtMain.Text;
        NotifyObservers();
    }

    /// <summary>
    /// 註冊訂閱者
    /// </summary>
    /// <param name="observer"></param>
    public void RegisterObserver(IObserver observer)
    {
        _observers.Add(observer);
    }

    /// <summary>
    /// 移除訂閱者
    /// </summary>
    /// <param name="observer"></param>
    public void RemoveObserver(IObserver observer)
    {
        _observers.Remove(observer);
    }

    /// <summary>
    /// 通知訂閱者
    /// </summary>
    public void NotifyObservers()
    {
        foreach (IObserver observer in _observers)
        {
            observer.Update(Message);
        }
    }
}

子窗體類(訂閱者)的代碼如下:

public partial class FrmSub1 : Form,IObserver
{
    public FrmSub1(ISubject subject)
    {
        InitializeComponent();
        //註冊
        subject.RegisterObserver(this);
    }
	//當發佈者事件發生時,訂閱者需要執行的方法
    public void Update(string message,bool isShown)
    {
       txtSub.Text = message;
    }
}

上面可以看到FrmMain又依賴了FrmSub1和FrmSub2,其實這是需要另一個方法Show()。不過爲表現出高層不依賴底層,我們改掉代碼,在NotifyObservers(bool isShown)方法中加入對子窗體是否show的判斷,從而實現了避免FrmMain依賴FrmSub1,FrmSub2
FrmMain類的代碼:

public partial class FrmMain : Form, ISubject
{
    private string Message { get; set; }
    private List<IObserver> _observers = new List<IObserver>();


    public FrmMain()
    {
        InitializeComponent();
    }


    private void FrmMain_Load(object sender, EventArgs e)
    {
        //FrmSub1 frmSub1 = new FrmSub1(this);
        //FrmSub2 frmSub2 = new FrmSub2(this);
        //frmSub1.Show();
        //frmSub2.Show();
        //這裏我們使用了方法,擺脫了對子窗體的直接依賴
        NotifyObservers(false);
    }

    /// <summary>
    /// 文本框改變事件,調用通知訂閱者方法
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void txtMain_TextChanged(object sender, EventArgs e)
    {
        this.Message = txtMain.Text;
        NotifyObservers(true);
    }

    /// <summary>
    /// 註冊訂閱者
    /// </summary>
    /// <param name="observer"></param>
    public void RegisterObserver(IObserver observer)
    {
        _observers.Add(observer);
    }

    /// <summary>
    /// 移除訂閱者
    /// </summary>
    /// <param name="observer"></param>
    public void RemoveObserver(IObserver observer)
    {
        _observers.Remove(observer);
    }

    /// <summary>
    /// 通知訂閱者
    /// </summary>
    public void NotifyObservers(bool isShown)
    {

        foreach (IObserver observer in _observers)
        {
            observer.Update(Message,isShown);
        }
    }

}

子窗體類的代碼:

public partial class FrmSub1 : Form,IObserver
{
    public FrmSub1(ISubject subject)
    {
        InitializeComponent();
        subject.RegisterObserver(this);
    }

    //這裏對Update做了改變,isShown爲true,表示事件觸發時子窗體已經顯示
    public void Update(string message,bool isShown)
    {
        if (isShown)
        {
            txtSub.Text = message;
        }
        else
        {
            this.Show();
        }

    }
}
[STAThread]
static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    FrmMain frmMain = new FrmMain();
    FrmSub1 frmSub1 = new FrmSub1(frmMain);
    FrmSub2 frmSub2 = new FrmSub2(frmMain);
    Application.Run(frmMain);
}

完整源代碼參考:https://gitee.com/Alexander360/ProDotnetDesignPatternFramework45

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章