Wpf ViewModel/View的属性应存储在哪里?

Wpf ViewModel/View的属性应存储在哪里?,wpf,architecture,mvvm,Wpf,Architecture,Mvvm,简单的例子: 我有一个具有分层项目结构的VM,我设计了包含TreeView的视图。每个树节点对应于与模型中的节点相关的数据项。 GUI必须尊重用户偏好和设置,并存储不同的参数,包括是否扩展每个特定节点。只要每个节点都与某些数据相关,我们就需要将视图设置与模型联系起来。我认为在代表模型的类中定义属性IsExpanded是不合适的 那么,我应该在哪里存储设置,以便它们与数据保持一致 我认为在代表模型的类中定义属性IsExpaned是不合适的 为什么??当模型链接到两个不同的树视图时,是否会出现这种情

简单的例子: 我有一个具有分层项目结构的VM,我设计了包含TreeView的视图。每个树节点对应于与模型中的节点相关的数据项。 GUI必须尊重用户偏好和设置,并存储不同的参数,包括是否扩展每个特定节点。只要每个节点都与某些数据相关,我们就需要将视图设置与模型联系起来。我认为在代表模型的类中定义属性IsExpanded是不合适的

那么,我应该在哪里存储设置,以便它们与数据保持一致

我认为在代表模型的类中定义属性IsExpaned是不合适的

为什么??当模型链接到两个不同的树视图时,是否会出现这种情况

如果是这种情况,您当然可以创建一个适配器视图模型来包装您的模型并添加属性。只是要小心,因为这种技术很容易导致内存泄漏

否则,将其放在模型中是完全可以接受的,特别是如果该模型专门用于服务于该视图。与所有事情一样,从最简单、最务实的方法开始

我认为在代表模型的类中定义属性IsExpaned是不合适的

为什么??当模型链接到两个不同的树视图时,是否会出现这种情况

如果是这种情况,您当然可以创建一个适配器视图模型来包装您的模型并添加属性。只是要小心,因为这种技术很容易导致内存泄漏


否则,将其放在模型中是完全可以接受的,特别是如果该模型专门用于服务于该视图。与所有事情一样,从最简单、最务实的方法开始

我认为这是MVVM中常见的难题。我可以从两个方面来看

接近A 在视图、模型和视图模型的分离中,您描述的内容驻留在模型中。例如,您编写了需要存储的代码。这并不意味着它与其他模型数据是模型的同一部分

考虑以下分离:

FolderModel—文件夹内容或属性的模型。 TreeNodeMel-用户在浏览树状视图时选择的模型。 像这样进行分离可能不是那么容易,但关键是MVVM并不是要强迫您将所有内容都放在同一个位置,我不认为MVVM会阻止您为用户交互保留模型,就像您为数据内容保留模型一样

数据可能来自数据库、Web服务、命名管道、磁盘上的文件,甚至信鸽:这根本不重要

为什么数据模型的数据不能来自用户的交互

方法B 不过,我处理这些难题的通常方法是,像iExpanded这样的属性基本上不需要在会话之间存储。这样,ViewModel中具有默认值而不是存储值的属性就足够了:

如果需要存储,则不需要将其存储在模型中。ViewModel用于表示模型数据的逻辑。如果ViewModel希望保存其逻辑状态,则不必将其存储在模型中

总结
在我看来,在不破坏MVVM模式的情况下,您可以通过以下任何一种方式存储它。

我认为这是MVVM中常见的难题。我可以从两个方面来看

接近A 在视图、模型和视图模型的分离中,您描述的内容驻留在模型中。例如,您编写了需要存储的代码。这并不意味着它与其他模型数据是模型的同一部分

考虑以下分离:

FolderModel—文件夹内容或属性的模型。 TreeNodeMel-用户在浏览树状视图时选择的模型。 像这样进行分离可能不是那么容易,但关键是MVVM并不是要强迫您将所有内容都放在同一个位置,我不认为MVVM会阻止您为用户交互保留模型,就像您为数据内容保留模型一样

数据可能来自数据库、Web服务、命名管道、磁盘上的文件,甚至信鸽:这根本不重要

为什么数据模型的数据不能来自用户的交互

方法B 不过,我处理这些难题的通常方法是,像iExpanded这样的属性基本上不需要在会话之间存储。这样,ViewModel中具有默认值而不是存储值的属性就足够了:

如果需要存储,则不需要将其存储在模型中。ViewModel用于表示模型数据的逻辑。如果ViewModel希望保存其逻辑状态,则不必将其存储在模型中

总结 在我看来
视图,您可以通过以下任何一种方式存储它,而无需破坏MVVM模式。

我也遇到过类似的情况,下面是我为解决它所做的工作

每个ViewModel节点都有一个IsExpanded属性,该属性绑定到TreeViewItem的IsExpanded属性。我不想通过将树存储在IsExpanded属性的模型中来保持树的状态,这与视觉状态有关,对吗?。因此,我让VM树结构生成一个字典,根据树中节点状态生成的字符串键存储每个节点的扩展状态:

字典树扩展状态


树中的每个节点都有一个ID,因此在我的例子中,键类似于/1/3/7,但任何唯一的都可以。然后在应用程序关闭时将该字典序列化为一个文件,实际上它是一个SerializableDictionary。然后,在应用程序重新启动时,它被反序列化,并用于在层次结构重新加载后设置扩展状态。这意味着树的状态与用户离开时的状态完全相同,但模型中没有存储任何内容。

我也处于类似的情况,下面是我为解决该问题所做的工作

每个ViewModel节点都有一个IsExpanded属性,该属性绑定到TreeViewItem的IsExpanded属性。我不想通过将树存储在IsExpanded属性的模型中来保持树的状态,这与视觉状态有关,对吗?。因此,我让VM树结构生成一个字典,根据树中节点状态生成的字符串键存储每个节点的扩展状态:

字典树扩展状态


树中的每个节点都有一个ID,因此在我的例子中,键类似于/1/3/7,但任何唯一的都可以。然后在应用程序关闭时将该字典序列化为一个文件,实际上它是一个SerializableDictionary。然后,在应用程序重新启动时,它被反序列化,并用于在层次结构重新加载后设置扩展状态。这意味着树的状态与用户留下的状态完全相同,但模型中没有存储任何内容。

如果与ViewModelItem不相关,为什么需要在ViewModel模式中扩展IsExpanded?我认为正确执行此操作的唯一方法是将ViewModelItem包装在第二个ViewModelItem中。请解释iExpanded的必要性。想象一下有问题的主题列表。主题是0级节点。问题-第1级。我希望GUI记住哪些主题被展开和折叠。这些主题可以在其他地方删除,因此我需要知道哪个节点是旧的,它的状态是持久的。而不是它是ViewModelItem的一部分,每个ViewModelItem,无论它是主题还是问题。如果IsExpanded上的IsExpanded比它应该是一个成员时更为强大,则只需使用第二个ViewModelItem封装ViewModelItem,其中包含第一个ViewModelItem和IsExpanded状态的布尔值。如果IsExpanded与ViewModelItem无关,为什么需要在ViewModel模式中进行IsExpanded?我认为正确执行此操作的唯一方法是将ViewModelItem包装在第二个ViewModelItem中。请解释iExpanded的必要性。想象一下有问题的主题列表。主题是0级节点。问题-第1级。我希望GUI记住哪些主题被展开和折叠。这些主题可以在其他地方删除,因此我需要知道哪个节点是旧的,它的状态是持久的。而不是它是ViewModelItem的一部分,每个ViewModelItem,无论它是主题还是问题。If在iExpanded上比它应该是一个成员时更繁荣,否则只需使用第二个ViewModelItem封装ViewModelItem,其中包含第一个ViewModelItem和iExpanded状态的布尔值。我认为它打破了这种范式。包含IsExpanding隐式连接模型和视图。我考虑另一种方法。每个数据都应该提供Id。ViewModel可以将项目的Id与其设置配对。尽管如此,我还是同意你的看法:与所有事情一样,从最简单、最实用的方法开始我应该读一读MVVM:我想包装器模型是最好的,但为什么它被称为MVVM而不是MWVWM,只是开玩笑而已。顺便问一下,我不是这里的主要问题,但你能稍微解释一下内存泄漏吗?@Silvermind。假设PersonVM有一个全名属性。为了生成此属性的属性更改通知,PersonVM需要侦听PersonModel上的事件。如果PersonVM在不删除事件侦听器的情况下超出范围,它将被PersonModel事件泄漏。XAML数据绑定使用弱事件,因此无需担心。当你显式地添加一个事件处理程序时,你需要小心。我认为这打破了这个范例。包含IsExpanding隐式连接模型和视图。我考虑另一种方法。每个数据都应该提供Id。ViewModel可以将项目的Id与其设置配对。尽管如此,我还是同意你的看法:与所有事情一样,从最简单、最实用的方法开始,我应该阅读MVVM:包装器模型是我最好的选择
我猜,但为什么它被称为MVVM而不是MWVWM,只是开个玩笑。顺便说一下,我不是这里的主要问题提问者,但你能稍微解释一下内存泄漏吗?@Silvermind。假设PersonVM有一个全名属性。为了生成此属性的属性更改通知,PersonVM需要侦听PersonModel上的事件。如果PersonVM在不删除事件侦听器的情况下超出范围,它将被PersonModel事件泄漏。XAML数据绑定使用弱事件,因此无需担心。当您显式添加事件处理程序时,您需要小心。我认为MVVM在某些情况下被推得太远了,因为很多人在这里询问有关MVVM的问题,看起来他们对此束手无策。我很有可能会错,但我认为MVVM对于大型项目来说非常方便,但是人们应该很少关注使用几乎完全相同的ViewModelItems来反映他们的数据库值。我认为人们往往会忘记UI和ViewModelItems之间存在双向关系。不仅仅是从ViewModelItems到UI的一种方式。但我可能大错特错了,这不是忘记。我只是从用户体验开始设计,并使VM适应视图,但仍然试图减少VM与视图的逻辑连接。它为GUI设计者提供了合理的自由度来更改某些UI,而不仅仅是外观和感觉部分,而不会干扰程序员。但它的编码成本更高,因为VM需要过度细化,以防为新的可能交互场景提供功能。当然,在简单的UI中,从头开始重写VM的代码比准备一切都要容易=与某些模式(例如依赖注入)不同,MVVM不会随着应用程序规模的增长而变得更有用。如果你正在使用的MVVM的特殊风格不能使你的小应用程序更容易,也不能使你的大应用程序更容易。我认为MVVM在某些情况下被推得太远了,因为很多人在这里询问有关MVVM的问题,看起来他们对此束手无策。我很有可能会错,但我认为MVVM对于大型项目来说非常方便,但是人们应该很少关注使用几乎完全相同的ViewModelItems来反映他们的数据库值。我认为人们往往会忘记UI和ViewModelItems之间存在双向关系。不仅仅是从ViewModelItems到UI的一种方式。但我可能大错特错了,这不是忘记。我只是从用户体验开始设计,并使VM适应视图,但仍然试图减少VM与视图的逻辑连接。它为GUI设计者提供了合理的自由度来更改某些UI,而不仅仅是外观和感觉部分,而不会干扰程序员。但它的编码成本更高,因为VM需要过度细化,以防为新的可能交互场景提供功能。当然,在简单的UI中,从头开始重写VM的代码比准备一切都要容易=与某些模式(例如依赖注入)不同,MVVM不会随着应用程序规模的增长而变得更有用。如果您正在使用的MVVM的特殊风格不能使您的小应用程序更容易,也不能使您的大应用程序更容易。这就像我的方法B:这就像我的方法B: