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