如何在Catel中实现具有撤消/重做支持的更改状态对话框?

时间:2014-09-08 14:56:34

标签: catel memento

使用对话框时,我无法让Undo和Redo正常运行。

我有一个简单的模型,其中包含一个属性,指示对象的状态(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;
        }
    }
}

1 个答案:

答案 0 :(得分:1)

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

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

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

然后在初始化保存方法中执行此操作,您将拥有2组属性(状态前和状态后)。现在你已经掌握了这一点,很容易计算出差异:

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

现在你有了不同之处,你可以创建一个memento批处理,它会恢复你预期的确切状态。

PS。如果您可以在完成后将其放入博客文章或使用此功能创建PR,那将是很棒的