WPF: MVVM拒绝使用转换器,但我需要漂亮的枚举值

8
在模型层,我定义了一个枚举:
public enum MemberStatus
{
    ActiveMember = 0,
    InactiveMember = 1, 
    Associate = 2,
    BoardMember = 3,
    Alumni = 4
}

在我的看法中,我有一个下拉框,其中填充了这些枚举值:

<UserControl.Resources>

  <ObjectDataProvider 
      x:Key="memberStatusesDataProvider" 
      ObjectType="{x:Type system:Enum}" 
      MethodName="GetValues">
    <ObjectDataProvider.MethodParameters>
      <x:Type TypeName="model:MemberStatus" />
    </ObjectDataProvider.MethodParameters>
  </ObjectDataProvider>

</UserControl.Resources>
...
<ComboBox 
    ItemsSource="{Binding Source={StaticResource memberStatusesDataProvider}}" 
    SelectedItem="{Binding Path=Status}" />
...

这将导致得到一个带有下拉框的选项,这些选项与枚举中定义的值完全相同。虽然这是我的最初目标,但我希望为用户提供更好的展示效果,例如:
  • 下拉框选项:
    • 活跃成员
    • 非活跃成员
    • 联合会员
    • 董事会成员
    • 校友
此外,如果应用程序的语言发生变化,我需要在该语言中使用枚举值。为了解决这个问题,我想到的第一件事是创建一个 MemberStatus 枚举值转换器。我在这个话题上找到了这篇优秀的文章:http://www.codeproject.com/KB/WPF/FriendlyEnums.aspx 但MVVM模式认为几乎不需要创建它们 - 我同意这个说法。然而,在这个例子中,这个说法并不适用于我。
应该如何处理?谢谢。

10
你在哪里看到 MVVM 不需要转换器的要求? - nlawalker
我会考虑设计的变更而不是调整转换器。一个好的解决方案是创建两个类 - MemberStatusAvailableMemberStatusAvailableMemberStatus 将扩展 List<MemberStatus> 并在构造函数中初始化自身。可以实现为单例形式。 - Greg Sansom
1
@nlawalker:我在Josh Smith的博客和其他几个来源中找到了它。实际上,在MVVM讨论中经常提到它。就个人而言,我同意这样的说法,因为视图模型的目的是将模型转换为视图 - 它可能会在途中为视图做好准备。 - Boris
1
我不同意关于在MVVM中不使用转换器的说法。WPF广泛使用内置转换器,我经常在我的MVVM编程中使用它们。我没有遇到任何问题。我对像“但是MVVM模式说不需要创建它们”这样的笼统陈述持保留态度。那只是一个人的观点,很多人都不同意。 - David Veeneman
4个回答

4
MVVM架构使值转换器过时的观点似乎来自Josh Smith,在他的博客文章“MVVM的哲学”中说道:(链接)
“…… ViewModel类本质上是一个超级值转换器,从而使IValueConverter接口对于大多数绑定无关紧要。”
我理解这意味着(我同意他的观点),视图模型负责从模型世界到视图的所有转换,因此使转换器变得过时。
将枚举(这是一种非常数据中心的数据类型)在模型中暴露给UI绝对是不好的,因为你向用户显示了不理想的信息。
请在您的视图模型中放置从枚举到UI字符串的映射。

1
如果代码更简单、更易读,则采用值转换器方法!但在大多数情况下,“超级转换器”仍然是最好的选择。 - David Hall
关键在于“大多数”绑定。可能有一两种情况更好地使用转换器。 - kevindaub
@David:感谢您的回答。我在这里解释了我的情况,即http://stackoverflow.com/questions/5706500/wpf-are-data-templates-obsolete-in-mvvm中的映射问题。因此,基本上,如果我依赖数据模板并使用模型类作为它们的数据类型,则无法使用视图模型中定义的映射属性,因为那不是模板的数据上下文。如果我应该使用视图模型属性,则链接问题提供的解决方案就没有意义。我在两个想法之间:)请给予建议,谢谢。 - Boris
@Boris 我看了那个问题,并同意评论者提到为单个VM拥有多个数据模板的做法。虽然我已经有一段时间没有认真地使用WPF了,但我还记得曾经这样做过。我可以说的是,如果你(就像我想的那样)直接绑定到你的模型,那么这将破坏MVVM的好处。此外,你应该理想地每个视图都有一个VM,它处理各种模型元素的转换。我偶尔会有一个VM为视图中的用户控件提供服务,但那只是当用户控件本质上是一个隔离的组件时。很抱歉我现在无法提供具体的代码。 - David Hall
@David 那正是我所想的!从MVVM角度来看,直接绑定模型毫无意义。此外,我同意每个视图拥有一个视图模型的概念,但也同意每个独立组件拥有一个视图模型的概念。那么,为了结束这个讨论,最后一个问题:您是否支持在模型中有很多属性,并将它们映射到视图模型中,以避免直接绑定?我之所以问是因为在很多情况下,映射是直接的,一对一的,这使得它看起来是多余的(模式的目的是减少冗余)。 - Boris
@Boris 这可能是该模式中最常见的抱怨,我自己也没有完全解决。我认为这真的取决于情况。如果您的模型很丰富,具有很多逻辑,那么隔离本身就是很好的,而且它将所有视图关注点排除在模型之外;如果模型只是一个DTO,则该模式开始变得过度。一个可帮助解决这个问题的工具是模型投影 AutoMapper。 - David Hall

3

转换器不仅用于转换enum,还比一次性的viewmodel更可重复使用。

  • 我可能想将bool转换为Brush,并且可以在视图中指定所有参数。

  • 或者我想通过数据绑定将string转换为DateTime,然后再转换回来。也许我想把所有东西都转换为大写字母。然后就有了我的最爱BoolToVisibilityConverter

我不想在我的VM中到处放置明确的直接间接代码,只是为了让一些少数群体满意。我认为他们忘记的一件事是,从Expression Blend很容易访问转换器

转换器是WPF的重要组成部分,补充了viewviewmodel之间的绑定。

我认为没有理由不能用它们来处理enum


2

MVVM并没有明确规定WPF的哪些部分是允许使用的,哪些是不允许使用的。如果转换器可以轻松地实现您的目标,那么就可以使用它们。我甚至建议更进一步,创建一个MarkupExtension来提供枚举值及其字符串等效值。您可以在每个枚举值上存储字符串,以DescriptionAttribute的形式。


所以从你的角度来看,最终是转换器,对吗?如果我理解正确,我会在那里进行枚举值到字符串的映射。还有一件事,我认为 DescriptionAttribute 不是一个好主意,因为这样的话值就是硬编码的。如果我在应用程序中支持不同的语言,那么 DescriptionAttribute 就必须动态更改。感谢您提供的答案,干杯。 - Boris
你可以提供一个语言感知的源集合。我在我们公司产品中使用的标记扩展支持从资源文件中读取描述。 - user7116
@user7116 这太可怕和懒惰了。避免避免避免。resx文件已经实现了这一点。现有的本地化工具无法替代属性中的字符串数组。 - Gusdor

1

我不同意MVVM会使ValueConverters过时。在许多情况下,实现ValueConverter比在ViewModel类中实现转换更有意义。

您可能会对WPF应用程序框架(WAF)BookLibrary示例应用程序感兴趣。它展示了如何在MVVM应用程序中本地化枚举。请查看BookLibrary.Presentation / Converters / LanguageToStringConverter类。


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