MVVM 和有状态命令 - 好还是坏主意?

发布于 2024-08-03 05:36:33 字数 1158 浏览 5 评论 0原文

我想我在这里发帖是希望具有 MVVM 专业知识的人能够就以下是否是个好主意提供意见:

我正在使用 Sacha Barber 的 Cinch MVVM 框架,其中包括 Marlon Grech 的 SimpleCommand 类。

该类没有其他替代品所具有的一件事,那就是 Text 属性,该属性通常可用于将 UI 元素绑定到命令操作的“标题”。因此,我一直在为此类编写一个扩展,它公开了一个 Text 属性。

现在,我遇到的是一个用例,我使用命令来切换与设备的连接。我可以通过多种不同的方式来实现这一点(不是总是存在 - 它是软件!)。一种方法是从我的 ViewModel 公开多个命令对象 - 一个用于“断开连接”,一个用于“连接”;让视图模型公开一个指示连接状态 (IsConnected) 的属性,并让视图有条件地绑定到 Connect 命令或 Disconnect 命令。不过,我对这个选项的反应是……恶心!

我最初开始考虑的不仅是提供一个 Text 属性,而且还让命令对象实现 INotifyPropertyChanged,以便视图模型可以根据系统状态将文本属性动态更改为“连接”或“断开连接”。这样做,我可以避免使用多个命令,而只公开一个“ToggleConnection”命令对象。

不过,从这条道路开始,我突然想到这种模式可能还有其他变体,其中 UI 需要根据命令状态进行更改。例如,除了根据连接状态更改命令文本之外,您可能还需要根据连接状态更改图标。因此,我开始编写一个“Stateful”类,它实现 INotifyPropertyChanged,并公开两个属性 -“Text”和“State”。我已经使该类变得通用,以便用户可以定义状态的类型(我通常不喜欢在可以避免的情况下使用“对象”)。

我的问题是......你认为这是一个好主意还是坏主意?可能与命令的初衷/设计背离;据我所知,一般情况下命令对象可能是无状态的,因为它们是系统的“动词”。对于路由命令,如果我理解正确的话,通常只期望命令的目标具有状态。特别是因为根据声明命令绑定的位置,相同的命令可以路由到不同的处理程序。

所以,我认为至少对于路由命令来说,状态是没有意义的。

但是,我不处理路由命令 - 我专门处理 MVVM 命令。在这种情况下,基本上没有命令的条件路由 - MVVM 视图直接绑定到特定视图模型的命令对象,并且它的执行和可以执行处理程序。

在这种情况下,这样做还有意义吗?

我附上了相关代码的副本,以防有用/感兴趣。

谢谢, 菲尔

I thought I'd post here in the hope that maybe someone with MVVM expertise would be able to offer opinions on whether the following is a good idea:

I'm using Sacha Barber's Cinch MVVM framework, which includes Marlon Grech's SimpleCommand class.

One thing this class doesn't have which some other alternatives have is a Text property, which can be commonly used to bind UI elements to the 'Title' of the command operation. Consequently I've been writing an extension to this class which exposes a Text property.

Now, what I've run into is a use case where I'm using a command to toggle connectivity to a device. There's a bunch of different ways I could implement this (aren't there always - it's software!). One way would be to expose multiple command objects from my ViewModel - one for 'Disconnect' and one for 'Connect; have the view model expose a property which indicates the connection state (IsConnected) and have the view conditionally bind to either the Connect command or Disconnect command. My reaction to this option though, is ... yuck!

What I initially started looking at instead was not only providing a Text property but also having the command object implement INotifyPropertyChanged so that the text property can be dynamically altered by the viewmodel to 'Connect' or 'Disconnect' according to system state. Doing this, I can avoid having multiple commands and just expose a single 'ToggleConnection' command object.

Starting down this path though, it occurs to me that there may be other variations of this pattern whereby the UI needs to be altered according to command state. For example, in addition to changing the command's text according to connection state, you may have places where an icon needs to change according to connection state. So, I started writing a 'Stateful' class which implements INotifyPropertyChanged, and exposes two properties - 'Text' and 'State'. I've made the class generic so that the type of State can be defined by the user (I usually prefer not to use 'object' where avoidable).

The question I have is... Do you think this is a good or bad idea? It may be diverging from the original intention / design of commands; From what I've seen it may in general be the case that command objects were intended to be stateless as they are the 'verbs' of the system. With routed commands, if I understand things correctly only the target of the command would usually be expected to have state. Particularly since the same command could be routed to different handlers depending on where command bindings are declared.

So, I'm thinking that at least with routed commands, state would not make sense.

However, I'm not dealing with routed commands - I'm specifically dealing with MVVM commands. In this case there basically is no conditional routing of commands - the MVVM views bind directly to a particular viewmodel's command objects and it's execute and canexecute handlers.

In this case, does it make sense?

I've attached a copy of the code in question, in case it's of use/interest.

Thanks,
Phil

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

原来分手还会想你 2024-08-10 05:36:33

这实际上取决于您认为哪种更容易合作。

我个人不会将 .Text 属性放在我的命令上,只是因为我无法重用这些命令。它与框架中提供的 RoutedUICommands(或类似的自定义静态命令)不同,因为它们在任何地方都可以重用,并且如果该命令上的“Exit”翻译发生更改,它将反映在整个应用程序中。在你的例子中情况并非如此——一切都是一次性的。

在您的情况下,按钮文本的文本实际上与您的命令分离(即使一个影响另一个),因此它可能最终会变得更容易并且代码会更少一些来分离它们,但区别不会就这么多,最终将成为品味问题。

我绝对同意你关于两个命令的事情 - blech。您编写的大多数按钮委托都必须以某种方式对状态做出反应(您与之交谈的服务已关闭,如果用户选择了此数据,则需要以这种方式填充此数据等),因此我并不认为这样做是错误的委托适应 ViewModel 上的状态信息。

不管怎样,这有点罗嗦……要点是“做任何感觉舒服的事情”。

It's really up to you what you think would be easier to work with.

I personally do not put the .Text property on my commands simply because I get no reuse out of these commands. It's different with the RoutedUICommands provided in the framework (or similar custom static commands) because they are reused everywhere and if the translation of "Exit" were to change on that command, it would be reflected throughout the application. This is not the case in your example - everything would be one-off.

In your case this text of your button text is really decoupled from your command (even though one affects the other) and so it's probably going to end up being easier and a little less code to decouple them, but the difference isn't going to be that much and it will end up being a matter of taste more than anything.

I'm definitely with you on the 2 commands thing - blech. Most button delegates you write will have to react to state in some way (service you talk to is down, this data needs to be populated this way if the user selected this, etc), so I don't really think it's wrong to have the delegate adapt to stateful information on the ViewModel.

Anyway, this is a bit wordy... the take-away is "do whatever feels comfortable".

口干舌燥 2024-08-10 05:36:33

就像最后一张海报所说的那样,“无论什么感觉舒服。”

就我而言,我通常使用类似 DelegateCommand 的东西。如果我需要绑定到某些数据,我会绑定到虚拟机。执行命令时,它会在我的虚拟机中执行(通过在 init 时提供给 DelegateCommand 的委托)。然后执行的委托可能会/可能不会运行一些可重用的代码来满足命令。

听起来您想使用该命令作为它自己的虚拟机。我以前从未想过这样做,但如果你感觉不错,那就去做吧! :)

Like the last poster said, "Whatever feels comfortable."

In my case, I usually use something like the DelegateCommand. If I need to bind to some data, I bind to the VM. When the command is executed, its executed within my VM (via the delegate provided to the DelegateCommand at init). Then the executed delegate may/maynot run some reusable code to satisfy the command.

It sounds like you want to use the Command as it's own VM. I never thought of doing this before myself, but if it feels good to you, do it! :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文