zoukankan      html  css  js  c++  java
  • 观察者模式的应用: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

  • 相关阅读:
    PCB genesis方槽加内角槽孔实现方法
    PCB genesis连孔加除毛刺孔(槽孔与槽孔)实现方法(三)
    PCB genesis连孔加除毛刺孔(圆孔与槽孔)实现方法(二)
    PCB genesis连孔加除毛刺孔(圆孔与圆孔)实现方法(一)
    为什么要用Redis而不直接用Map做缓存
    Linux 查询端口被占用命令
    HashMap 和 Hashtable 的区别
    RandomAccess是什么
    接口和抽象类的区别是什么?
    为什么 Java 中只有值传递?
  • 原文地址:https://www.cnblogs.com/AlexanderZhao/p/12184407.html
Copyright © 2011-2022 走看看