James Bond Asked:2024-11-04 17:55:21 +0000 UTC2024-11-04 17:55:21 +0000 UTC 2024-11-04 17:55:21 +0000 UTC 哪种程序架构最适合 MDI 界面? 772 多文档界面意味着程序将至少有两种表单 - 主表单(容器)和表单,其副本将在主表单内创建。 最初,我计划将我的程序建立在经典 MVC 的基础上,但如果一个控制器同时负责主窗体和子窗体,在我看来,这似乎是一个重大违规,会使控制器变得过于复杂。 但具有 MDI 界面的程序非常常见,并且可能已经有一个经过验证且非常适合它们的架构。请告诉我,具体是哪一个? PS 目前我认为下面的配置是最成功的: c# 2 个回答 Voted Pavel Mayorov 2024-11-05T20:44:01Z2024-11-05T20:44:01Z 有许多经过验证且合适的架构(MVC、MVP、MVVM),它们都有一个共同的缺点 - 它们太抽象,不能回答代码实际应该如何编写的问题。 事实上,MVC 是如此抽象,以至于在 Habré 上人们仍然定期争论表单到底是什么 - 模型、控制器或视图 因此,我建议降低一点。所以,你有两种形式,你需要以某种方式连接它们。而且,正如您自己所注意到的,建议确保子表单不了解父表单。 有三种方法可以做到这一点。 方法1.通过通用模型。 子窗体可以更改模型中的属性(最好通过数据绑定),模型将发送事件,主窗体将做出反应(最好通过数据绑定)。这也适用于相反的方向。 当然,这并不意味着主窗体和子窗体的模型必须在字面上是通用的。请记住,与其他 MVC 组件一样,模型可以分为独立的子模型或分层相关的子模型。将模型所需的部分传递给子窗体。 方法2.通过事件 子窗体可以声明事件,主窗体可以订阅该事件。 方法三、通过接口 当事件不够或太多时。 子窗体可以在构造函数中请求某些接口(例如,IChildFormOwner或IChildFormHost),主窗体可以实现它(直接或通过内部类)。 方法4.通过任务 通常,一种罕见的方法仅适用于一种情况 - 在对话框表单中,其任务是向用户请求数据。在这种情况下,表单的生命周期可以很好地封装成异步方法,例如 ShowAsync: private readonly TaskCompletionSource tcs = new(); private void bthOk_Click(object sender, EventArgs e) { tcs.TrySetResult(); Close(); } private void btnCancel_Click(object sender, EventArgs e) { tcs.TrySetCanceled(); Close(); } protected override void OnFormClosed(FormClosedEventArgs e) { tcs.TrySetCanceled(); base.OnFormClosed(e); } public Task ShowAsync() { Show(); Activate(); return tcs.Task; } Best Answer Faraday 2024-11-04T20:53:29Z2024-11-04T20:53:29Z 对于MDI项目,您可以使用以下常见架构模式之一: 多维控制器 MVP MVVM 文档视图 您可以根据您对上述各项的理解来使用上述任何一项。让我们考虑一下该模式的组织MVC。一般来说,值得注意的是,使用 会更正确MVVM,它按规则运行MVC,但用于桌面应用程序并具有一些功能。 如果我们超越经典应用程序MVC,控制器必须负责某个狭窄的区域,并提供处理与该区域相关的数据的所有必要方法。 根据相关评论中的要求,我可以假设为每个表单使用单独的控制器并不是一个坏主意。管理子表单可以通过将适当的参数传递给控制器来完成,您还可以存储子表单的链接并从父表单调用一些方法 与父控制器的交互可以通过callback一个函数来实现,该函数可以在初始化期间传递给构造,或者在调用时传递给子控制器的方法。执行逻辑大致如下图所示(抱歉我的画功) 这样,您就不会使用不属于主控制器的操作使主控制器过载,而是能够将它们移动到子控制器级别,并且如果需要,您还可以维护控制器之间的依赖关系。 为了保持控制器之间的依赖性和通信,我仍然会选择stateless这种方法。要点是所有交互都是通过数据库或有状态服务进行的。但值得注意的是,如果您希望在程序中动态更改而不保存状态,则这种方法不太适合。该方法的结构如下图所示。
有许多经过验证且合适的架构(MVC、MVP、MVVM),它们都有一个共同的缺点 - 它们太抽象,不能回答代码实际应该如何编写的问题。
事实上,MVC 是如此抽象,以至于在 Habré 上人们仍然定期争论表单到底是什么 - 模型、控制器或视图
因此,我建议降低一点。所以,你有两种形式,你需要以某种方式连接它们。而且,正如您自己所注意到的,建议确保子表单不了解父表单。
有三种方法可以做到这一点。
方法1.通过通用模型。
子窗体可以更改模型中的属性(最好通过数据绑定),模型将发送事件,主窗体将做出反应(最好通过数据绑定)。这也适用于相反的方向。
当然,这并不意味着主窗体和子窗体的模型必须在字面上是通用的。请记住,与其他 MVC 组件一样,模型可以分为独立的子模型或分层相关的子模型。将模型所需的部分传递给子窗体。
方法2.通过事件
子窗体可以声明事件,主窗体可以订阅该事件。
方法三、通过接口
当事件不够或太多时。
子窗体可以在构造函数中请求某些接口(例如,
IChildFormOwner或IChildFormHost),主窗体可以实现它(直接或通过内部类)。方法4.通过任务
通常,一种罕见的方法仅适用于一种情况 - 在对话框表单中,其任务是向用户请求数据。在这种情况下,表单的生命周期可以很好地封装成异步方法,例如 ShowAsync:
对于
MDI项目,您可以使用以下常见架构模式之一:您可以根据您对上述各项的理解来使用上述任何一项。让我们考虑一下该模式的组织
MVC。一般来说,值得注意的是,使用 会更正确MVVM,它按规则运行MVC,但用于桌面应用程序并具有一些功能。如果我们超越经典应用程序
MVC,控制器必须负责某个狭窄的区域,并提供处理与该区域相关的数据的所有必要方法。根据相关评论中的要求,我可以假设为每个表单使用单独的控制器并不是一个坏主意。管理子表单可以通过将适当的参数传递给控制器来完成,您还可以存储子表单的链接并从父表单调用一些方法
与父控制器的交互可以通过
callback一个函数来实现,该函数可以在初始化期间传递给构造,或者在调用时传递给子控制器的方法。执行逻辑大致如下图所示(抱歉我的画功)这样,您就不会使用不属于主控制器的操作使主控制器过载,而是能够将它们移动到子控制器级别,并且如果需要,您还可以维护控制器之间的依赖关系。
为了保持控制器之间的依赖性和通信,我仍然会选择
stateless这种方法。要点是所有交互都是通过数据库或有状态服务进行的。但值得注意的是,如果您希望在程序中动态更改而不保存状态,则这种方法不太适合。该方法的结构如下图所示。