在ViewModel中的类型为Visibility的属性

5
在WPF应用程序中,当您需要从ViewModel触发View元素的可见性时,基本上有两种方法:

方法1:使用bool

class ViewModel
{
    public bool IsMyImageVisible { get; set; }
}

查看:

<Window.Resources>
    <BooleanToVisibilityConverter x:Key="booleanToVisibility" />
</Window.Resources>

<Image Visibility="{Binding IsMyImageVisible, Converter={StaticResource booleanToVisibility}}" />

方法二:使用Visibility


class ViewModel
{
    public Visibility MyImageVisibility { get; set; }
}

视图:

<Image Visibility="{Binding MyImageVisibility}" />

问题

  1. "方法2"仍然符合 MVVM 标准吗?

  2. 何时应使用 "方法1"?

编辑:将问题更改为不那么基于观点。


2
这个问题过于基于_个人意见_,不适合在这里讨论。我会尝试在 Code Review 网站上提问。无论如何在我看来:1)因为它严格来说更符合 MVVM。2)是的,它打破了规则,但我认为这不是一个问题,我们必须有一点灵活性,它们只是指南,而不是规则。3)不。 - Adriano Repetti
1
@Adriano:为什么会破坏关注点分离?两种方法最终都做了完全相同的事情。您是否认为第一种方法也破坏了关注点分离? - Benoit Blanchon
关于1)因为您将UI中的可见性概念与模型中的“对用户可见”概念绑定在一起,但它们可能并不相同(例如图像可能被静音、显示在较小的列表中、在其他地方可访问等)。您的UI经常会更改,而您的模型可能多年都不会更改。这就是为什么我认为您可以这样做或不这样做(我不认为您必须总是搜索抽象),在简单的情况下公开一些细节可能是可以的。 - Adriano Repetti
1
最后一点是,您公开了一个特定于WPF的枚举。如果您计划将UI移植到Web等其他平台,则会破坏它。如果您不认为自己会将应用程序移植到另一个平台,则可以放心使用它(无论如何,坦率地说...您可能需要重写模型中的更多属性)。 - Adriano Repetti
3
我已经写了太多的评论...我想指出另一件事。我们正在讨论对于该属性,使用枚举还是布尔类型更好。我认为我们应该考虑真正的问题:该属性的名称(以及它的含义)。如果你将其命名为“可见”,那么你就将一个UI细节(可见性)放到了模型中。模型应该有与领域相关的属性(是否被删除?是否无法访问?是否被用户隐藏?)。如果你使用领域词汇和概念来描述模型,则甚至不会想到可见性。 - Adriano Repetti
1
很高兴看到你发表了最后的评论。那正是我在问“您是否认为第一种方法也破坏了关注点分离”时所想的。我真的很担心通过在第一种方法中公开 IsVisible,可能会走错整个道路。非常感谢您所有的评论。 - Benoit Blanchon
3个回答

11
第二个选项将您的ViewModel绑定到特定技术(WPF)。其他技术如某些Web框架将具有不同的Visibility枚举。您还可能需要向模型项目添加WPF引用,这对于某些情况可能不是一个好主意(因为该项目的所有使用者现在都必须包括该引用)。
如果您的ViewModel不需要跨框架兼容性,则可以使用第二个选项而无需任何其他缺点。

1
“Visibility”确实与WPF相关联。但是很可能您已经在ViewModel中使用了WPF的东西,比如“ICommand”或“Dispatcher”。 - Benoit Blanchon
2
错误。ICommand或Dispatcher是Windows命名空间的类,与WPF无关。 - Ignacio Soler Garcia
好的,“Visibility” 在 “PresentationCore.dll” 中定义,而 “Dispatcher” 在 “WindowsBase.dll” 中定义。我从来没有看到任何项目仅引用这两个 dll 中的一个。 - Benoit Blanchon
1
@BenoitBlanchon 这不仅仅是它们被定义的位置问题。ICommand 可以在任何地方使用(现在已经移动到 System.dll),但是 _domain_。ICommand 受限于 WPF 领域,而 Visibility 则不是。 - Adriano Repetti

4
考虑以下情景:
下面的接口设计决策已经做出:不要隐藏图片,而是将其变为10%的透明度,就像幽灵一样。现在,如果你选择了第二个选项,你必须改变你的视图模型代码,因为接口设计发生了变化。然而,如果你选择了第一个选项,那么你只需要修改接口(也许添加一个BoolToOpacity转换器)来反映这种变化。第一个选项更符合MVVM哲学。如果你有不同的人负责接口设计和视图模型代码,则他们不必干涉彼此的工作。

2
这种灵活性真的值得在 XAML 的清晰度上做出牺牲吗?另外,根据我的经验,View 中的更改通常需要更改 ViewModel;因此我认为这是可以接受的缺点。 - Benoit Blanchon
2
这是一个偏好问题。我个人认为XAML的清晰度并没有降低。(顺便说一下,您可以尝试将转换器放在共享资源中,以使XAML更加简洁)。但如果您认为这是可以接受的缺点,那么也许MVVM模式并不适合您。请考虑:许多MVVM开箱即用框架中都存在BoolToVisibility转换器,这表明第一种方法更适合MVVM。 - Boluc Papuccuoglu
然而,BooleanToVisibilityConverter非常有限:它永远不会返回Visibility.Hidden。如果您需要这个功能,您需要编写自己的转换器或添加触发器。所有这些都可以通过在ViewModel中使用Visibility来避免。 - Benoit Blanchon
2
+1 我认为这是避免模型可见性的最佳点。更长或更短的代码(最佳实践往往不会更短......)并不比“意图”更重要(如果您不关心这一点,就不要使用 MVVM)。@BenoitBlanchon 假设您正在显示图像列表,标志可能只是“已删除”(布尔值)。第一个版本中,您不显示已删除的图像,第二个版本中,您添加了一个标志来显示它们(静音)。在这种情况下,您还需要更改模型的名称、类型和行为... - Adriano Repetti

2

我认为这主要取决于你对该属性的使用。在应用程序的某些视图中,您可能只需显示或隐藏图像,但是在创建另一个视图时,也许您想基于图像的可用性显示全新的模板。

在这种情况下,Visibility属性会变得有点奇怪。


在大多数情况下,您只想使控件可见或不可见...但是您是正确的,bool打开了更多的可能性。 - Benoit Blanchon

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