0

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

我有一个简单的模型,其属性指示对象的状态(running, paused, stopped),可以通过对话框进行更改。发生的情况是,我的撤消队列中似乎没有执行任何操作,或者撤消将对象恢复到中间状态。

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

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

如果使用第二种方法,用户打开对话框,将状态更改为paused,在对话框中单击“确定”,撤消/重做的行为与预期相同,但如果再次打开对话框并选择“取消”,则会向“撤消”队列添加另一个操作,即用户必须单击 Undo 两次才能回到初始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;
        }
    }
}
4

1 回答 1

1

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

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

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

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

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

现在您有了不同之处,您可以创建一个纪念品批次,它会按照您的预期恢复准确的状态。

附言。如果您可以在完成后将其放入博客文章中或使用此功能创建 PR,那就太好了

于 2014-09-09T11:32:24.657 回答