`
kakajw
  • 浏览: 263365 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

从设计模式的角度分析和实现 windows 资源管理器的联动

 
阅读更多

主要关注
设计模式:观察者模式
架构模型:MVC模型

 

ps: 本文略过有关观察者模式和MVC模型的概念介绍。


1.资源管理器Explorer的组成

Explorer的组成:Tree(左边树,简称树),ListView(右边列表视图,简称列表),LocationBar(上方的地址栏,简称地址栏),如下所示:



 

2.三者的联动

资源管理器中,可通过选中树上的目录节点,或双击列表中的目录,或向地址栏填入目录路径并回车等事件,以浏览或选中指定目录;
值得注意的是,树、列表和地址栏的变化或响应是同步的,也就是说,三者中任一的改变可能会引起其他二者的同步变化,例如:当选中树上的某一目录节点A时,列表会同步显示A目录下的目录和文件,地址栏也会同步显示A目录的路径;或者向地址栏填入目录B的路径并回车时,树会改变当前选中节点到B节点,列表会同步显示B目录包含的目录和文件;当双击列表的目录也会引起树和地址栏的同步变化;姑且把这种同步的变化称为联动。

 

联动的实现
显然,三者的联动是通过事件响应的方式实现,具体实现方式也有多种;


简单的设计:
三者均包含其余两者对象的引用,当事件发生时,主体(事件源)响应当前事件,同时通过引用调用其余二者的相应的响应方法,以实现同步变化(联动);例如:树对象包含列表对象和地址栏对象的引用,当树的选中节点发生变化时,树自身会响应这种变化(如选中的节点的颜色变化,展开节点等),同时通过引用调用列表对象和地址栏对象的相应的方法,以保持联动;  这也是典型的观察者模式,被观察者(主题角色)是树对象,观察者是列表对象和地址栏对象,观察者订阅树对象的发布的事件;同时,三者既充当被观察者,又充当观察者角色;
缺点:组件间耦合,不好扩展,当新增组件、移除组件时,所有组件都受影响,需要重新重新设置对其他组件对象的引用;


更好的设计
为消除组件间的耦合,使得组件间互不可见,是透明的,我们引入一个被称为控制器(Controller)的角色,它等同于MVC模型的控制器(Controller)的角色,而树、列表和地址栏被当成视图(View),这样控制器就能响应视图(三者)的变化,而视图之间是透明的;而关于模型(Model)由什么来充当呢,模型的变化会通过控制器传递给视图,引起视图的变化,并且视图的变化也有可能通过控制器引起模型的改变(或许会有疑问,会不会导致死循环?不控制的话当然会,但可以通过判断视图的状态信息来防止);因此模型应该是能被树、列表和地址栏所改变,且是通过上述的这些事件来改变的,它就是当前目录的路径(或目录地址)path信息!也是资源管理器的关键设计。
M:path
V:tree、listView、LocationBar
C:Controller

 

更进一步,从观察者设计模式的角度看来,核心事件为当前目录路径(path)的变化,树、列表和地址栏既是观察者,又是被观察者,因此我们有以下设计:

 

核心事件:path 的变化。
IPathViewer:接口,显示器,视图,具备根据 Path 更新方法(更新当前视图)。
IPathEditer:接口,编辑器,被观察者角色(主题角色),编辑或设置path值,发布 path 变化事件,并触发path变化事件。
Controller:类,控制器,观察者角色,订阅编辑器的 Path 变化事件,并响应该事件;所有的显示器和编辑器都需要向控制器注册,因此控制器拥有显示器集合和编辑器集合;通过编辑器集合,控制器可以向所有的编辑器订阅 Path 变化事件,当path变化事件发生时,控制器遍历显示器集合,并调用每个显示器的path更新方法以更新视图。


 另外,可以看出,就控制器和所有的显示器之间的关系而言,控制器也是充当的被观察者角色,所有的显示器充当观察者角色; 而对于编辑器和显示器和而言,也可理解为,编辑器是被观察者,显示器观察者,只是订阅-发布的事件响应过程是通过控制器间接实现的,这正是为了解耦的结果;
 
对于树、列表和地址栏,他们既是显示器,是针对path的不同视图,具有根据path更新方法,又是编辑器,能触发path变化事件;同时实现这两个接口;

 

三者联动过程:
编辑器发布和触发path变化事件,控制器订阅和响应该事件,并调用显示器的相应方法更新视图。


3.代码实现(C#): 

 

  IPathViewer:显示器接口,定义path属性以及path更新方法; 

    /// <summary>
    /// 定义 Path 视图。
    /// </summary>
    public interface IPathViewer
    {
        /// <summary>
        /// 定义 Path 属性。
        /// </summary>
        string Path { get; set; }
    }

  IPathEditer:编辑器接口,发布 Path 变化事件;  

    /// <summary>
    /// 定义 Path 的编辑器。
    /// </summary>
    public interface IPathEditer
    {
        /// <summary>
        /// 发布 Path 事件。
      /// </summary>
        event PathEventHandler PathChanging;
    }

  Treeview 控件:

    /// <summary>
    /// 定义 Treeview 控件。
    /// </summary>
    public class FSTreeview : TreeView,IPathViewer, IPathEditer
    {
        /// <summary>
        /// 定义 Path 属性。
        /// </summary>
        private string _path;
        public string Path
        {
            get { return _path; }
            
          // 更新视图的方法。
          set
            {
                _path = value;
                try
                {
                    _Locate(_path);
                }
                catch (Exception e)
                {
                    throw e;
                }

            }
        }

        /// <summary>
        /// 发布 Path 事件。
     /// </summary>
        public event PathEventHandler PathChanging;

        /// <summary>
        /// 初始化 FSTreeview 。
        /// </summary>
        public void Initialize()
        {
            _LoadRootNode();
            BeforeExpand += _BeforeExpandHandler;
            AfterSelect += _AfterSelectHandler;
        }

        /// <summary>
        /// 定位到某个节点。
        /// </summary>
        /// <param name="p_path">指定节点的路径 <see cref="string"/> 。</param>
        private void _Locate(string p_path)
        {
            if (p_path != null)
            {                              
                this.SelectedNode = parentNode;
            }
        }       

        /// <summary>
        /// 处理 BeforeExpand 事件。
        /// </summary>
        /// <param name="p_sender">表示事件的来源 <see cref="object"/> 。</param>
        /// <param name="e">表示事件的数据 <see cref="TreeViewCancelEventArgs"/> 。</param>
        private void _BeforeExpandHandler(object p_sender, TreeViewCancelEventArgs e)
        {  }

        /// <summary>
        /// 处理 AfterSelect 事件。
        /// </summary>
        /// <param name="p_sender">表示事件的来源 <see cref="object"/> 。</param>
        /// <param name="e">表示事件的数据 <see cref="TreeViewEventArgs"/> 。</param>
        private void _AfterSelectHandler(object p_sender, TreeViewEventArgs e)
        {
           ......
           //触发 path 事件。
           var pathAgrs = new PathEventAgrs(fullpath);
           PathChanging(this, pathAgrs); 
        } 
    }

 定义 ListView 控件:

    /// <summary>
    /// 定义 ListView 控件。
    /// </summary>
    public class FSListView : ListView, IPathViewer, IPathEditer
    {
        /// <summary>
        /// 定义 Path 属性。
        /// </summary>
        private string _path = null;
        public string Path
        {
            get
            {
                return _path;
            }
            // 更新视图的方法。
            set
            {
                _path = value;
                if (_path != null)
                {
                    this.Items.Clear();
                    _LoadItems(value);
                }
            }
        }

        /// <summary>
        /// 发布 PathEvent 事件。
        /// </summary>
        public event PathEventHandler PathChanging;


        /// <summary>
        /// 初始化 FSListView 。
        /// </summary>
        public void Initialize()
        {
            this.DoubleClick += OnDoubleClick;
        }

        /// <summary>
        /// 处理 ListView 的双击事件。
        /// </summary>
        /// <param name="p_sender">指定事件的来源 <see cref="object"/>。</param>
        /// <param name="e">指定事件的数据 <see cref="EventArgs"/>。</param>
        private void OnDoubleClick(object p_sender, EventArgs e)
        {
          .....   
          // 触发 path 事件。          
          PathEventAgrs pathArgs = new PathEventAgrs(path);
          PathChanging(this, pathArgs);
        }
        /// <summary>
        /// 加载 items 并显示。
        /// </summary>
        /// <param name="p_path">指定 path <see cref="string"/>。</param>
        private void _LoadItems(string p_path)
        { }

    }

    定义 AdressBar 控件。

    /// <summary>
    /// 定义 AdressBar 控件。
    /// </summary>
    public class AdressBar : TextBox, IPathViewer, IPathEditer
    {
        /// <summary>
        /// 定义 Path 属性。
        /// </summary>
        private string _path;
        public string Path
        {
            get
            {
                return _path;
            }
           // 更新视图的方法。
            set
            {
                _path = value;
                if (value != null)
                {
                    this.Text = value;
                }
            }
        }

        /// <summary>
        /// 发布 PathEvent 事件。
     /// </summary>
        public event PathEventHandler PathChanging;


        /// <summary>
        /// 初始化 AdressBar 。
        /// </summary>
        public void Initialize()
        {
            this.Validating += _OnValidatingHandler;
            this.KeyPress += _OnKeyPressHandler;
        }

        /// <summary>
        /// 处理 Validating 事件。
        /// </summary>
        /// <param name="p_sender">表示事件的来源 <see cref="object"/>。</param>
        /// <param name="e">表示事件的数据 <see cref="EventArgs"/>。</param>
        private void _OnValidatingHandler(object p_sender, EventArgs e)
        {
            // 触发 path 事件。
            PathEventAgrs adressBarAgrs = new PathEventAgrs(this.Text);
            PathChanging(this, adressBarAgrs);
        }

        /// <summary>
        /// 处理 KeyPress 事件。
        /// </summary>
        /// <param name="p_sender">表示事件的来源 <see cref="object"/>。</param>
        /// <param name="e">表示事件的数据 <see cref="KeyPressEventArgs"/>。</param>
        private void _OnKeyPressHandler(object p_sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == 13)
            {
                _OnValidatingHandler(this, e);
            }
        }
    }

  控制器的实现:PathEventController

 /// <summary>
    /// 表示 Path 事件的控制器。
    /// </summary>
    public class PathEventController
    {
       // 显示器和编辑器的集合。
        private readonly List<IPathViewer> _pathViewerList = null;
        private readonly List<IPathEditer> _pathEditerList = null;

        /// <summary>
        /// 定义 Path 属性。 
        /// </summary>
        private string _path = null;
        public string Path
        {
            get
            {
                return _path;
            } 

         // 当path 改变时,更新所有视图。
       set
            {
                if ((value != null) && (!value.Equals(_path)))
                {
                        _path = _ValidatePath(value);
                        foreach (IPathViewer pathViewer in _pathViewerList)
                        {
                            pathViewer.Path = _path;
                        }
                    
                    
                }
            }
        }

        /// <summary>
        /// 初始化 PathEventController 实例。
        /// </summary>
        public PathEventController()
        {
            _pathViewerList = new List<IPathViewer>();
            _pathEditerList = new List<IPathEditer>();
        }

        public void Initialize()
        {
            Path = "/";
        }

        /// <summary>
        /// 添加 Control 。
        /// </summary>
        /// <param name="p_control">指定 Control 实例 <see cref="Control"/>。</param>
        public void AddControls(Control p_control)
        {
            if (typeof(IPathViewer).IsInstanceOfType(p_control))
            {
                IPathViewer pathViewer = (IPathViewer)p_control;

                // 添加 IPathViewer 实例到容器。
                _pathViewerList.Add(pathViewer);
            }
            if (typeof(IPathEditer).IsInstanceOfType(p_control))
            {  
                IPathEditer pathEditer = (IPathEditer)p_control;

                // 订阅 IPathEditer 的 Path 事件。
                pathEditer.PathChanging += _OnPathChanging;

                // 添加 IPathEditer 实例到容器。
                _pathEditerList.Add(pathEditer);
            }
        }


        /// <summary>
        ///  处理 IPathViewer 的 PathChanging 事件。
      /// </summary>
        /// <param name="p_sender">指定事件的来源 <see cref="object"/>。</param>
        /// <param name="e">指定事件包含的数据 <see cref="PathEventAgrs"/>。</param>
        private void _OnPathChanging(object p_sender, PathEventAgrs e)
        {
            // 调用 Path 属性的 set 方法更新视图。
            Path = e.Path;
        }

        /// <summary>
        /// 验证 Path 的有效性和正确性。
        /// </summary>
        /// <returns>表示 Path 有效性 <see cref="bool"/>。</returns>
        private string _ValidatePath(string p_path)
        {  }
    }

 定义 Path 事件 和相关事件参数类: 

    /// <summary>
    /// 发布 PathEvent 事件。
    /// </summary>
    /// <param name="p_sender"></param>
    /// <param name="e"></param>
    public delegate void PathEventHandler(object p_sender, PathEventAgrs e);


    /// <summary>
    /// 表示 PathEvent 事件提供数据。 
    /// </summary>
    public class PathEventAgrs : EventArgs
    {
        /// <summary>
        /// 表示文件系统的路径。
        /// </summary>
        public string Path { get; private set; }

        /// <summary>
        /// 初始化 PathEventAgrs 的实例。
        /// </summary>
        /// <param name="p_path">指定文件路径 <see cref="string"/>。</param>
        public PathEventAgrs(string p_path)
        {
            Path = p_path;
        }
    }

 

  • 大小: 78.1 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics