在 C# 中应该使用哪种设计模式和 WPF 来通过用户界面动态“更改对象的类”?

Posted

技术标签:

【中文标题】在 C# 中应该使用哪种设计模式和 WPF 来通过用户界面动态“更改对象的类”?【英文标题】:What design pattern should be used in C# with WPF for dynamically "changing object's classes" via a user interface? 【发布时间】:2013-05-28 12:29:57 【问题描述】:

我目前正在开发一个 C# WPF 应用程序,它允许您创建一个图形(即通过边连接的一堆顶点),然后使用这个图形作为一个模式在一堆其他(更大) 图(“主机”图)。每个图形元素至少有一个类型和一个标签。

模式图元素(边和顶点)可以有不同的“限制类型”。

例如,一个顶点可以有“这个顶点”标签必须是“顶点 A”或“这个顶点”类型必须在集合 Type A, Type B, Type H 中的限制。

对于边,限制类型有点困难。一条边可以被限制为“简单”边或“路径”边。模式图中两个顶点之间的路径边可以被认为是一个占位符,它允许您在宿主图中的两个顶点之间找到多个边(和顶点)。相比之下,简单边允许您在宿主图中仅找到一条边(而不是其他顶点)。

如果一条边有路径限制(而不是正常的边限制),它还有一些附加属性,例如最小路径长度或路径上允许的顶点类型。

在这个UML类图中可以看到类型限制结构:

~~~

现在从 UI 的角度来看:用户应该能够配置边缘是否有路径限制。如果有,用于附加设置的必要附加控件(文本框、列表框等)应自动出现。所有控件的更改都应自动反映在数据结构中。

以下是更改选定边的设置时用户界面的行为: (其实在右侧应该还有一个滚动条,可以让你向下滚动并配置路径上允许的边缘类型。也暂时忽略顶点和边缘重叠设置。)

~~~

最后,我的问题归结为:

如何在保持 WPF 的数据绑定优雅的同时实现这样的动态对象类更改? (对于动态对象类更改,我的意思是通过单击“将此边缘视为路径”复选框,所选边缘将获得不同的限制类型。)

我真的必须创建一个老式事件侦听器,当更改“将此边缘视为路径”复选框的值时触发并使用它“手动”更新其他侧边栏控件的可见性?

如果我以某种方式更改了我的限制类结构,会有什么帮助吗?

【问题讨论】:

en.wikipedia.org/wiki/State_pattern 您可能想看看my Nodes Editor Sample。您在此处讨论的所有内容都已在示例中以非常优雅和干净的模式 (MVVM) 实现。 @HighCore:谢谢你的好例子。但是,该示例并未根据我的情况处理“更改”对象的类。 @HaukeP “更改对象的类”是什么意思?那没有意义。你甚至不能用标准的 C# 对象来做到这一点。如果您的意思是“更改对象的属性”,那么您可以通过将相关代码添加到 ViewModel 来完美地做到这一点。此外,如果您需要将某些对象“交换”为其他对象,也可以在 ViewModel 级别执行。 @HaukeP。你的虚拟机应该保持不变,你可以改变它的模型within,比如虚拟机有一个public Model Model属性,然后你把它从ModelA更改为ModelB。然后 UI 的相关部分基于ContentPresenters,它为每种模型类型呈现适当的DataTemplate。就像我在节点编辑器中所做的那样,在左侧面板中为节点和连接器显示不同的编辑器。看看吧。 【参考方案1】:

您所追求的设计模式可能是MVVM。看起来您当前正在将您的 UI 直接附加到您的模型,而两者之间没有视图模型。有时你可以侥幸逃脱,有时则不行。

可以编写一个应用于IsChecked 属性绑定的值转换器,它将布尔值转换为限制类的正确实例并返回。随着复选框状态的变化,会创建一个不同的实例并将其分配到您的模型中。这将通过数据模板的选择反映在 UI 中。

它可能比这更复杂,例如,如果您想在状态更改之间保留值。这就是您想要在 UI 和模型之间引入视图模型的地方。视图模型紧密地反映了 UI 并具有相应的布尔属性,当它发生变化时,您可以相应地更改模型。

无论哪种方式,都应该没有理由挂钩控件上的事件:尝试在视图模型中处理这一切。

【讨论】:

这听起来很有帮助。还有一些问题:我是否应该只创建一个包含所有理论上可能的属性和负责控件可见性的附加属性的 ViewModel?或者我应该创建两个 ViewModel,一个用于顶点,一个用于边?甚至三个,一个用于顶点,一个用于简单边,一个用于普通边? 您可能需要一个整体视图模型来表示 UI 的公共部分的边缘。这将包含表示更改复选框时出现的不同 UI 的两个视图模型之一。创建两个 DataTemplates 来显示其中的每一个。【参考方案2】:

好的,我不会真正谈论设计模式,所以我不会用这些术语来回答。虽然我确实会说 WPF,但设计模式通常是我已经做过但不知道它叫什么的东西。

WPF 并不真正关心什么是什么类型。如果您在某种ContentControl 中使用DataTemplate 显示它,您可以为每种类型的对象设置一个,该对象实际上可能需要特殊处理,WPF 会为您处理。 DataTemplateSelector 对于为给定类型选择 UI 部分的更复杂逻辑可能是一个好主意,根据我的经验,这往往发生在许多所谓的简单场景中,但事实证明并非如此。缺点是DataTemplateSelector 必须了解您的所有数据类型和模板才能在它们之间进行选择。

这仅适用于侧面板,实际图形的渲染可能需要以更全面的方式完成,我想说,这是一个完全不同的问题。

【讨论】:

非常感谢第一个回答。经过一番搜索,我发现this answer 似乎与您的建议相似。这两个答案都有助于控制必要控件的“可见性”。但是,您将如何实现必须实际更改限制的 type(即类)的 CheckBox“将此边缘视为路径”? 更准确地说:您会将“将此边缘视为路径”复选框的 IsChecked 属性绑定到哪种绑定源?老实说,我不太喜欢 WPF 绑定 - 转换器在这里有帮助吗?或者那将是一个完全错误的选择? 这有点尴尬,但你可以将它绑定到边缘的父对象的属性,它在其设置器中触发适当的代码以将边缘换出路径。另一种方法涉及处理视图中复选框上的事件,并使用这些事件来调用执行交换的视图模型方法。你的喜好,真的。

以上是关于在 C# 中应该使用哪种设计模式和 WPF 来通过用户界面动态“更改对象的类”?的主要内容,如果未能解决你的问题,请参考以下文章

有经验的 DBA 应该使用哪种 C# 操作 ms-access 的方法?

我应该在通知系统中使用哪种设计模式?

C# WPF 怎样使用 Graphics呢?

在 watch app swift 中应该使用哪种设计模式?

使用 Xamarin.iOS (C#) 在 iOS 中进行后台处理:我应该使用哪种方法?

我应该在同一台机器上运行的 Firefox 扩展和 C# 代码之间使用哪种 IPC 方法?