1. 程式人生 > >觀察者模式的應用:Winform窗體之間傳值

觀察者模式的應用: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