如何在 MVVM 应用程序中实现撤消/重做?
我正在开发一个 Silverlight LoB 应用程序,设计人员希望它有一个选项卡式界面,类似于 Visual Studio 的界面(我们可能会使用 Telerik Rad 控件作为停靠选项卡)。完成原型后,该界面到目前为止运行良好,但我在思考如何在 MVVM 项目中实现撤消/重做功能时遇到问题。
撤消/重做功能必须:
- 在撤消/重做时,恢复 UI 状态, 即返回焦点、选择等 控件(例如文本框) 变化的起源。
- 拥有每个视图的撤消/重做堆栈
通常,我会使用命令模式,但我不确定如何将其应用于 MVVM。
我用过命令&绑定以获得视图和视图的理想化松耦合。视图模型,但它使撤消/重做变得更加棘手,因为视图模型没有任何视图概念以及接收命令或更改绑定属性时视图状态。看来我需要某种服务跟踪每当用户执行某些可撤消的操作并获取状态以供以后恢复时哪个视图处于活动状态。
对于在 MVVM 中实现撤消/重做的最佳实践有什么共识吗?我饶有兴趣地研究了 Daniel Vaughan 在他的钙项目中是如何做到这一点的; Blend 显然是使用 MVVM 模式编写的,它的行为正如我希望我的应用程序那样,如果 MS 解释他们是如何做到的那就太好了!
I'm working on a Silverlight LoB app which the designers want to have a tabbed-interface, similar to the interface of Visual Studio (we'll probably use the Telerik Rad controls for docking tabs). Having done a prototype, the interface is working well so far, but I'm having problems thinking of how to implement undo/redo functionality in an MVVM project.
The undo/redo functionality has to:
- On undo/redo, restore the UI state,
i.e. return focus, selection etc. to
the control(s) (such as a textbox)
that the change originated from. - Have a per-view undo/redo stack
Typically, I'd use the command pattern, but I'm not sure how to apply that with MVVM.
I've used commanding & binding to get the idealised loose-coupling of the views & view-models, but it makes undo/redo a lot trickier, since the view-model doesn't have any concept of the view and the state of the view when a command is received or a bound property is changed. It seems that I need some kind of service tracking which view is active whenever the user performs some undoable action and gets the state for later restoration.
Is there any consensus on what is the best-practise for implmenting undo/redo in MVVM? I've looked with interest at how Daniel Vaughan does it in his Calcium project; Blend was apparently written using the MVVM pattern and it behaves just as I want my app to, it'd be great if MS explained how they did it!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您需要做的第一件事是确保将操作与界面完全分开。这意味着将所有影响数据的操作转变为离散的操作。这也意味着无论什么导致观点改变也应该被记录为一个离散的行动。基本上,界面的状态应该只反映数据更改和基于命令的视图更改(请参阅下面关于视图更改的最后一条注释)。
我们以前使用过的最成功的撤消系统允许嵌套 IUndoableCommand 对象。这些复合命令汇总为单个用户操作(您希望在“撤消”菜单中看到显示的操作类型)。
我注意到您提到在视图之间使用撤消...这对于多形式应用程序来说似乎是不寻常的行为。通常撤消仅在单个控件内以及任何拖放操作中进行。例外情况通常是基于图形的界面(不是基于表单的)。在撤消过程中更改形式相当于 MS Word 切换到另一个文档并继续撤消...对于最终用户来说相当令人不安。可能想让用户体验人员重新考虑设计的这方面。只值我的2美分。
希望这有帮助。
The first thing you need to do is make sure you separate the actions completely from the interface. That means turning all operations that affect data into discrete actions. That also implies that whatever causes a change of view should also be recorded as a discrete action. Basically the state of the interface should only reflect the data-changes and command-based view changes (see my last note below about view changing though).
The most successful undo systems we have used previously allowed nesting of IUndoableCommand objects. These compound commands roll up into a single user action (the sort of action you would expect to see display in an Undo menu).
I note you mention using undo across views... That seems like unusual behaviour for a multiform app. Normally undo is only within individual controls and for any drag-drop operations. The exceptions are usually graphic based interfaces (not forms-based). Changing form during undo would be the equivalent of MS Word switching to another document and continuing to undo... quite disturbing for the end user. Might want to get the user experience guys to rethink that aspect of the design. Just my 2 cents worth.
Hope this helps.
@JamesCo,
我已经为 WPF 应用程序实现了撤消/重做,并最终将我的撤消/重做代码发布到 http:// muf.codeplex.com/。您还可以通过 NuGet 获取它。只需查找“MUF”或“受监控的撤消框架”即可。它包括对 Silverlight 4.0 以及 .NET 3.5、4.0 和 WP7 的支持。
我的 WPF 应用程序也使用 MVVM,并且在某些情况下,确实允许撤消选择更改等操作。我还跟踪了 WPF 框架中显示的活动“页面”,以便用户移回应应用撤消操作的页面。
该库采用灵活的方法来编写撤消/重做每个步骤的操作。最终,只需要一个撤消委托和一个重做委托。您可以让这些代表做您喜欢做的任何事情。库中包含一个默认实现,它仅采用对象、属性名称、旧值和新值。它构建基于反射的委托,这些委托将根据需要应用旧值或新值。
至于隔离每个视图的更改,该库允许您为每个“文档”或“容器”保留单独的撤消/重做操作堆栈。您只需传递对容器的引用即可获取关联的撤消/重做堆栈。
最后,该库支持批量更改。这在需要将多个操作作为一个单元一起撤消时非常有用。
欢迎在 codeplex 网站 (http://muf.codeplex.com/) 上提出意见和问题。您还可以在那里找到完整的文档和示例应用程序。
@JamesCo,
I've implemented undo / redo for a WPF application and ended up publishing my undo / redo code to http://muf.codeplex.com/. You can also get it via NuGet. Just look for "MUF" or "Monitored Undo Framework". It includes support for Silverlight 4.0, as well as .NET 3.5, 4.0, and WP7.
My WPF app also used MVVM and, in some cases, did allow undoing things like selection changes. I also tracked the active "page" that was shown in the WPF Frame so that the user was moved back to the page where the undo action should apply.
The library takes a flexible approach as to how you code up the action to undo / redo each step. Ultimately, it just takes a delegate for undo and a delegate for redo. You can have these delegates do whatever you like. Included in the library is a default implementation that simply takes the object, the name of the property, the old value, and the new value. It constructs reflection based delegates that will apply the old or new values as needed.
As far as isolating the changes for each view, the library allows you to keep a separate stack of undo / redo actions for each "document" or "container". You simply pass a reference to the container in order to get the associated undo / redo stack.
Lastly, the library includes support for batching changes together. This is useful in times where multiple actions should be undone together as a unit.
Comments and questions are welcome on the codeplex site (http://muf.codeplex.com/). You'll also find complete documentation and sample apps there.