我正在设计一个具有以下基本思想的GUI(类似地模仿Visual Studio的基本外观):文件导航控制选择器(用于选择在编辑器组件中显示的内容)编辑记录器(错误,警告,确认等)现在,我将使用TreeView进行文件导航,使用ListView选择要在编辑器中显示的控件,并使用RichTextBox记录器。根据在TreeView中选择的内容,编辑器将具有2种类型的编辑模式。编辑器将是一个RichTextBox,用于手动编辑文件中的文本,或者它将是一个带有拖放数据面板视图的面板和子TextBox,以便在此面板中进行编辑。我试图遵循Passive View设计模式,以将Model与View完全分离,反之亦然。该项目的性质是,我添加的任何组件都必须进行编辑/删除。因此,我需要独立于给定的控件到下一个控件。如果今天我使用TreeView进行文件导航,但是明天我被告知要使用其他方法,那么我想相对轻松地实现一个新控件。我根本不了解如何构建程序。我了解每个控件一个Presenter,但是我不知道如何使它工作,以至于我拥有一个带有控件(子视图)的视图(程序的整个GUI),使得整个视图以及单个视图都可以替换。反映我的模型的控件。在主视图(按被动视图标准应该是轻量级的)中,我是否单独实现子视图?如果是这样,说我有一个接口INavigator来抽象Navigator对象的角色。导航器将需要一个Presenter和一个Model才能在Navigator视图和主视图之间起作用。我感觉好像迷失在某个地方的设计模式行话中。可以在here中找到最相似的问题,但是它没有足够详细地回答我的问题。有人能帮我理解如何“构建”该程序吗?感谢您的帮助。谢谢,丹尼尔 最佳答案 抽象是好的,但重要的是要记住,在某个时候某事物必须对一两个事物了解一两个事物,否则我们只会将一堆精美抽象的乐高玩具坐在地板上,而不是将它们组装成一堆。一个房子。控制反转/依赖注入/ flippy-dippy-upside-down-无论我们如何称呼本周之类的容器,例如Autofac都可以真正帮助将它们拼凑在一起。当我将WinForms应用程序放在一起时,通常会得到重复的模式。我将从一个Program.cs文件开始,该文件配置Autofac容器,然后从中获取MainForm的实例,并显示MainForm。有人将其称为外壳,工作区或桌面,但无论如何,它是具有菜单栏并显示子窗口或子用户控件的“窗体”,并且在关闭时退出应用程序。接下来是前面提到的MainForm。我在Visual Studio视觉设计器中做了一些基本的事情,例如拖放SplitContainers和MenuBar等,然后我开始在代码中花哨:我将某些关键接口“注入”到了MainForm的构造函数,以便我可以使用它们,以便MainForm可以编排子控件,而无需真正了解它们。例如,我可能有一个IEventBroker界面,该界面允许各种组件发布或订阅“事件”(如BarcodeScanned或ProductSaved)。这允许应用程序的某些部分以松散耦合的方式响应事件,而不必依赖于连接传统的.NET事件。例如,与我的EditProductPresenter一起出现的EditProductUserControl可能会说this.eventBroker.Fire("ProductSaved", new EventArgs<Product>(blah)),而IEventBroker会检查该事件的订户列表并调用其回调。例如,ListProductsPresenter可以侦听该事件并动态更新其附加到的ListProductsUserControl。最终结果是,如果用户将产品保存在一个用户控件中,则另一个用户控件的演示者可以在打开的情况下做出反应并进行自我更新,而无需任何控件都知道彼此的存在,并且无需MainForm必须安排该事件。如果我正在设计MDI应用程序,则可能要让MainForm实现具有IWindowWorkspace和Open()方法的Close()接口。我可以将该界面注入我的各种演示者中,以允许他们打开和关闭其他窗口,而无需他们直接了解MainForm。例如,当用户双击ListProductsPresenter中数据网格中的一行时,EditProductPresenter可能想要打开EditProductUserControl和相应的ListProductsUserControl。它可以引用IWindowWorkspace(实际上是MainForm,但不需要知道),然后调用Open(newInstanceOfAnEditControl)并假定控件以某种方式显示在应用程序的适当位置。 (MainForm实现可能会将控件交换到面板上某处的视图中。)但是ListProductsPresenter将如何创建EditProductUserControl的实例? Autofac's delegate factories在这里是一个真正的快乐,因为您只需将一个代表注入到演示者中,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. }}因此ListProductsPresenter知道Edit功能集(即,编辑演示者和编辑用户控件)-很好,他们可以并驾齐驱-但不必知道关于Edit功能集的所有依赖关系的说明,而是依靠Autofac提供的委托为其解决所有这些依赖关系。通常,我发现我在“演示者/视图模型/监督控制器”之间存在一对一的对应关系(让我们在当天结束时不要太着迷于它们之间的差异)。 UserControl / Form”。 UserControl在其构造函数中接受演示者/视图模型/控制器,并在适当时对自身进行数据绑定,从而尽可能多地推迟给演示者。有人通过UserControl之类的界面从演示者隐藏IEditProductView,如果视图不是完全被动的,则很有用。我倾向于对所有内容都使用数据绑定,因此通信是通过INotifyPropertyChanged完成的,不要打扰。但是,如果演示者与视图无懈可击,您将使生活变得更加轻松。对象模型中的属性是否与数据绑定无关?公开一个新的属性。您永远不会拥有具有一个布局的EditProductPresenter和EditProductUserControl,然后想要编写与同一演示者一起使用的用户控件的新版本。您将只对它们进行编辑,它们对于所有意图和目的都是一个单元,一项功能,演示者仅存在,因为它很容易进行单元测试,而用户控件则不行。如果您希望功能是可替换的,则需要像这样抽象整个功能。因此,您可能具有与INavigationFeature进行对话的MainForm接口。您可以具有实现TreeBasedNavigationPresenter的INavigationFeature,并由TreeBasedUserControl使用。您可能有一个同时实现CarouselBasedNavigationPresenter且被INavigationFeature占用的CarouselBasedUserControl。用户控件和演示者仍然可以并驾齐驱,但是您的MainForm不必关心它是否与基于树的视图或基于轮播的视图进行交互,并且您可以将它们换出而无需是更明智的。最后,很容易混淆自己。每个人都是书呆子,并且使用略有不同的术语来传达相似构架模式之间的细微差异(通常不重要)。在我的拙见中,依赖关系注入对于构建可组合的,可扩展的应用程序确实产生了奇迹,因为保持了耦合。将功能分为“演示者/视图模型/控制器”和“视图/用户控件/表单”确实对质量产生了疑问,因为大多数逻辑都被拉入了前者,因此可以轻松地对其进行单元测试;而结合这两个原则似乎确实是您要寻找的,您只是对术语感到困惑。或者,我可能会充满。祝好运!关于c# - 如何构造C#WinForms模型 View 呈现器(被动 View )程序?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4329776/
10-09 01:08