更新 SimpleObservable

This commit is contained in:
Hakoyu 2023-09-09 21:22:06 +08:00
parent a2f8496395
commit c24055f446
20 changed files with 266 additions and 65 deletions

View File

@ -33,9 +33,9 @@ public class FoodModel : I18nModel<I18nFoodModel>
public FoodModel()
{
DescriptionId.Value = $"{Id.Value}_{nameof(DescriptionId)}";
Id.ValueChanged += (v) =>
Id.ValueChanged += (o, n) =>
{
DescriptionId.Value = $"{v}_{nameof(DescriptionId)}";
DescriptionId.Value = $"{n}_{nameof(DescriptionId)}";
};
}

View File

@ -28,9 +28,9 @@ public class I18nModel<T>
CurrentI18nData.Value = I18nDatas[I18nHelper.Current.CultureName.Value];
}
private void LangChanged(string value)
private void LangChanged(string oldValue, string newValue)
{
if (I18nDatas.TryGetValue(value, out var result))
if (I18nDatas.TryGetValue(newValue, out var result))
CurrentI18nData.Value = result;
}

View File

@ -39,9 +39,9 @@ public class ModInfoModel : I18nModel<I18nModInfoModel>
public ModInfoModel()
{
DescriptionId.Value = $"{Id.Value}_{nameof(DescriptionId)}";
Id.ValueChanged += (v) =>
Id.ValueChanged += (o, n) =>
{
DescriptionId.Value = $"{v}_{nameof(DescriptionId)}";
DescriptionId.Value = $"{n}_{nameof(DescriptionId)}";
};
}

View File

@ -38,6 +38,7 @@ public class ModMaker : MainPlugin
Setting();
};
modset.Items.Add(menuset);
Application.Current.Resources.MergedDictionaries.Add(new ModMakerStyles());
}
public override void Setting()
@ -45,14 +46,19 @@ public class ModMaker : MainPlugin
if (Maker == null)
{
// 载入ModMaker资源
Application.Current.Resources.MergedDictionaries.Add(new ModMakerStyles());
Maker = new ModMakerWindow();
Maker.ModMaker = this;
//Maker.ModMaker = this;
Maker.Show();
Maker.Closed += Maker_Closed;
}
else
{
Maker.Topmost = true;
}
}
private void Maker_Closed(object sender, EventArgs e)
{
Maker = null;
}
}

View File

@ -15,7 +15,7 @@ public class ObservableRange<T>
Max.ValueChanged += ValueChanged;
}
private void ValueChanged(T value)
private void ValueChanged(T oldValue, T newValue)
{
Info.Value = $"({Min.Value}, {Max.Value})";
}

View File

@ -26,10 +26,10 @@ public class PetModel : I18nModel<I18nPetInfoModel>
{
PetNameId.Value = $"{Id.Value}_{nameof(PetNameId)}";
DescriptionId.Value = $"{Id.Value}_{nameof(DescriptionId)}";
Id.ValueChanged += (v) =>
Id.ValueChanged += (o, n) =>
{
PetNameId.Value = $"{v}_{nameof(PetNameId)}";
DescriptionId.Value = $"{v}_{nameof(DescriptionId)}";
PetNameId.Value = $"{n}_{nameof(PetNameId)}";
DescriptionId.Value = $"{n}_{nameof(DescriptionId)}";
};
}

View File

@ -46,9 +46,9 @@ public class SelectTextModel : I18nModel<I18nSelectTextModel>
public SelectTextModel()
{
ChooseId.Value = $"{Id.Value}_{nameof(ChooseId)}";
Id.ValueChanged += (v) =>
Id.ValueChanged += (o, n) =>
{
ChooseId.Value = $"{v}_{nameof(ChooseId)}";
ChooseId.Value = $"{n}_{nameof(ChooseId)}";
};
}

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
@ -11,6 +12,9 @@ namespace HKW.HKWViewModels.SimpleObservable;
/// <summary>
/// 可观察命令
/// </summary>
[DebuggerDisplay(
"CanExecute = {CanExecuteProperty.Value}, EventCount = {ExecuteEvent.GetInvocationList().Length}, AsyncEventCount = {AsyncExecuteEvent.GetInvocationList().Length}"
)]
public class ObservableCommand : ICommand
{
/// <summary>
@ -21,6 +25,7 @@ public class ObservableCommand : ICommand
/// <summary>
/// 等待异步执行完成
/// </summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly ObservableValue<bool> r_waiting = new(false);
/// <inheritdoc/>
@ -30,14 +35,12 @@ public class ObservableCommand : ICommand
r_waiting.PropertyChanged += InvokeCanExecuteChanged;
}
private void InvokeCanExecuteChanged(
object? sender,
System.ComponentModel.PropertyChangedEventArgs e
)
private void InvokeCanExecuteChanged(object? sender, PropertyChangedEventArgs e)
{
CanExecuteChanged?.Invoke(sender, e);
}
#region ICommand
/// <summary>
/// 能否被执行
/// </summary>
@ -75,7 +78,54 @@ public class ObservableCommand : ICommand
await asyncEvent.Invoke();
r_waiting.Value = false;
}
#endregion
#region NotifyReceiver
/// <summary>
/// 添加通知属性改变后接收器
/// <para>
/// 添加的接口触发后会执行 <see cref="NotifyCanExecuteReceived"/>
/// </para>
/// <para>示例:
/// <code><![CDATA[
/// ObservableValue<string> value = new();
/// ObservableCommand command = new();
/// command.AddNotifyReceiver(value);
/// command.NotifyCanExecuteReceived += (ref bool canExecute) =>
/// {
/// canExecute = false; // trigger this
/// };
/// value.Value = "A"; // execute this
/// // result: value.Value == "A" , command.CanExecuteProperty == false
/// ]]>
/// </code></para>
/// </summary>
/// <param name="notifies">通知属性改变后接口</param>
public void AddNotifyReceiver(params INotifyPropertyChanged[] notifies)
{
foreach (var notify in notifies)
notify.PropertyChanged += Notify_PropertyChanged;
}
/// <summary>
/// 删除通知属性改变后接收器
/// </summary>
/// <param name="notifies">通知属性改变后接口</param>
public void RemoveNotifyReceiver(params INotifyPropertyChanged[] notifies)
{
foreach (var notify in notifies)
notify.PropertyChanged -= Notify_PropertyChanged;
}
private void Notify_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
var temp = CanExecuteProperty.Value;
NotifyCanExecuteReceived?.Invoke(ref temp);
CanExecuteProperty.Value = temp;
}
#endregion
#region Event
/// <summary>
/// 能否执行属性改变后事件
/// </summary>
@ -91,6 +141,13 @@ public class ObservableCommand : ICommand
/// </summary>
public event AsyncExecuteHandler? AsyncExecuteEvent;
/// <summary>
/// 可执行通知接收器事件
/// </summary>
public event NotifyReceivedHandler? NotifyCanExecuteReceived;
#endregion
#region Delegate
/// <summary>
/// 执行
/// </summary>
@ -101,4 +158,11 @@ public class ObservableCommand : ICommand
/// </summary>
/// <returns></returns>
public delegate Task AsyncExecuteHandler();
/// <summary>
/// 通知接收器
/// </summary>
/// <param name="value">引用值</param>
public delegate void NotifyReceivedHandler(ref bool value);
#endregion
}

View File

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@ -11,6 +13,9 @@ namespace HKW.HKWViewModels.SimpleObservable;
/// 带参数的可观察命令
/// </summary>
/// <typeparam name="T">参数类型</typeparam>
[DebuggerDisplay(
"CanExecute = {CanExecuteProperty.Value}, EventCount = {ExecuteEvent.GetInvocationList().Length}, AsyncEventCount = {AsyncExecuteEvent.GetInvocationList().Length}"
)]
public class ObservableCommand<T> : ICommand
where T : notnull
{
@ -18,6 +23,7 @@ public class ObservableCommand<T> : ICommand
public ObservableValue<bool> CanExecuteProperty { get; } = new(true);
/// <inheritdoc cref="ObservableCommand.r_waiting"/>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly ObservableValue<bool> r_waiting = new(false);
/// <inheritdoc />
@ -27,14 +33,12 @@ public class ObservableCommand<T> : ICommand
r_waiting.PropertyChanged += InvokeCanExecuteChanged;
}
private void InvokeCanExecuteChanged(
object? sender,
System.ComponentModel.PropertyChangedEventArgs e
)
private void InvokeCanExecuteChanged(object? sender, PropertyChangedEventArgs e)
{
CanExecuteChanged?.Invoke(sender, e);
}
#region ICommand
/// <inheritdoc cref="ObservableCommand.CanExecute(object?)"/>
public bool CanExecute(object? parameter)
{
@ -63,7 +67,32 @@ public class ObservableCommand<T> : ICommand
await asyncEvent.Invoke(parameter);
r_waiting.Value = false;
}
#endregion
#region NotifyReceiver
/// <inheritdoc cref="ObservableCommand.AddNotifyReceiver(INotifyPropertyChanged[])"/>
public void AddNotifyReceiver(params INotifyPropertyChanged[] notifies)
{
foreach (var notify in notifies)
notify.PropertyChanged += Notify_PropertyChanged;
}
/// <inheritdoc cref="ObservableCommand.RemoveNotifyReceiver(INotifyPropertyChanged[])"/>
public void RemoveNotifyReceiver(params INotifyPropertyChanged[] notifies)
{
foreach (var notify in notifies)
notify.PropertyChanged -= Notify_PropertyChanged;
}
private void Notify_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
var temp = CanExecuteProperty.Value;
NotifyCanExecuteReceived?.Invoke(ref temp);
CanExecuteProperty.Value = temp;
}
#endregion
#region Event
/// <inheritdoc cref="ObservableCommand.CanExecuteChanged"/>
public event EventHandler? CanExecuteChanged;
@ -73,6 +102,11 @@ public class ObservableCommand<T> : ICommand
/// <inheritdoc cref="ObservableCommand.AsyncExecuteEvent"/>
public event AsyncExecuteHandler? AsyncExecuteEvent;
/// <inheritdoc cref="ObservableCommand.NotifyCanExecuteReceived"/>
public event NotifyReceivedHandler? NotifyCanExecuteReceived;
#endregion
#region Delegate
/// <inheritdoc cref="ObservableCommand.ExecuteHandler"/>
/// <param name="value">值</param>
public delegate void ExecuteHandler(T value);
@ -80,4 +114,11 @@ public class ObservableCommand<T> : ICommand
/// <inheritdoc cref="ObservableCommand.AsyncExecuteHandler"/>
/// <param name="value">值</param>
public delegate Task AsyncExecuteHandler(T value);
/// <summary>
/// 通知接收器
/// </summary>
/// <param name="value">引用值</param>
public delegate void NotifyReceivedHandler(ref bool value);
#endregion
}

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@ -11,39 +12,118 @@ namespace HKW.HKWViewModels.SimpleObservable;
/// 可观察值
/// </summary>
/// <typeparam name="T"></typeparam>
[DebuggerDisplay("{Value}")]
public class ObservableValue<T> : INotifyPropertyChanging, INotifyPropertyChanged
where T : notnull
{
private T? _value = default;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private T _value = default!;
/// <summary>
/// 当前值
/// </summary>
public T? Value
public T Value
{
get => _value;
set
{
if (_value?.Equals(value) is true)
return;
PropertyChanging?.Invoke(this, new(nameof(Value)));
ValueChanging?.Invoke(_value, value);
NotifyPropertyChanging(_value, value);
_value = value;
PropertyChanged?.Invoke(this, new(nameof(Value)));
ValueChanged?.Invoke(value);
NotifyPropertyChanged(_value, value);
}
}
#region Ctor
/// <inheritdoc/>
public ObservableValue() { }
/// <inheritdoc/>
/// <param name="value">值</param>
/// <param name="value">初始值</param>
public ObservableValue(T value)
{
_value = value;
}
#endregion
#region NotifyProperty
/// <summary>
/// 通知属性改变前
/// </summary>
/// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param>
private void NotifyPropertyChanging(T oldValue, T newValue)
{
PropertyChanging?.Invoke(this, new(nameof(Value)));
ValueChanging?.Invoke(oldValue, newValue);
}
/// <summary>
/// 通知属性改变后
/// </summary>
/// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param>
private void NotifyPropertyChanged(T oldValue, T newValue)
{
PropertyChanged?.Invoke(this, new(nameof(Value)));
ValueChanged?.Invoke(oldValue, newValue);
}
#endregion
#region Overwrite
/// <inheritdoc/>
public override string ToString()
{
return Value?.ToString()!;
}
#endregion
#region NotifyReceiver
/// <summary>
/// 添加通知属性改变后接收器
/// <para>
/// 添加的接口触发后会执行 <see cref="NotifyReceived"/>
/// </para>
/// <para>示例:
/// <code><![CDATA[
/// ObservableValue<string> value1 = new();
/// ObservableValue<string> value2 = new();
/// value2.AddNotifyReceiver(value1);
/// value2.NotifyReceived += (ref string v) =>
/// {
/// v = "B"; // trigger this
/// };
/// value1.Value = "A"; // execute this
/// // result: value1.Value == "A" , value2.Value == "B"
/// ]]>
/// </code></para>
/// </summary>
/// <param name="notifies">通知属性改变后接口</param>
public void AddNotifyReceiver(params INotifyPropertyChanged[] notifies)
{
foreach (var notify in notifies)
notify.PropertyChanged += Notify_PropertyChanged;
}
/// <summary>
/// 删除通知属性改变后接收器
/// </summary>
/// <param name="notifies">通知属性改变后接口</param>
public void RemoveNotifyReceiver(params INotifyPropertyChanged[] notifies)
{
foreach (var notify in notifies)
notify.PropertyChanged -= Notify_PropertyChanged;
}
private void Notify_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
var temp = Value;
NotifyReceived?.Invoke(ref temp);
Value = temp;
}
#endregion
#region Event
/// <summary>
/// 属性改变前事件
/// </summary>
@ -57,23 +137,31 @@ public class ObservableValue<T> : INotifyPropertyChanging, INotifyPropertyChange
/// <summary>
/// 值改变前事件
/// </summary>
public event ValueChangingEventHandler? ValueChanging;
public event ValueChangeEventHandler? ValueChanging;
/// <summary>
/// 值改变后事件
/// </summary>
public event ValueChangedEventHandler? ValueChanged;
public event ValueChangeEventHandler? ValueChanged;
/// <summary>
/// 值改变后事件方法
/// 通知接收器事件
/// </summary>
/// <param name="value">值</param>
public delegate void ValueChangedEventHandler(T? value);
public event NotifyReceivedHandler? NotifyReceived;
#endregion
#region Delegate
/// <summary>
/// 值改变事件方法
/// 值改变事件
/// </summary>
/// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param>
public delegate void ValueChangingEventHandler(T? oldValue, T? newValue);
public delegate void ValueChangeEventHandler(T oldValue, T newValue);
/// <summary>
/// 通知接收器
/// </summary>
/// <param name="value">引用值</param>
public delegate void NotifyReceivedHandler(ref T value);
#endregion
}

View File

@ -34,9 +34,9 @@ public class ClickTextPageVM
RemoveCommand.ExecuteEvent += Remove;
}
private void Filter_ValueChanged(string value)
private void Filter_ValueChanged(string oldValue, string newValue)
{
if (string.IsNullOrWhiteSpace(value))
if (string.IsNullOrWhiteSpace(newValue))
{
ShowClickTexts.Value = ClickTexts;
}
@ -44,7 +44,7 @@ public class ClickTextPageVM
{
ShowClickTexts.Value = new(
ClickTexts.Where(
m => m.Id.Value.Contains(value, StringComparison.OrdinalIgnoreCase)
m => m.Id.Value.Contains(newValue, StringComparison.OrdinalIgnoreCase)
)
);
}

View File

@ -36,16 +36,16 @@ public class FoodPageVM
RemoveCommand.ExecuteEvent += Remove;
}
private void Filter_ValueChanged(string value)
private void Filter_ValueChanged(string oldValue, string newValue)
{
if (string.IsNullOrWhiteSpace(value))
if (string.IsNullOrWhiteSpace(newValue))
{
ShowFoods.Value = Foods;
}
else
{
ShowFoods.Value = new(
Foods.Where(m => m.Id.Value.Contains(value, StringComparison.OrdinalIgnoreCase))
Foods.Where(m => m.Id.Value.Contains(newValue, StringComparison.OrdinalIgnoreCase))
);
}
}

View File

@ -37,16 +37,18 @@ public class LowTextPageVM
RemoveCommand.ExecuteEvent += Remove;
}
private void Filter_ValueChanged(string value)
private void Filter_ValueChanged(string oldValue, string newValue)
{
if (string.IsNullOrWhiteSpace(value))
if (string.IsNullOrWhiteSpace(newValue))
{
ShowLowTexts.Value = LowTexts;
}
else
{
ShowLowTexts.Value = new(
LowTexts.Where(m => m.Id.Value.Contains(value, StringComparison.OrdinalIgnoreCase))
LowTexts.Where(
m => m.Id.Value.Contains(newValue, StringComparison.OrdinalIgnoreCase)
)
);
}
}

View File

@ -55,11 +55,11 @@ public class ModEditWindowVM
SaveToCommand.ExecuteEvent += SaveTo;
}
private void CurrentLang_ValueChanged(string value)
private void CurrentLang_ValueChanged(string oldValue, string newValue)
{
if (value is null)
if (newValue is null)
return;
ModInfo.Value.CurrentI18nData.Value = ModInfo.Value.I18nDatas[value];
ModInfo.Value.CurrentI18nData.Value = ModInfo.Value.I18nDatas[newValue];
}
public void Close()

View File

@ -31,7 +31,7 @@ public class PetEditWindowVM
Image.ValueChanged += Image_ValueChanged;
}
private void Image_ValueChanged(BitmapImage value)
private void Image_ValueChanged(BitmapImage oldValue, BitmapImage newValue)
{
//LengthRatio.Value = BorderLength.Value / value.PixelWidth;
}

View File

@ -34,16 +34,16 @@ public class PetPageVM
RemoveCommand.ExecuteEvent += Remove;
}
private void Filter_ValueChanged(string value)
private void Filter_ValueChanged(string oldValue, string newValue)
{
if (string.IsNullOrWhiteSpace(value))
if (string.IsNullOrWhiteSpace(newValue))
{
ShowPets.Value = Pets;
}
else
{
ShowPets.Value = new(
Pets.Where(m => m.Id.Value.Contains(value, StringComparison.OrdinalIgnoreCase))
Pets.Where(m => m.Id.Value.Contains(newValue, StringComparison.OrdinalIgnoreCase))
);
}
}

View File

@ -34,9 +34,9 @@ public class SelectTextPageVM
RemoveCommand.ExecuteEvent += Remove;
}
private void Filter_ValueChanged(string value)
private void Filter_ValueChanged(string oldValue, string newValue)
{
if (string.IsNullOrWhiteSpace(value))
if (string.IsNullOrWhiteSpace(newValue))
{
ShowSelectTexts.Value = SelectTexts;
}
@ -44,7 +44,7 @@ public class SelectTextPageVM
{
ShowSelectTexts.Value = new(
SelectTexts.Where(
m => m.Id.Value.Contains(value, StringComparison.OrdinalIgnoreCase)
m => m.Id.Value.Contains(newValue, StringComparison.OrdinalIgnoreCase)
)
);
}

View File

@ -32,7 +32,7 @@ public class WorkEditWindowVM
Image.ValueChanged += Image_ValueChanged;
}
private void Image_ValueChanged(BitmapImage value)
private void Image_ValueChanged(BitmapImage oldValue, BitmapImage newValue)
{
//LengthRatio.Value = BorderLength.Value / value.PixelWidth;
}

View File

@ -38,21 +38,21 @@ public class WorkPageVM
RemoveCommand.ExecuteEvent += Remove;
}
private void CurrentPet_ValueChanged(PetModel value)
private void CurrentPet_ValueChanged(PetModel oldValue, PetModel newValue)
{
ShowWorks.Value = value.Works;
ShowWorks.Value = newValue.Works;
}
private void Filter_ValueChanged(string value)
private void Filter_ValueChanged(string oldValue, string newValue)
{
if (string.IsNullOrWhiteSpace(value))
if (string.IsNullOrWhiteSpace(newValue))
{
ShowWorks.Value = Works;
}
else
{
ShowWorks.Value = new(
Works.Where(m => m.Id.Value.Contains(value, StringComparison.OrdinalIgnoreCase))
Works.Where(m => m.Id.Value.Contains(newValue, StringComparison.OrdinalIgnoreCase))
);
}
}

View File

@ -99,12 +99,12 @@ public class ModMakerWindowVM
}
}
private void ModFilterText_ValueChanged(string value)
private void ModFilterText_ValueChanged(string oldValue, string newValue)
{
if (string.IsNullOrEmpty(value))
if (string.IsNullOrEmpty(newValue))
ShowHistories.Value = Histories;
else
ShowHistories.Value = new(Histories.Where(i => i.Id.Contains(value)));
ShowHistories.Value = new(Histories.Where(i => i.Id.Contains(newValue)));
}
public void CreateNewMod()