12

在我之前的项目中,我已经在 c++ 中实现了 undo system,并且我知道它是如何工作的。我也知道命令模式。

我将实现一个 C#/WPF 桌面应用程序,并希望我的设计基于 MV-VM 模式。

该应用程序将:

  • 是一个相对较小的项目(1 个开发人员估计需要 2-3 周的工作时间)
  • 有一个具有持久性的简单数据模型(linq to XML)
  • 支持撤销/重做

我想知道是否有人在遵循 MV-VM 模式时有实施撤消系统的经验。它如何融入其中?它如何从 INotifyPropertyChanged 和 INotifyCollectionChanged 通知中受益,因此在实现模型(业务对象)时所需的工作最少。

我认为撤消系统会以某种方式集成到 ViewModel 层中,因为它是一种 UI 状态。

任何想法?

4

3 回答 3

13

这是我用于我的项目的解决方案。事实证明,该解决方案运行良好。

系统正在使用撤消事件对象,其中每个撤消事件都知道如何撤消和重做自身。

interface IUndoEvent
{
    void Undo();
    void Redo();
}

我能够通过仅实现 2 个撤消事件来构建系统:一个用于属性更改;另一个用于更改属性。一个用于集合更改。

这个想法是这些事件通过直接修改模型来实现撤消/重做。

class PropertyChangeUndoEvent : IUndoEvent
{
    private ModelBase _target;
    private string _propertyName;
    private object _oldValue;
    private object _newValue;

    public PropertyChangeUndoEvent(ModelBase target, string propertyName, object oldValue, object newValue)
    {
        _target = target;
        _propertyName = propertyName;
        _oldValue = oldValue;
        _newValue = newValue;
    }

    public void Undo()
    {
        SetValue(_oldValue);
    }

    public void Redo()
    {
        SetValue(_newValue);
    }

    private void SetValue(object value)
    {
        // Set Value on the _target using reflection (_propertyName)
    }
}

ViewModel 通过调用 ViewModelBase 函数负责创建撤消事件:

class MyViewModel : ViewModelBase
{
    public string Name
    {
        get { return _model.Name; }

        // The SetValue will create a undo event, and push it to the UndoManager
        set { SetValue(_model, "Name", value); }
    }
}

最后,还有一个UndoManager(项目单例),用于存储撤消堆栈和重做堆栈。

于 2009-10-22T12:22:30.927 回答
4

您可能会发现 Monitored Undo Framework 很有用。http://muf.codeplex.com/。它不使用“自上而下”命令模式,而是在更改发生时监视更改,并允许您在撤消堆栈上放置一个委托来撤消更改。

我将其创建为使用 MVVM 构建的 WPF 应用程序的一部分。大多数撤消操作起源于我们的底层域模型,但我们也连接到 ViewModel 的某些区域以允许在那里撤消/重做。

您可以在http://muf.codeplex.com/上的 codeplex 站点上找到更多信息和文档。

于 2011-06-21T03:30:55.077 回答
2

我想您正在将 Command 模式与 Memento 结合起来?

我认为撤消系统会以某种方式集成到 ViewModel 层中,因为它是一种 UI 状态。

?!通常,undo/redo 作用于业务对象,UI 反映业务层。

假设我们有一个带有“描述”字符串的产品类。ProductVM 公开了一个引发 PropertyChanged 的​​字符串属性。在修改时,纪念品会保留旧的模型实例。如果撤消,请使用 ProductVM.Description = (memento as Product).Description 恢复备忘录:模型和 UI 也会更新。

注意:避免(memento as Product),仅用于示例;)

于 2009-05-25T15:39:59.677 回答