如何构建 C# WinForms 模型-视图-呈现器(被动视图)程序?

2023-12-29

我正在设计一个具有以下基本思想的 GUI(类似于 Visual Studio 的基本外观和感觉):

  1. 文件导航
  2. 控制选择器(用于选择编辑器组件中显示的内容)
  3. Editor
  4. 记录器(错误、警告、确认等)

现在,我将使用 TreeView 进行文件导航,使用 ListView 来选择要在编辑器中显示的控件,使用 RichTextBox 进行记录器。编辑器将有 2 种类型的编辑模式,具体取决于在 TreeView 中选择的内容。编辑器要么是一个用于手动编辑文件内文本的 RichTextBox,要么是一个带有拖放 DataGridView 和子文本框的面板,用于在此面板中进行编辑。

我试图遵循被动视图设计模式,将模型与视图完全分离,反之亦然。该项目的性质是我添加的任何组件都可以编辑/删除。因此,我需要独立于给定的控制到下一个。如果今天我使用 TreeView 进行文件导航,但明天我被告知使用其他东西,那么我想相对轻松地实现一个新控件。

我根本不明白如何构建该程序。我了解每个控件一个演示者,但我不知道如何使其工作,以便我有一个带有控件(子视图)的视图(程序的整个 GUI),以便整个视图以及单个视图都可以替换反映我的模型的控件。

在按被动视图标准应该是轻量级的主视图中,我是否单独实现子视图?如果是这样,假设我有一个接口 INavigator 来抽象 Navigator 对象的角色。导航器需要一个 Presenter 和一个 Model 来在导航器视图和主视图之间进行操作。我觉得我在某个地方迷失了设计模式术语。

可以找到最相似相关的问题here https://stackoverflow.com/questions/786476/mvp-and-multiple-user-controls,但它没有足够详细地回答我的问题。

有人可以帮助我理解如何“构建”这个程序吗?我很感激任何帮助。

Thanks,

Daniel


抽象是好的,但在某些时候记住这一点很重要某物必须对一两件事了解一两件事,否则我们只会在地板上放一堆抽象的乐高积木,而不是将它们组装成房子。

控制反转/依赖注入/flippy-dippy-upside-down-whatever-we-call-it-this-week 容器,例如Autofac http://code.google.com/p/autofac/确实可以帮助将这一切拼凑在一起。

当我组装一个 WinForms 应用程序时,通常会得到一个重复的模式。

我将从Program.cs配置 Autofac 容器的文件,然后获取该容器的实例MainForm从中,并显示MainForm。有些人将其称为外壳、工作区或桌面,但无论如何它是具有菜单栏并显示子窗口或子用户控件的“表单”,当它关闭时,应用程序退出。

接下来就是前面提到的MainForm。我做了一些基本的事情,比如拖放一些SplitContainers and MenuBar在 Visual Studio 视觉设计器中进行诸如此类的操作,然后我开始喜欢代码:我会将某些关键接口“注入”到MainForm的构造函数,以便我可以使用它们,以便我的 MainForm 可以编排子控件,而无需真正了解它们。

例如,我可能有一个IEventBroker允许各种组件发布或订阅“事件”的接口,例如BarcodeScanned or ProductSaved。这允许应用程序的各个部分以松散耦合的方式响应事件,而不必依赖连接传统的 .NET 事件。例如,EditProductPresenter这与我的EditProductUserControl可以说this.eventBroker.Fire("ProductSaved", new EventArgs<Product>(blah))IEventBroker将检查该事件的订阅者列表并调用他们的回调。例如,ListProductsPresenter可以监听该事件并动态更新ListProductsUserControl它所附着的。最终结果是,如果用户将产品保存在一个用户控件中,则另一个用户控件的演示者可以做出反应并更新自身(如果它恰好是打开的),而任一控件都不必知道彼此的存在,并且无需MainForm必须精心策划该事件。

如果我正在设计一个 MDI 应用程序,我可能有MainForm实施一个IWindowWorkspace接口有Open() and Close()方法。我可以将该界面注入到我的各个演示者中,以允许他们打开和关闭其他窗口,而无需他们意识到MainForm直接地。例如,ListProductsPresenter可能想开一个EditProductPresenter以及相应的EditProductUserControl当用户双击数据网格中的一行时ListProductsUserControl。它可以参考一个IWindowWorkspace——这实际上是MainForm,但它不需要知道这一点——并调用Open(newInstanceOfAnEditControl)并假设该控件以某种方式显示在应用程序的适当位置。 (这MainForm据推测,实现会将控件交换到面板上某处的视图中。)

但到底怎么会ListProductsPresenter create的那个实例EditProductUserControl? Autofac的代理工厂 http://code.google.com/p/autofac/wiki/DelegateFactories在这里真的很有趣,因为您只需将委托注入到演示者中,Autofac 就会自动将其连接起来,就像它是一个工厂一样(伪代码如下):


public class EditProductUserControl : UserControl
{
    public EditProductUserControl(EditProductPresenter presenter)
    {
        // initialize databindings based on properties of the presenter
    }
}

public class EditProductPresenter
{
    // Autofac will do some magic when it sees this injected anywhere
    public delegate EditProductPresenter Factory(int productId);

    public EditProductPresenter(
        ISession session, // The NHibernate session reference
        IEventBroker eventBroker,
        int productId)    // An optional product identifier
    {
        // do stuff....
    }

    public void Save()
    {
        // do stuff...
        this.eventBroker.Publish("ProductSaved", new EventArgs(this.product));
    }
}

public class ListProductsPresenter
{
    private IEventBroker eventBroker;
    private EditProductsPresenter.Factory factory;
    private IWindowWorkspace workspace;

    public ListProductsPresenter(
        IEventBroker eventBroker,
        EditProductsPresenter.Factory factory,
        IWindowWorkspace workspace)
    {
       this.eventBroker = eventBroker;
       this.factory = factory;
       this.workspace = workspace;

       this.eventBroker.Subscribe("ProductSaved", this.WhenProductSaved);
    }

    public void WhenDataGridRowDoubleClicked(int productId)
    {
       var editPresenter = this.factory(productId);
       var editControl = new EditProductUserControl(editPresenter);
       this.workspace.Open(editControl);
    }

    public void WhenProductSaved(object sender, EventArgs e)
    {
       // refresh the data grid, etc.
    }
}

So the ListProductsPresenter知道关于Edit功能集(即编辑演示者和编辑用户控件)——这很好,它们是齐头并进的——但它不需要知道所有依赖关系 of the Edit功能集,而不是依赖 Autofac 提供的委托来解决它的所有这些依赖关系。

一般来说,我发现“演示者/视图模型/监督控制器”之间存在一对一的对应关系(我们不要太关注差异,因为最终它们都非常相似)和“UserControl/Form". The UserControl在其构造函数中接受呈现器/视图模型/控制器,并根据需要进行数据绑定本身,尽可能地遵循呈现器。有些人隐藏UserControl来自演示者通过界面,例如IEditProductView,如果视图不是完全被动的,这可能很有用。我倾向于对所有事情使用数据绑定,因此通信是通过INotifyPropertyChanged不用打扰。

但是,如果演讲者无耻地与观点联系在一起,你的生活就会变得更加轻松。对象模型中的属性是否与数据绑定不匹配?公开一个新属性就是如此。你永远不会有一个EditProductPresenter and an EditProductUserControl使用一种布局,然后想要编写与同一演示者一起使用的新版本的用户控件。您只需编辑它们,它们的所有意图和目的都是一个单元,一个功能,演示者仅存在,因为它很容易进行单元测试,而用户控件则不然。

如果您希望某个功能可替换,则需要将整个功能抽象化。所以你可能有一个INavigationFeature您的界面MainForm与. 交谈。你可以有一个TreeBasedNavigationPresenter实现INavigationFeature并被消耗TreeBasedUserControl。你可能有一个CarouselBasedNavigationPresenter这也实现了INavigationFeature并被消耗CarouselBasedUserControl。用户控件和演示者仍然齐头并进,但您的MainForm不必关心它是否与基于树的视图或基于轮播的视图交互,并且您可以将它们交换出来而无需MainForm成为更明智的人。

最后,你很容易让自己感到困惑。每个人都是迂腐的,并使用略有不同的术语来表达相似的架构模式之间的微妙(通常是不重要的)差异。以我的拙见,依赖注入对于构建可组合、可扩展的应用程序确实有奇迹,因为耦合被降低了。将功能分离为“呈现器/视图模型/控制器”和“视图/用户控件/表单”对于质量来说确实是奇迹,因为大多数逻辑都被引入到前者中,从而可以轻松进行单元测试;将这两个原则结合起来似乎确实是您正在寻找的,您只是对术语感到困惑。

或者,我可能已经充满了它。祝你好运!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何构建 C# WinForms 模型-视图-呈现器(被动视图)程序? 的相关文章

  • 如何在 C++ 中的文件末尾添加数据?

    我已按照网上的说明进行操作 此代码应该将输入添加到文件 数据库 的末尾 但当我检查时 数据会覆盖现有数据 请帮忙 这是我的代码 int main string name string address string handphone cou
  • 用 C++ 进行服装建模 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在编写一些软件 最终会绘制一个人体框架 可以配置各种参数 并且计划是在假人身上放置某种衣服 我研究
  • 如何读取扩展文件属性/文件元数据

    因此 我按照教程使用 ASP net core 将文件 上传 到本地路径 这是代码 public IActionResult About IList
  • 使用Physics.Raycast 和Physics2D.Raycast 检测对象上的点击

    我的场景中有一个空的游戏对象 带有 2D 组件盒碰撞器 我将脚本附加到该游戏对象 void OnMouseDown Debug Log clic 但是当我点击我的游戏对象时 没有任何效果 你有什么想法 如何检测我的盒子碰撞器上的点击 使用光
  • 为 Visual Studio 2013 编译 Tesseract

    我正在尝试使用tesseract在 Visual Studio 2013 中 我在链接器 gt 输入 不是 libtesseract302 static lib 中使用 libtesseract302 lib 一切都正常 并且已编译并运行
  • 如何修复此错误“GDI+ 中发生一般错误”?

    从默认名称打开图像并以默认名称保存 覆盖它 我需要从 Image Default jpg 制作图形 将其放在 picturebox1 image 上并在 picurebox1 上绘制一些图形 它有效 这不是我的问题 但我无法保存 pictu
  • 将内置类型转换为向量

    我的 TcpClient 类接受vector
  • 将 System.Windows.Input.KeyEventArgs 键转换为 char

    我需要将事件参数作为char 但是当我尝试转换 Key 枚举时 我得到的字母和符号与传入的字母和符号完全不同 如何正确地将密钥转换为字符 这是我尝试过的 ObserveKeyStroke this new ObervableKeyStrok
  • C# Dns.GetHostEntry 不返回连接到 WiFi 的移动设备的名称

    我有一个 C 中的 Windows 窗体应用程序 我试图获取列表中所有客户端的主机名 下面给出的是 ra00l 来自此链接的代码示例 GetHostEntry 非常慢 https stackoverflow com questions 99
  • 使用 C 语言使用 strftime() 获取缩写时区

    我看过this https stackoverflow com questions 34408909 how to get abbreviated timezone and this https stackoverflow com ques
  • 如何在 C# 中定义文本框数组?

    您好 当我在 Windows 申请表上创建文本框时 我无法将其命名为 box 0 box 1 等 我这样做的目的是因为我想循环使用它们 其实我发现TextBox array firstTextBox secondTextBox 也有效
  • 如何将整数转换为 void 指针?

    在 C 中使用线程时 我面临警告 警告 从不同大小的整数转换为指针 代码如下 include
  • 批量更新 SQL Server C#

    我有一个 270k 行的数据库 带有主键mid和一个名为value 我有一个包含中值和值的文本文件 现在我想更新表格 以便将每个值分配给正确的中间值 我当前的方法是从 C 读取文本文件 并为我读取的每一行更新表中的一行 必须有更快的方法来做
  • 将 log4net 与 Autofac 结合使用

    我正在尝试将 log4net 与 Autofac 一起使用 我粘贴了这段代码http autofac readthedocs org en latest examples log4net html http autofac readthed
  • HttpWebRequest 在第二次调用时超时

    为什么以下代码在第二次 及后续 运行时超时 代码挂在 using Stream objStream request GetResponse GetResponseStream 然后引发 WebException 表示请求已超时 我已经尝试过
  • Server.MapPath - 给定的物理路径,预期的虚拟路径

    我正在使用这行代码 var files Directory GetFiles Server MapPath E ftproot sales 在文件夹中查找文件 但是我收到错误消息说 给定物理路径但虚拟路径 预期的 我对在 C 中使用 Sys
  • 有没有办法强制显示工具提示?

    我有一个验证字段的方法 如果无法验证 该字段将被清除并标记为红色 我还希望在框上方弹出一个工具提示 并向用户显示该值无效的消息 有没有办法做到这一点 并且可以控制工具提示显示的时间 我怎样才能让它自己弹出而不是鼠标悬停时弹出 If the
  • 英特尔 Pin 与 C++14

    问题 我有一些关于在 C 14 或其他 C 版本中使用英特尔 Pin 的问题 使用较新版本从较旧的 C 编译代码很少会出现任何问题 但由于 Intel Pin 是操作指令级别的 如果我使用 C 11 或 C 14 编译它 是否会出现任何不良
  • 如何使用 Word Automation 获取页面范围

    如何使用办公自动化找到 Microsoft Word 中第 n 页的范围 似乎没有 getPageRange n 函数 并且不清楚它们是如何划分的 这就是您从 VBA 执行此操作的方法 转换为 Matlab COM 调用应该相当简单 Pub
  • 在客户端系统中安装后桌面应用程序无法打开

    我目前正在使用 Visual Studio 2017 和 4 6 1 net 框架 我为桌面应用程序创建了安装文件 安装程序在我的系统中完美安装并运行 问题是安装程序在其他计算机上成功安装 但应用程序无法打开 edit 在客户端系统中下载了

随机推荐