C#CommandManager结合了undo / Redo模式

时间:2013-04-03 10:24:40

标签: c# wpf icommand undo-redo

我目前正在寻找使用ICommand将撤消/重做(基于this文章)与命令模式相结合。阅读CanExecute和CanExecuteChanged的想法,我找到了CommandManager,它将已更改的状态传播到所有已注册的UIElements。 但是,如果命令存储已更改的数据本身,则很难将这两者结合起来。这里是当前情况的一个例子:

interface IUndoCommand : ICommand
{
    void Undo();
}

和实施:

public class SampleCommand : IUndoCommand
{
    private Point oldPosition;
    private Shape shape;

    // Gets the currently moved shape object
    public MoveCommand(Shape shape, Point oldPosition)
    {
        this.oldPosition = oldPosition;
        this.newPosition = new Point(Cavas.GetLeft(shape),Canvas.GetTop(shape));
        this.shape = shape;
    }

    public void Execute(object parameter)
    {
        Model.PersistChanges(shape, newPosition);
    }

    public void Undo()
    {
        Model.PersistChanges(shape, oldPosition);
    }

    public bool CanExecute(object parameter)
    {
    }

    public event EventHandler CanExecuteChanged;
}

冲突

在此实现中,命令存储更改,因此每次执行时都需要创建新实例。但是,CommandManager会跟踪状态并通知UIElements。如果我在那里注册命令的每个实例,则一个UIElement有几个相等的命令竞争CanExecute-state。这似乎打破了这个想法,所以它是如何运作的?

当然,我可以将重做/撤消所需的状态从命令移动到模型,并准确注册一个(静态)命令实例,该实例将全部使用。但实际上我喜欢在命令中训练状态的想法。

如果我省略了ICommand用法,那么wpftutorial.net的例子就可以了 - 虽然我还没有完全理解那里的动作/撤消动作。

问题

如何组合这些方法,undo / redo + CommandManager?是实现在模型中保存状态的唯一解决方案(将MVVM视为基础)还是有其他机会?

编辑:

是否可以使用CommandManager在命令中保存状态? ICommand-Interface提供了跟踪CanExecute-State的功能,这是个不错的主意。但是,我没有看到在命令中保存状态时保留这个想法的可能性(因此需要几个不同的实例)。

1 个答案:

答案 0 :(得分:1)

对于其他每一个绊脚石的人:

我现在使用分层ViewModels解决了这个问题。每个命令现在都有自己的ViewModel。

public class ViewModel 
{

    public ViewModel()
    {
        MoveCommand = new MoveCommand(this);
    }

    public Shape Shape {get;set;}
    public Point CurrentPosition {get;set;}
    public ICommand MoveCommand {get; private set;}
}


public class MoveCommand
{
    ViewModel viewModel;
    Point shiftVector;

    public MoveCommand(ViewModel viewModel, Point shiftVector)
    {
        this.viewModel = viewModel;
        this.shiftVector = shiftVector;
    }

    public void Execute(object parameter)
    {
        shapeVM.CurrentPosition.X += shiftVector.X;
        shapeVM.CurrentPosition.Y += shiftVector.Y;
    }

    public void Undo()
    {
        shapeVM.CurrentPosition.X -= shiftVector.X;
        shapeVM.CurrentPosition.Y -= shiftVector.Y;
    }

    public bool CanExecute(object parameter)
    {
    }

    // Notice here: the events should be passed to the command manager to take care about it
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove {CommandManager.RequerySuggested -= value;}
    }

分层部分发挥如下作用:

public class BaseViewModel
{
    ObservableCollection<ViewModels> ViewModels;

    // pass the command from the child's viewModel.
    public ICommand MoveCommand
    {
        get
        {
            return SelectedItem.MoveCommand;
        }
    }

    public SelectedItem ViewModel {get;set;}

    public BaseViewModel()
    {

    }

}