WPF转换器在MVVM模式中如何使用?

19
假设我有一个视图与ViewModel A绑定,它有一个可观察的集合Customers。MVVM模式的一个优点是,我也可以将视图绑定到填充不同数据的ViewModel B。但是,如果在我的View转换器Converters中显示客户,例如我有一个"ContractToCustomerConverter",它接受合同并返回要显示的适当客户,则存在的问题是转换器存在于MVVM模式之外,因此不知道我的ViewModel是否有另一个客户来源。有没有一种方法让视图将ViewModel传递到转换器中,以便它参与MVVM模式提供的解耦过程?还是有一种方法让我在ViewModel中包含转换器,以便转换器使用ViewModel可用的当前依赖项?或者转换器只是被吹嘘的代码后台,因此在MVVM模式中不使用转换器,因此,如果您正在使用MVVM,则只需创建自己的“转换器”(ViewModel类上的方法),这些方法返回像Image对象、Visibility对象、FlowDocuments等对象,以在视图上使用,而不使用转换器吗?

我在MVVM模板工具包下载中看到WPF演示应用程序中使用转换器后遇到了这些问题,请解答。解压后查看"Messenger Sample"。

6个回答

13

在MVVM中,我通常不使用转换器,除了纯UI任务(比如BooleanToVisibilityConverter)。在我的看法中,你应该在ContractViewModel中声明一个类型为CustomerViewModel的Customer属性,而不是使用ContractToCustomerConverter。

Note: - IMHO 表示“In My Humble Opinion”,可译为“依我拙见”。

11
这次交流中,有一条评论支持Kent的立场,认为完全不使用转换器很有趣:

ViewModel基本上是一个强化版的值转换器。它将“原始”数据转换为适合呈现的数据,反之亦然。如果你发现自己将元素属性绑定到ViewModel的属性,并且你正在使用值转换器,请停止!为什么不在ViewModel上创建一个公开“格式化”数据的属性,然后完全放弃值转换器呢?

而在这个讨论中

我唯一能看到在MVVM架构中使用值转换器的地方是跨元素绑定。 如果我将面板的可见性绑定到复选框的IsChecked,则需要使用BooleanToVisibilityConverter。


9
转换器在MVVM中应该很少使用。事实上,我尽量不使用它们。VM应该做所有视图需要完成其工作的事情。如果视图基于合同需要一个客户,那么VM应该有一个客户属性,在合同更改时由VM逻辑更新。
“这种MVVM模式的优点是我也可以将View绑定到ViewModel B,它会填充不同的数据。”
我对这个说法持有异议。根据我的经验,视图不会在不同的VM类型之间共享,这也不是MVVM的目标。

3
我理解你的意思是:不同的虚拟机应该不能共享视图,但一个 ViewModel 应该能够被不同的视图共享,这就是 MVVM 可测试性的优势,对吗?你可以连接一个模拟视图和模拟模型到 ViewModel 上,以确保它从模拟模型接收的所有数据组合都能产生正确的属性值,并向视图公开。你同意吗? - Edward Tanguay

5
对于那些在视图中没有“非平凡转换器”的人,你如何处理以下情况?
假设我有一个气候传感器的模型,它表示给定位置各种仪器(气压计,湿度计,温度计等)的时间序列读数。
假设我的视图模型公开了来自我的模型的传感器的可观察集合。
我有一个包含WPF工具包DataGrid的视图,它绑定到视图模型,其中ItemsSource属性设置为传感器的可观察集合。 如何为给定传感器的每个仪器表示其视图?通过显示一个小图形(想象Edward Tufte sparkline 在这里),该图形是通过使用转换器(TimeSeriesToSparklineConverter)将时间序列转换为图像源生成的。
这就是我对MVVM的理解:模型向视图模型公开数据。 视图模型向视图公开行为、模型数据和状态。 视图负责以视觉方式表示模型数据并提供与视图模型状态一致的行为界面。
因此,我认为火花线图像不应该放在模型中(模型是数据,而不是特定的可视化表示)。我也不相信火花线图像应该放在视图模型中(如果我的视图希望以不同方式表示数据,例如仅显示系列的最小值、最大值、平均值、标准偏差等作为网格行,那么该怎么办?)。因此,我认为视图应该处理将数据转换为所需表示的工作。
因此,如果我想在命令行界面而不是WPF GUI中公开某个视图模型的行为、模型数据和给定状态,我不希望我的模型或视图模型包含图像。这样做是否有误?我们需要一个SensorCollectionGUIViewModel和一个SensorCollectionCommandLineViewModel吗?对我来说,这似乎是错误的:我认为视图模型是视图的抽象表示,不是具体的并且与这些名称暗示的特定技术相关联。
这就是我的MVVM不断演变的理解。因此,对于那些不建议使用转换器的人,你在这里做什么?

3
我理解你所描述的问题是这样的:通过一个值转换器,你可以创建一个ClimateSensorToSparklineGraphConverter,它将接收一个气候传感器集合并输出一张图片。对于像创建位图图片这样的任务,你不会使用DataTemplate和包含ViewModel的ViewModel集合来完成,而是需要C#代码来创建图片。当你在转换器中访问一个用户集合以确定当前用户所允许看到的内容时,就会出现问题。这会破坏MVVM模式,因为ViewModel应该注入用户信息。 - Edward Tanguay

1

我已经使用Stackoverflow多年了,这是我第一次发布的答案。

我认为转换器属于MVVM中的View,考虑以下情况:

应用程序由3个团队开发,WebAPI团队、Web客户端团队和UI团队。由于UI经常更改,因此Web客户端团队(从WebAPI接收数据并将其放入ViewModel中)不能总是修改ViewModel以满足UI需求。当UI团队具有不同版本的设计时,这变得不可能。因此,UI团队必须有自己的方法来呈现数据,解决方案是转换器。

希望这能帮助某些人。


0

我来谈一下我的看法。

在合适的情况下,我会使用转换器。

解释: 有些情况下,您需要在UI中以多种方式表示模型中的一个值。我通过1种类型公开此值。另一种类型通过转换器处理。如果您要通过VM的2个属性公开1个值,则需要手动处理更新通知。

例如,我有一个带有2个int的模型:TotalCountDoneCount。现在,我希望这两个值都显示在TextBlocks中,并且我还想显示完成百分比。

我使用DivisionConverter多转换器来解决这个问题,该转换器接受前面提到的2个int。

如果我在VM中有特殊的PercentDone,则需要在每次更新DoneCount时更新此属性。


你基本上是通过声明转换器并在绑定抛出属性更改时触发它来更新PercentDone属性,只不过它由一个带有函数的类表示,而不是一个属性...所以我认为这不是一个真正好的用例。我认为纯UI-to-UI的东西需要转换器。 - Joris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接