应该如何在Catel中实现具有undo/redo支持的更改状态对话框



在使用对话框时,我无法使"撤消"one_answers"重做"正常工作。

我有一个简单的模型,它的属性指示对象的状态(runningpausedstopped),可以通过对话框进行更改。发生的情况是,我在撤消队列中得到的操作似乎什么都不做,或者撤消将对象恢复到中间状态。

模型对象已在构造函数中用memento注册。该对话框有三个单选按钮,每个单选按钮代表三种不同状态中的一种。每个单选按钮都绑定到一个命令。每个命令都会执行属性更改。我尝试了两种不同的方法,一种是每个命令直接在对象中设置属性,另一种是在调用时为视图模型设置实例变量,然后使用Saving事件修改对象。

如果使用第一种方法,如果用户在对话框中单击"确定"之前单击了多个单选按钮,则每个属性更改都会被放入"撤消"队列。试图通过将整个对话框包装成一个批来解决这个问题,但这会导致撤消状态更改,对象将恢复到最后一个对话框之前的状态,即,如果在对话框打开之前将属性设置为stopped,并且用户按下了暂停单选按钮,然后启动一个对话框,最后单击"确定",撤消将属性设置成paused,而不是预期的stopped

如果使用第二种方法,用户打开对话框,将状态更改为paused,在对话框中单击"确定",则撤消/重做行为如预期,但如果再次打开对话框并选择"取消",则将向撤消队列中添加一个操作,即用户必须单击"撤消"两次才能返回到初始stopped-状态。

因此,我的问题是,应该如何正确实施这一点,以获得预期的行为;每个对话框交互都可以撤消,而不是对话框中的每个交互?

以下是ViewModel的代码:

namespace UndoRedoTest.ViewModels
{
    using Catel.Data;
    using Catel.MVVM;
    public class StartStopViewModel : ViewModelBase
    {
        Machine.MachineState _state;
        public StartStopViewModel(Machine controlledMachine) 
        {
            ControlledMachine = controlledMachine;
            _state = controlledMachine.State;
            StartMachine = new Command(OnStartMachineExecute);
            PauseMachine = new Command(OnPauseMachineExecute);
            StopMachine = new Command(OnStopMachineExecute);
            Saving += StartStopViewModel_Saving;
        }
        void StartStopViewModel_Saving(object sender, SavingEventArgs e)
        {
            ControlledMachine.State = _state;
        }
        [Model]
        public Machine ControlledMachine
        {
            get { return GetValue<Machine>(ControlledMachineProperty); }
            private set { SetValue(ControlledMachineProperty, value); }
        }
        public static readonly PropertyData ControlledMachineProperty = RegisterProperty("ControlledMachine", typeof(Machine));
        public override string Title { get { return "Set Machine state"; } }
        public Command StartMachine { get; private set; }
        public Command PauseMachine { get; private set; }
        public Command StopMachine { get; private set; }
        private void OnStartMachineExecute()
        {
            _state = Machine.MachineState.RUNNING;
            //ControlledMachine.SecondState = Machine.MachineState.RUNNING;
        }
        private void OnPauseMachineExecute()
        {
            _state = Machine.MachineState.PAUSED;
            //ControlledMachine.SecondState = Machine.MachineState.PAUSED;
        }
        private void OnStopMachineExecute()
        {
            _state = Machine.MachineState.STOPPED;
            //ControlledMachine.SecondState = Machine.MachineState.STOPPED;
        }
    }
}

首先,不要订阅Saving事件,只需覆盖Save()方法。请注意,当使用ModelAttribute装饰模型时,Catel会为您处理模型状态。因此,您需要获得对话框的前状态和后状态,然后将结果集推送到一个批中。

例如,我会为对象类(或模型类)创建如下扩展方法:

public static Dictionary<string, object> GetProperties(this IModel model)
{
    // todo: return properties
}

然后在InitializeSave方法中执行此操作,您将有两组属性(前状态和后状态)。现在你有了,很容易计算差异:

public static Dictionary<string, object> GetChangedProperties(Dictionary<string, object> preState, Dictionary<string, object> postState)
{
    // todo: calculate difference
}

现在你有了区别,你可以创建一个纪念批次,它会恢复你预期的确切状态。

ps。如果你能在完成后把它放进博客文章中,或者用这个功能创建一个PR,那就太好了

最新更新