在之前的文章中讲过如何实现撤消重做栈:https://gyrojeff.top/index.php/archives/wpf-implement-undo-redo/

问题引入

之前,我们每定义一个支持撤消重做的属性都要写很多代码:

private string _text;

public string Text
{
    get => _text;
    set => UndoRedoManager.PushAndPerformRecord(o =>
    {
        var nowValue = Text;
        SetProperty(ref _text, (string)o);
        return nowValue;
    }, o =>
    {
        var nowValue = Text;
        o ??= value;
        SetProperty(ref _text, (string)o);
        return nowValue;
    });
}

本文的目标是将上述冗长的表达改写成:

private string _text;

[Undoable]
public string Text
{
    get => _text;
    set => SetProperty(value);
}

实现逻辑

代码

public enum BackingNamingStyle
{
    /// <summary>
    /// Example: "BackingStore" => "_backingStore"
    /// </summary>
    UnderscoreAndLowerCase,
    /// <summary>
    /// Example: "BackingStore" => "backingStore"
    /// </summary>
    LowerCase
}

public partial class NotifyObject
{
    public bool SetProperty<T>(T value, [CallerMemberName] string propertyName = "", 
        BackingNamingStyle backingNamingStyle = BackingNamingStyle.UnderscoreAndLowerCase,
        Action beforeChanged = null, Action onChanged = null,
        UndoRedoRecord.DelegateActionWithAndReturnValue undoAction = null,
        UndoRedoRecord.DelegateActionWithAndReturnValue redoAction = null,
        UndoRedoRecord.DelegateActionWithValue disposeAction = null)
    {
        var backingName = "";
        switch (backingNamingStyle)
        {
            case BackingNamingStyle.LowerCase:
                backingName = propertyName[0].ToString().ToLower() + propertyName.Substring(1);
                break;
            case BackingNamingStyle.UnderscoreAndLowerCase:
                backingName = "_" + propertyName[0].ToString().ToLower() + propertyName.Substring(1);
                break;
        }

        Type type = null;
        FieldInfo backingInstance = null;
        do
        {
            type = type == null ? GetType() : type.BaseType;
            if (type == null) return false;
            backingInstance = type.GetField(backingName, BindingFlags.NonPublic | BindingFlags.Instance);
        } while (backingInstance == null);

        if (EqualityComparer<T>.Default.Equals((T)backingInstance.GetValue(this), value))
            return false;

        var attribute = GetType().GetProperty(propertyName)?.GetCustomAttribute<Undoable>();
        if (attribute == null)
        {
            beforeChanged?.Invoke();
            backingInstance.SetValue(this, value);
            onChanged?.Invoke();
            RaisePropertyChanged(propertyName);
        }
        else
        {
            UndoRedoManager.PushAndPerformRecord(undoAction ?? (o =>
            {
                var nowValue = backingInstance.GetValue(this);
                backingInstance.SetValue(this, o);
                RaisePropertyChanged(propertyName);
                return nowValue;
            }), redoAction ?? (o =>
            {
                var nowValue = backingInstance.GetValue(this);
                o ??= value;
                backingInstance.SetValue(this, o);
                RaisePropertyChanged(propertyName);
                return nowValue;
            }), disposeAction);
        }

        return true;
    }
}
最后修改:2021 年 11 月 30 日 04 : 38 PM
真的不买杯奶茶嘛....qwq