修复 多语言编辑器Id重复问题

This commit is contained in:
Hakoyu 2023-11-11 00:21:32 +08:00
parent b163719b69
commit bdb47b0124
12 changed files with 798 additions and 195 deletions

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Globalization; using System.Globalization;
@ -202,4 +203,62 @@ public static class Extensions
{ {
return $"{cultureInfo.DisplayName} [{cultureInfo.Name}]"; return $"{cultureInfo.DisplayName} [{cultureInfo.Name}]";
} }
/// <summary>
/// 获取目标
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="weakReference">弱引用</param>
/// <returns>获取成功返回目标值, 获取失败则返回 <see langword="null"/></returns>
public static T? GetTarget<T>(this WeakReference<T> weakReference)
where T : class
{
return weakReference.TryGetTarget(out var t) ? t : null;
}
/// <summary>
/// 枚举出带有索引值的枚举值
/// </summary>
/// <typeparam name="T">值类型</typeparam>
/// <param name="collection">集合</param>
/// <returns>带有索引的枚举值</returns>
public static IEnumerable<ItemInfo<T>> Enumerate<T>(this IEnumerable<T> collection)
{
var index = 0;
foreach (var item in collection)
yield return new(index++, item);
}
}
/// <summary>
/// 项信息
/// </summary>
/// <typeparam name="T"></typeparam>
[DebuggerDisplay("[{Index}, {Value}]")]
public readonly struct ItemInfo<T>
{
/// <summary>
/// 索引值
/// </summary>
public int Index { get; }
/// <summary>
/// 值
/// </summary>
public T Value { get; }
/// <inheritdoc/>
/// <param name="value">值</param>
/// <param name="index">索引值</param>
public ItemInfo(int index, T value)
{
Index = index;
Value = value;
}
/// <inheritdoc/>
public override string ToString()
{
return $"[{Index}, {Value}]";
}
} }

View File

@ -2,15 +2,16 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace VPet.ModMaker.Models; namespace VPet.ModMaker.Models;
[DebuggerDisplay("{Id}, Count = {Datas.Count}")]
public class I18nData public class I18nData
{ {
public ObservableValue<string> Id { get; } = new(); public ObservableValue<string> Id { get; } = new();
public ObservableCollection<string> Cultures { get; } = new();
public ObservableCollection<ObservableValue<string>> Datas { get; } = new(); public ObservableCollection<ObservableValue<string>> Datas { get; } = new();
} }

View File

@ -100,7 +100,7 @@ public class FoodModel : I18nModel<I18nFoodModel>
{ {
DescriptionId.Value = $"{n}_{nameof(DescriptionId)}"; DescriptionId.Value = $"{n}_{nameof(DescriptionId)}";
}; };
ReferencePrice.AddNotifyReceiver( ReferencePrice.AddNotifySender(
Strength, Strength,
StrengthFood, StrengthFood,
StrengthDrink, StrengthDrink,
@ -109,9 +109,9 @@ public class FoodModel : I18nModel<I18nFoodModel>
Likability, Likability,
Exp Exp
); );
ReferencePrice.NotifyReceived += (ref double v) => ReferencePrice.SenderPropertyChanged += (s, _) =>
{ {
v = Math.Floor(SetValueToFood(_food).RealPrice); s.Value = Math.Floor(SetValueToFood(_food).RealPrice);
}; };
} }

View File

@ -105,11 +105,11 @@ public class PetModel : I18nModel<I18nPetInfoModel>
PetNameId.Value = $"{n}_{nameof(PetNameId)}"; PetNameId.Value = $"{n}_{nameof(PetNameId)}";
DescriptionId.Value = $"{n}_{nameof(DescriptionId)}"; DescriptionId.Value = $"{n}_{nameof(DescriptionId)}";
}; };
AnimeCount.AddNotifyReceiver(Animes); AnimeCount.AddNotifySender(Animes);
AnimeCount.AddNotifyReceiver(FoodAnimes); AnimeCount.AddNotifySender(FoodAnimes);
AnimeCount.NotifyReceived += (ref int v) => AnimeCount.SenderPropertyChanged += (s, _) =>
{ {
v = Animes.Count + FoodAnimes.Count; s.Value = Animes.Count + FoodAnimes.Count;
}; };
} }

View File

@ -131,7 +131,7 @@ public class WorkModel : I18nModel<I18nWorkModel>
public WorkModel() public WorkModel()
{ {
IsOverLoad.AddNotifyReceiver( IsOverLoad.AddNotifySender(
WorkType, WorkType,
MoneyBase, MoneyBase,
MoneyLevel, MoneyLevel,
@ -141,9 +141,9 @@ public class WorkModel : I18nModel<I18nWorkModel>
LevelLimit, LevelLimit,
FinishBonus FinishBonus
); );
IsOverLoad.NotifyReceived += (ref bool v) => IsOverLoad.SenderPropertyChanged += (s, _) =>
{ {
v = VPet_Simulator.Windows.Interface.ExtensionFunction.IsOverLoad(ToWork()); s.Value = VPet_Simulator.Windows.Interface.ExtensionFunction.IsOverLoad(ToWork());
}; };
} }

View File

@ -65,50 +65,4 @@ public static class Utils
} }
return bitmapImage; return bitmapImage;
} }
/// <summary>
/// 枚举出带有索引值的枚举值
/// </summary>
/// <typeparam name="T">值类型</typeparam>
/// <param name="collection">集合</param>
/// <returns>带有索引的枚举值</returns>
public static IEnumerable<ItemInfo<T>> Enumerate<T>(this IEnumerable<T> collection)
{
var index = 0;
foreach (var item in collection)
yield return new(index++, item);
}
}
/// <summary>
/// 项信息
/// </summary>
/// <typeparam name="T"></typeparam>
[DebuggerDisplay("[{Index}, {Value}]")]
public readonly struct ItemInfo<T>
{
/// <summary>
/// 索引值
/// </summary>
public int Index { get; }
/// <summary>
/// 值
/// </summary>
public T Value { get; }
/// <inheritdoc/>
/// <param name="value">值</param>
/// <param name="index">索引值</param>
public ItemInfo(int index, T value)
{
Index = index;
Value = value;
}
/// <inheritdoc/>
public override string ToString()
{
return $"[{Index}, {Value}]";
}
} }

View File

@ -2,55 +2,55 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HKW.HKWViewModels.SimpleObservable; namespace HKW.HKWViewModels.SimpleObservable;
/// <summary> /// <summary>
/// 可观察值 /// 可观察值
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> [DebuggerDisplay("\\{ObservableValue, Value = {Value}\\}")]
[DebuggerDisplay("{Value}")] public class ObservableValue
public class ObservableValue<T>
: INotifyPropertyChanging, : INotifyPropertyChanging,
INotifyPropertyChanged, INotifyPropertyChanged,
IEquatable<ObservableValue<T>> IEquatable<ObservableValue>
{ {
[DebuggerBrowsable(DebuggerBrowsableState.Never)] [DebuggerBrowsable(DebuggerBrowsableState.Never)]
private T _value = default!; private object _value = default!;
/// <summary> /// <summary>
/// 当前值 /// 当前值
/// </summary> /// </summary>
public T Value public object Value
{ {
get => _value; get => _value;
set set
{ {
if (_value?.Equals(value) is true) if (_value?.Equals(value) is true)
return; return;
NotifyPropertyChanging();
var oldValue = _value; var oldValue = _value;
if (NotifyPropertyChanging(oldValue, value))
return;
_value = value; _value = value;
NotifyPropertyChanged(oldValue, value); NotifyPropertyChanged(oldValue!, value);
} }
} }
/// <summary> /// <summary>
/// /// 含值
/// </summary> /// </summary>
public bool HasValue => Value != null; public bool HasValue => Value != null;
/// <summary>
/// 唯一标识符
/// </summary>
public Guid Guid { get; } = Guid.NewGuid();
#region Ctor #region Ctor
/// <inheritdoc/> /// <inheritdoc/>
public ObservableValue() { } public ObservableValue() { }
/// <inheritdoc/> /// <inheritdoc/>
/// <param name="value">初始值</param> /// <param name="value">初始值</param>
public ObservableValue(T value) public ObservableValue(object value)
{ {
_value = value; _value = value;
} }
@ -60,16 +60,10 @@ public class ObservableValue<T>
/// <summary> /// <summary>
/// 通知属性改变前 /// 通知属性改变前
/// </summary> /// </summary>
/// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param>
/// <returns>取消改变</returns> /// <returns>取消改变</returns>
private bool NotifyPropertyChanging(T oldValue, T newValue) protected void NotifyPropertyChanging()
{ {
PropertyChanging?.Invoke(this, new(nameof(Value))); PropertyChanging?.Invoke(this, new(nameof(Value)));
var cancel = false;
// 若全部事件取消改变 则取消改变
ValueChanging?.Invoke(oldValue, newValue, ref cancel);
return cancel;
} }
/// <summary> /// <summary>
@ -77,60 +71,121 @@ public class ObservableValue<T>
/// </summary> /// </summary>
/// <param name="oldValue">旧值</param> /// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param> /// <param name="newValue">新值</param>
private void NotifyPropertyChanged(T oldValue, T newValue) protected void NotifyPropertyChanged(object oldValue, object newValue)
{ {
PropertyChanged?.Invoke(this, new(nameof(Value))); PropertyChanged?.Invoke(this, new(nameof(Value)));
ValueChanged?.Invoke(oldValue, newValue); if (oldValue is null || newValue is null)
PropertyChanged?.Invoke(this, new(nameof(HasValue)));
} }
#endregion #endregion
#region NotifyReceiver #region NotifySender
/// <summary> /// <summary>
/// 添加通知属性改变后接收器 /// 通知发送者
/// </summary>
public ICollection<ObservableValue> NotifySenders => _notifySenders.Values;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly Dictionary<Guid, ObservableValue> _notifySenders = new();
/// <summary>
/// 添加通知发送者
/// <para> /// <para>
/// 添加的接口触发后会执行 <see cref="NotifyReceived"/> /// 添加的发送者改变后会执行 <see cref="SenderPropertyChanged"/>
/// </para> /// </para>
/// <para>示例: /// <para>示例:
/// <code><![CDATA[ /// <code><![CDATA[
/// ObservableValue<string> value1 = new(); /// ObservableValue<string> value1 = new();
/// ObservableValue<string> value2 = new(); /// ObservableValue<string> value2 = new();
/// value2.AddNotifyReceiver(value1); /// value2.AddNotifySender(value1);
/// value2.NotifyReceived += (ref string v) => /// value2.NotifyReceived += (source, sender) =>
/// { /// {
/// v = "B"; // trigger this /// source.Value = sender.Value;
/// }; /// };
/// value1.Value = "A"; // execute this /// value1.Value = "A";
/// // result: value1.Value == "A" , value2.Value == "B" /// // value1.Value == "A", value2.Value == "A"
/// ]]> /// ]]>
/// </code></para> /// </code></para>
/// </summary> /// </summary>
/// <param name="notifies">通知属性改变后接口</param> /// <param name="items">发送者</param>
public void AddNotifyReceiver(params INotifyPropertyChanged[] notifies) public void AddNotifySender(params ObservableValue[] items)
{ {
foreach (var notify in notifies) foreach (var item in items)
notify.PropertyChanged += Notify_PropertyChanged; {
item.PropertyChanged += Notify_SenderPropertyChanged;
_notifySenders.Add(item.Guid, item);
}
} }
/// <summary> /// <summary>
/// 删除通知属性改变后接收器 /// 添加通知发送者
/// <para>
/// 注: 此方法添加的发送者不会被记录到 <see cref="NotifySenders"/> 中
/// </para>
/// </summary> /// </summary>
/// <param name="notifies">通知属性改变后接口</param> /// <param name="notices">发送者</param>
public void RemoveNotifyReceiver(params INotifyPropertyChanged[] notifies) public void AddNotifySender(params INotifyPropertyChanged[] notices)
{ {
foreach (var notify in notifies) foreach (var item in notices)
notify.PropertyChanged -= Notify_PropertyChanged; {
item.PropertyChanged += Notify_SenderPropertyChanged;
}
} }
private void Notify_PropertyChanged(object? sender, PropertyChangedEventArgs e) /// <summary>
/// 删除通知发送者
/// </summary>
/// <param name="items">发送者</param>
public void RemoveNotifySender(params ObservableValue[] items)
{ {
var temp = Value; foreach (var item in items)
NotifyReceived?.Invoke(ref temp); {
Value = temp; item.PropertyChanged -= Notify_SenderPropertyChanged;
_notifySenders.Remove(item.Guid);
}
} }
/// <summary>
/// 删除通知发送者
/// <para>
/// 注: 此方法删除的发送者不会从 <see cref="NotifySenders"/> 中删除
/// </para>
/// </summary>
/// <param name="notices">发送者</param>
public void RemoveNotifySender(params INotifyPropertyChanged[] notices)
{
foreach (var item in notices)
{
item.PropertyChanged -= Notify_SenderPropertyChanged;
}
}
/// <summary>
/// 清空通知发送者
/// </summary>
public void ClearNotifySender()
{
foreach (var sender in _notifySenders.Values)
sender.PropertyChanged -= Notify_SenderPropertyChanged;
_notifySenders.Clear();
}
private void Notify_SenderPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
NotifySenderPropertyChanged(this, sender);
}
/// <summary>
/// 通知接收事件
/// </summary>
/// <param name="source">源</param>
/// <param name="sender">发送者</param>
protected void NotifySenderPropertyChanged(ObservableValue source, object? sender)
{
SenderPropertyChanged?.Invoke(source, sender);
}
#endregion #endregion
#region Other #region Other
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString()
@ -141,7 +196,7 @@ public class ObservableValue<T>
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object? obj) public override bool Equals(object? obj)
{ {
return Equals(obj as ObservableValue<T>); return Equals(obj as ObservableValue);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -151,9 +206,9 @@ public class ObservableValue<T>
} }
/// <inheritdoc/> /// <inheritdoc/>
public bool Equals(ObservableValue<T>? other) public bool Equals(ObservableValue? other)
{ {
return Value?.Equals(other) is true; return Guid.Equals(other?.Guid) is true;
} }
/// <summary> /// <summary>
@ -162,7 +217,7 @@ public class ObservableValue<T>
/// <param name="value1">左值</param> /// <param name="value1">左值</param>
/// <param name="value2">右值</param> /// <param name="value2">右值</param>
/// <returns>相等为 <see langword="true"/> 否则为 <see langword="false"/></returns> /// <returns>相等为 <see langword="true"/> 否则为 <see langword="false"/></returns>
public static bool operator ==(ObservableValue<T> value1, ObservableValue<T> value2) public static bool operator ==(ObservableValue value1, ObservableValue value2)
{ {
return value1.Value?.Equals(value2.Value) is true; return value1.Value?.Equals(value2.Value) is true;
} }
@ -173,7 +228,7 @@ public class ObservableValue<T>
/// <param name="value1">左值</param> /// <param name="value1">左值</param>
/// <param name="value2">右值</param> /// <param name="value2">右值</param>
/// <returns>不相等为 <see langword="true"/> 否则为 <see langword="false"/></returns> /// <returns>不相等为 <see langword="true"/> 否则为 <see langword="false"/></returns>
public static bool operator !=(ObservableValue<T> value1, ObservableValue<T> value2) public static bool operator !=(ObservableValue value1, ObservableValue value2)
{ {
return value1.Value?.Equals(value2.Value) is not true; return value1.Value?.Equals(value2.Value) is not true;
} }
@ -192,41 +247,17 @@ public class ObservableValue<T>
public event PropertyChangedEventHandler? PropertyChanged; public event PropertyChangedEventHandler? PropertyChanged;
/// <summary> /// <summary>
/// 值改变前事件 /// 通知接收事件
/// </summary> /// </summary>
public event ValueChangingEventHandler? ValueChanging; public event NotifySenderPropertyChangedHandler? SenderPropertyChanged;
/// <summary>
/// 值改变后事件
/// </summary>
public event ValueChangedEventHandler? ValueChanged;
/// <summary>
/// 通知接收器事件
/// </summary>
public event NotifyReceivedHandler? NotifyReceived;
#endregion #endregion
#region Delegate #region Delegate
/// <summary> /// <summary>
/// 值改变事件 /// 通知发送者属性改变接收器
/// </summary> /// </summary>
/// <param name="oldValue">旧值</param> /// <param name="source">源</param>
/// <param name="newValue">新值</param> /// <param name="sender">发送者</param>
/// <param name="cancel">取消</param> public delegate void NotifySenderPropertyChangedHandler(ObservableValue source, object? sender);
public delegate void ValueChangingEventHandler(T oldValue, T newValue, ref bool cancel);
/// <summary>
/// 值改变后事件
/// </summary>
/// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param>
public delegate void ValueChangedEventHandler(T oldValue, T newValue);
/// <summary>
/// 通知接收器
/// </summary>
/// <param name="value">引用值</param>
public delegate void NotifyReceivedHandler(ref T value);
#endregion #endregion
} }

View File

@ -0,0 +1,149 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HKW.HKWViewModels.SimpleObservable;
/// <summary>
/// 可观察值组合
/// <para>示例:<code><![CDATA[
/// var value1 = new ObservableValue<string>();
/// var value2 = new ObservableValue<string>();
/// var group = new ObservableValueGroup<string>() { value1, value2 };
/// value1.Value = "A";
/// // value1 == "A", value2 == "A"
/// group.Remove(value1);
/// value1.Value = "C";
/// // value1 == "C", value2 == "A"]]></code></para>
/// </summary>
/// <typeparam name="T">值类型</typeparam>
[DebuggerDisplay("\\{ObservableValueGroup, Count = {Count}\\}")]
public class ObservableValueGroup<T> : IEnumerable<ObservableValue<T>?>
{
/// <summary>
/// 数量
/// </summary>
public int Count => _bindingValues.Count;
/// <summary>
/// 在添加的时候改变值 (如果分组中存在值)
/// </summary>
[DefaultValue(false)]
public bool ChangeOnAdd { get; set; } = false;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly Dictionary<Guid, WeakReference<ObservableValue<T>>> _bindingValues = new();
/// <summary>
/// 添加项
/// </summary>
/// <param name="items">项</param>
public void Add(params ObservableValue<T>[] items)
{
foreach (var item in items)
AddX(item);
}
private void AddX(ObservableValue<T> item)
{
if (item.Group is not null)
throw new ArgumentException("item.Group must be null", nameof(item));
_bindingValues.Add(item.Guid, new(item));
item.ValueChanged -= Item_ValueChanged;
if (ChangeOnAdd)
{
foreach (var bindingValue in _bindingValues)
{
if (bindingValue.Value.TryGetTarget(out var target))
{
item.Value = target.Value;
break;
}
}
}
item.ValueChanged += Item_ValueChanged;
item.Group = this;
}
/// <summary>
/// 删除项
/// </summary>
/// <param name="items">项</param>
public void Remove(params ObservableValue<T>[] items)
{
foreach (var item in items)
RemoveX(item);
}
private void RemoveX(ObservableValue<T> item)
{
var result = _bindingValues.Remove(item.Guid);
if (result)
{
item.ValueChanged -= Item_ValueChanged;
item.Group = null;
}
}
/// <summary>
/// 清空分组
/// </summary>
public void Clear()
{
foreach (var bindingValue in _bindingValues)
{
if (bindingValue.Value.TryGetTarget(out var target))
{
target.ValueChanged -= Item_ValueChanged;
target.Group = null;
}
}
_bindingValues.Clear();
}
/// <summary>
/// 查找项
/// </summary>
/// <param name="item">项</param>
/// <returns>包含为 <see langword="true"/> 不包含为 <see langword="false"/></returns>
public bool Contains(ObservableValue<T> item)
{
return _bindingValues.ContainsKey(item.Guid);
}
/// <inheritdoc/>
public IEnumerator<ObservableValue<T>?> GetEnumerator()
{
return _bindingValues.Values
.Select(v => v.TryGetTarget(out var t) ? t : null)
.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private bool _onChange = false;
private void Item_ValueChanged(T oldValue, T newValue)
{
if (_onChange)
return;
_onChange = true;
foreach (var bindingValue in _bindingValues.AsEnumerable())
{
if (bindingValue.Value.TryGetTarget(out var target))
target.Value = newValue;
else
_bindingValues.Remove(bindingValue.Key);
}
_onChange = false;
}
}

View File

@ -0,0 +1,204 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HKW.HKWViewModels.SimpleObservable;
/// <summary>
/// 可观察值
/// </summary>
/// <typeparam name="T"></typeparam>
[DebuggerDisplay("\\{ObservableValue, Value = {Value}\\}")]
public class ObservableValue<T> : ObservableValue, IEquatable<ObservableValue<T>>
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private T _value = default!;
/// <inheritdoc cref=" ObservableValue.Value"/>
public new T Value
{
get => _value;
set
{
if (_value?.Equals(value) is true)
return;
var oldValue = _value;
if (NotifyPropertyChanging(oldValue, value))
return;
_value = value;
NotifyPropertyChanged(oldValue, value);
}
}
/// <summary>
/// 分组
/// </summary>
public ObservableValueGroup<T>? Group { get; internal set; }
#region Ctor
/// <inheritdoc/>
public ObservableValue() { }
/// <inheritdoc/>
/// <param name="value">初始值</param>
public ObservableValue(T value)
{
_value = value;
}
#endregion
#region NotifyProperty
/// <summary>
/// 通知属性改变前
/// </summary>
/// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param>
/// <returns>取消改变</returns>
private bool NotifyPropertyChanging(T oldValue, T newValue)
{
NotifyPropertyChanging();
var cancel = false;
// 若全部事件取消改变 则取消改变
ValueChanging?.Invoke(oldValue, newValue, ref cancel);
return cancel;
}
/// <summary>
/// 通知属性改变后
/// </summary>
/// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param>
private void NotifyPropertyChanged(T oldValue, T newValue)
{
base.NotifyPropertyChanged(oldValue!, newValue!);
ValueChanged?.Invoke(oldValue, newValue);
}
#endregion
#region NotifySender
/// <summary>
/// 通知发送者
/// </summary>
public new ICollection<ObservableValue<T>> NotifySenders => _notifySenders.Values;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly Dictionary<Guid, ObservableValue<T>> _notifySenders = new();
/// <inheritdoc cref=" ObservableValue.AddNotifySender(ObservableValue[])"/>
public void AddNotifySender(params ObservableValue<T>[] items)
{
foreach (var item in items)
{
item.PropertyChanged += NotifySenderPropertyChanged;
_notifySenders.Add(item.Guid, item);
}
}
/// <inheritdoc cref=" ObservableValue.RemoveNotifySender(ObservableValue[])"/>
public void RemoveNotifySender(params ObservableValue<T>[] items)
{
foreach (var item in items)
{
item.PropertyChanged -= NotifySenderPropertyChanged;
_notifySenders.Remove(item.Guid);
}
}
/// <inheritdoc cref=" ObservableValue.ClearNotifySender"/>
public new void ClearNotifySender()
{
foreach (var sender in _notifySenders.Values)
sender.PropertyChanged -= NotifySenderPropertyChanged;
_notifySenders.Clear();
}
private void NotifySenderPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
NotifySenderPropertyChanged(this, sender);
}
#endregion
#region Other
/// <inheritdoc/>
public override string ToString()
{
return Value?.ToString() ?? string.Empty;
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return Equals(obj as ObservableValue<T>);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return Value?.GetHashCode() ?? 0;
}
/// <inheritdoc/>
public bool Equals(ObservableValue<T>? other)
{
return Guid.Equals(other?.Guid) is true;
}
/// <summary>
/// 判断 <see cref="Value"/> 相等
/// </summary>
/// <param name="value1">左值</param>
/// <param name="value2">右值</param>
/// <returns>相等为 <see langword="true"/> 否则为 <see langword="false"/></returns>
public static bool operator ==(ObservableValue<T> value1, ObservableValue<T> value2)
{
return value1.Value?.Equals(value2.Value) is true;
}
/// <summary>
/// 判断 <see cref="Value"/> 不相等
/// </summary>
/// <param name="value1">左值</param>
/// <param name="value2">右值</param>
/// <returns>不相等为 <see langword="true"/> 否则为 <see langword="false"/></returns>
public static bool operator !=(ObservableValue<T> value1, ObservableValue<T> value2)
{
return value1.Value?.Equals(value2.Value) is not true;
}
#endregion
#region Event
/// <summary>
/// 值改变前事件
/// </summary>
public event ValueChangingEventHandler? ValueChanging;
/// <summary>
/// 值改变后事件
/// </summary>
public event ValueChangedEventHandler? ValueChanged;
#endregion
#region Delegate
/// <summary>
/// 值改变事件
/// </summary>
/// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param>
/// <param name="cancel">取消</param>
public delegate void ValueChangingEventHandler(T oldValue, T newValue, ref bool cancel);
/// <summary>
/// 值改变后事件
/// </summary>
/// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param>
public delegate void ValueChangedEventHandler(T oldValue, T newValue);
#endregion
}

View File

@ -126,6 +126,8 @@
<Compile Include="Models\ModModel\WorkModel.cs" /> <Compile Include="Models\ModModel\WorkModel.cs" />
<Compile Include="SimpleObservable\ObservableCommandT.cs" /> <Compile Include="SimpleObservable\ObservableCommandT.cs" />
<Compile Include="ModMakerStyles.cs" /> <Compile Include="ModMakerStyles.cs" />
<Compile Include="SimpleObservable\ObservableValueGroup.cs" />
<Compile Include="SimpleObservable\ObservableValueT.cs" />
<Compile Include="ViewModels\ModEdit\AddCultureWindowVM.cs" /> <Compile Include="ViewModels\ModEdit\AddCultureWindowVM.cs" />
<Compile Include="ViewModels\ModEdit\AnimeEdit\FoodAnimeEditWindowVM.cs" /> <Compile Include="ViewModels\ModEdit\AnimeEdit\FoodAnimeEditWindowVM.cs" />
<Compile Include="ViewModels\ModEdit\AnimeEdit\AnimeEditWindowVM.cs" /> <Compile Include="ViewModels\ModEdit\AnimeEdit\AnimeEditWindowVM.cs" />

View File

@ -8,7 +8,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pu="https://opensource.panuon.com/wpf-ui" xmlns:pu="https://opensource.panuon.com/wpf-ui"
xmlns:vm="clr-namespace:VPet.ModMaker.ViewModels.ModEdit.I18nEdit" xmlns:vm="clr-namespace:VPet.ModMaker.ViewModels.ModEdit.I18nEdit"
Title="I18nEditWindow" Title="{ll:Str 多语言编辑器}"
Width="800" Width="800"
Height="450" Height="450"
d:DataContext="{d:DesignInstance Type=vm:I18nEditWindowVM}" d:DataContext="{d:DesignInstance Type=vm:I18nEditWindowVM}"
@ -20,10 +20,20 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition /> <RowDefinition />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBox <Grid>
pu:TextBoxHelper.Watermark="{ll:Str 搜索Id}" <Grid.ColumnDefinitions>
Style="{DynamicResource StandardTextBoxStyle}" <ColumnDefinition />
Text="{Binding Search.Value, UpdateSourceTrigger=PropertyChanged}" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox
pu:TextBoxHelper.Watermark="{ll:Str 搜索}"
Style="{DynamicResource StandardTextBoxStyle}"
Text="{Binding Search.Value, UpdateSourceTrigger=PropertyChanged}" />
<ComboBox
Grid.Column="1"
ItemsSource="{Binding SearchTargets}"
SelectedItem="{Binding SearchTarget.Value}" />
</Grid>
<DataGrid <DataGrid
x:Name="DataGrid_Datas" x:Name="DataGrid_Datas"
Grid.Row="1" Grid.Row="1"

View File

@ -28,11 +28,25 @@ public partial class I18nEditWindow : WindowX
{ {
ShowI18nDatas.Value = I18nDatas; ShowI18nDatas.Value = I18nDatas;
} }
else else if (SearchTarget.Value == nameof(ModInfoModel.Id))
{ {
ShowI18nDatas.Value = new( ShowI18nDatas.Value = new(
I18nDatas.Where( I18nDatas.Where(
m => m.Id.Value.Contains(newValue, StringComparison.OrdinalIgnoreCase) m => m.Id.Value?.Contains(newValue, StringComparison.OrdinalIgnoreCase) is true
)
);
}
else
{
var cultureIndex = I18nHelper.Current.CultureNames.IndexOf(SearchTarget.Value);
ShowI18nDatas.Value = new(
I18nDatas.Where(
m =>
m.Datas[cultureIndex].Value?.Contains(
newValue,
StringComparison.OrdinalIgnoreCase
)
is true
) )
); );
} }
@ -66,6 +80,7 @@ public partial class I18nEditWindow : WindowX
foreach (var culture in I18nHelper.Current.CultureNames) foreach (var culture in I18nHelper.Current.CultureNames)
{ {
AddCulture(culture); AddCulture(culture);
SearchTargets.Add(culture);
} }
LoadFood(model); LoadFood(model);
LoadClickText(model); LoadClickText(model);
@ -78,19 +93,54 @@ public partial class I18nEditWindow : WindowX
{ {
foreach (var food in model.Foods) foreach (var food in model.Foods)
{ {
var nameData = new I18nData(); if (AllData.TryGetValue(food.Id.Value, out var outData))
var descriptionData = new I18nData();
nameData.Id.Value = food.Id.Value;
descriptionData.Id.Value = food.DescriptionId.Value;
foreach (var culture in I18nHelper.Current.CultureNames)
{ {
nameData.Cultures.Add(culture); foreach (var culture in I18nHelper.Current.CultureNames.Enumerate())
nameData.Datas.Add(food.I18nDatas[culture].Name); {
descriptionData.Cultures.Add(culture); if (outData.Datas[culture.Index].Group is null)
descriptionData.Datas.Add(food.I18nDatas[culture].Description); {
var group = new ObservableValueGroup<string>()
{
outData.Datas[culture.Index]
};
}
outData.Datas[culture.Index].Group!.Add(food.I18nDatas[culture.Value].Name);
}
}
else
{
var data = new I18nData();
data.Id.Value = food.Id.Value;
foreach (var culture in I18nHelper.Current.CultureNames)
data.Datas.Add(food.I18nDatas[culture].Name);
I18nDatas.Add(data);
AllData.Add(food.Id.Value, data);
}
if (AllData.TryGetValue(food.DescriptionId.Value, out var outData1))
{
foreach (var culture in I18nHelper.Current.CultureNames.Enumerate())
{
if (outData1.Datas[culture.Index].Group is null)
{
var group = new ObservableValueGroup<string>()
{
outData1.Datas[culture.Index]
};
}
outData1.Datas[culture.Index].Group!.Add(
food.I18nDatas[culture.Value].Description
);
}
}
else
{
var data = new I18nData();
data.Id.Value = food.DescriptionId.Value;
foreach (var culture in I18nHelper.Current.CultureNames)
data.Datas.Add(food.I18nDatas[culture].Description);
I18nDatas.Add(data);
AllData.Add(food.DescriptionId.Value, data);
} }
I18nDatas.Add(nameData);
I18nDatas.Add(descriptionData);
} }
} }
@ -98,14 +148,29 @@ public partial class I18nEditWindow : WindowX
{ {
foreach (var text in model.ClickTexts) foreach (var text in model.ClickTexts)
{ {
var data = new I18nData(); if (AllData.TryGetValue(text.Id.Value, out var outData))
data.Id.Value = text.Id.Value;
foreach (var culture in I18nHelper.Current.CultureNames)
{ {
data.Cultures.Add(culture); foreach (var culture in I18nHelper.Current.CultureNames.Enumerate())
data.Datas.Add(text.I18nDatas[culture].Text); {
if (outData.Datas[culture.Index].Group is null)
{
var group = new ObservableValueGroup<string>()
{
outData.Datas[culture.Index]
};
}
outData.Datas[culture.Index].Group!.Add(text.I18nDatas[culture.Value].Text);
}
}
else
{
var data = new I18nData();
data.Id.Value = text.Id.Value;
foreach (var culture in I18nHelper.Current.CultureNames)
data.Datas.Add(text.I18nDatas[culture].Text);
I18nDatas.Add(data);
AllData.Add(text.Id.Value, data);
} }
I18nDatas.Add(data);
} }
} }
@ -113,14 +178,29 @@ public partial class I18nEditWindow : WindowX
{ {
foreach (var text in model.LowTexts) foreach (var text in model.LowTexts)
{ {
var data = new I18nData(); if (AllData.TryGetValue(text.Id.Value, out var outData))
data.Id.Value = text.Id.Value;
foreach (var culture in I18nHelper.Current.CultureNames)
{ {
data.Cultures.Add(culture); foreach (var culture in I18nHelper.Current.CultureNames.Enumerate())
data.Datas.Add(text.I18nDatas[culture].Text); {
if (outData.Datas[culture.Index].Group is null)
{
var group = new ObservableValueGroup<string>()
{
outData.Datas[culture.Index]
};
}
outData.Datas[culture.Index].Group!.Add(text.I18nDatas[culture.Value].Text);
}
}
else
{
var data = new I18nData();
data.Id.Value = text.Id.Value;
foreach (var culture in I18nHelper.Current.CultureNames)
data.Datas.Add(text.I18nDatas[culture].Text);
I18nDatas.Add(data);
AllData.Add(text.Id.Value, data);
} }
I18nDatas.Add(data);
} }
} }
@ -128,19 +208,52 @@ public partial class I18nEditWindow : WindowX
{ {
foreach (var text in model.SelectTexts) foreach (var text in model.SelectTexts)
{ {
var data = new I18nData(); if (AllData.TryGetValue(text.Id.Value, out var outData))
var chooseData = new I18nData();
data.Id.Value = text.Id.Value;
chooseData.Id.Value = text.ChooseId.Value;
foreach (var culture in I18nHelper.Current.CultureNames)
{ {
data.Cultures.Add(culture); foreach (var culture in I18nHelper.Current.CultureNames.Enumerate())
data.Datas.Add(text.I18nDatas[culture].Text); {
chooseData.Cultures.Add(culture); if (outData.Datas[culture.Index].Group is null)
chooseData.Datas.Add(text.I18nDatas[culture].Choose); {
var group = new ObservableValueGroup<string>()
{
outData.Datas[culture.Index]
};
}
outData.Datas[culture.Index].Group!.Add(text.I18nDatas[culture.Value].Text);
}
}
else
{
var data = new I18nData();
data.Id.Value = text.Id.Value;
foreach (var culture in I18nHelper.Current.CultureNames)
data.Datas.Add(text.I18nDatas[culture].Text);
I18nDatas.Add(data);
AllData.Add(text.Id.Value, data);
}
if (AllData.TryGetValue(text.ChooseId.Value, out var outData1))
{
foreach (var culture in I18nHelper.Current.CultureNames.Enumerate())
{
if (outData1.Datas[culture.Index].Group is null)
{
var group = new ObservableValueGroup<string>()
{
outData1.Datas[culture.Index]
};
}
outData1.Datas[culture.Index].Group!.Add(text.I18nDatas[culture.Value].Choose);
}
}
else
{
var data = new I18nData();
data.Id.Value = text.ChooseId.Value;
foreach (var culture in I18nHelper.Current.CultureNames)
data.Datas.Add(text.I18nDatas[culture].Choose);
I18nDatas.Add(data);
AllData.Add(text.ChooseId.Value, data);
} }
I18nDatas.Add(data);
I18nDatas.Add(chooseData);
} }
} }
@ -148,32 +261,112 @@ public partial class I18nEditWindow : WindowX
{ {
foreach (var pet in model.Pets) foreach (var pet in model.Pets)
{ {
var data = new I18nData(); if (pet.IsSimplePetModel)
var petNameData = new I18nData(); continue;
var descriptionData = new I18nData(); if (AllData.TryGetValue(pet.Id.Value, out var outData))
data.Id.Value = pet.Id.Value;
petNameData.Id.Value = pet.PetNameId.Value;
descriptionData.Id.Value = pet.DescriptionId.Value;
foreach (var culture in I18nHelper.Current.CultureNames)
{ {
data.Cultures.Add(culture); foreach (var culture in I18nHelper.Current.CultureNames.Enumerate())
data.Datas.Add(pet.I18nDatas[culture].Name); {
petNameData.Cultures.Add(culture); if (outData.Datas[culture.Index].Group is null)
petNameData.Datas.Add(pet.I18nDatas[culture].PetName); {
descriptionData.Cultures.Add(culture); var group = new ObservableValueGroup<string>()
descriptionData.Datas.Add(pet.I18nDatas[culture].Description); {
outData.Datas[culture.Index]
};
}
outData.Datas[culture.Index].Group!.Add(pet.I18nDatas[culture.Value].Name);
}
} }
I18nDatas.Add(data); else
I18nDatas.Add(petNameData); {
I18nDatas.Add(descriptionData); var data = new I18nData();
data.Id.Value = pet.Id.Value;
foreach (var culture in I18nHelper.Current.CultureNames)
data.Datas.Add(pet.I18nDatas[culture].Name);
I18nDatas.Add(data);
AllData.Add(pet.Id.Value, data);
}
if (AllData.TryGetValue(pet.PetNameId.Value, out var outData1))
{
foreach (var culture in I18nHelper.Current.CultureNames.Enumerate())
{
if (outData1.Datas[culture.Index].Group is null)
{
var group = new ObservableValueGroup<string>()
{
outData1.Datas[culture.Index]
};
}
outData1.Datas[culture.Index].Group!.Add(pet.I18nDatas[culture.Value].PetName);
}
}
else
{
var data = new I18nData();
data.Id.Value = pet.PetNameId.Value;
foreach (var culture in I18nHelper.Current.CultureNames)
data.Datas.Add(pet.I18nDatas[culture].PetName);
I18nDatas.Add(data);
AllData.Add(pet.PetNameId.Value, data);
}
if (AllData.TryGetValue(pet.DescriptionId.Value, out var outData2))
{
foreach (var culture in I18nHelper.Current.CultureNames.Enumerate())
{
if (outData2.Datas[culture.Index].Group is null)
{
var group = new ObservableValueGroup<string>()
{
outData2.Datas[culture.Index]
};
}
outData2.Datas[culture.Index].Group!.Add(
pet.I18nDatas[culture.Value].Description
);
}
}
else
{
var data = new I18nData();
data.Id.Value = pet.DescriptionId.Value;
foreach (var culture in I18nHelper.Current.CultureNames)
data.Datas.Add(pet.I18nDatas[culture].Description);
I18nDatas.Add(data);
AllData.Add(pet.DescriptionId.Value, data);
}
//var data = new I18nData();
//var petNameData = new I18nData();
//var descriptionData = new I18nData();
//data.Id.Value = pet.Id.Value;
//petNameData.Id.Value = pet.PetNameId.Value;
//descriptionData.Id.Value = pet.DescriptionId.Value;
//foreach (var culture in I18nHelper.Current.CultureNames)
//{
// data.Datas.Add(pet.I18nDatas[culture].Name);
// petNameData.Datas.Add(pet.I18nDatas[culture].PetName);
// descriptionData.Datas.Add(pet.I18nDatas[culture].Description);
//}
//I18nDatas.Add(data);
//I18nDatas.Add(petNameData);
//I18nDatas.Add(descriptionData);
} }
} }
private readonly Dictionary<string, DataGridTextColumn> _dataGridI18nColumns = new(); private readonly Dictionary<string, DataGridTextColumn> _dataGridI18nColumns = new();
public HashSet<string> Ids { get; } = new(); public Dictionary<string, I18nData> AllData { get; } = new();
public ObservableCollection<I18nData> I18nDatas { get; } = new(); public ObservableCollection<I18nData> I18nDatas { get; } = new();
public ObservableValue<ObservableCollection<I18nData>> ShowI18nDatas { get; } = new(); public ObservableValue<ObservableCollection<I18nData>> ShowI18nDatas { get; } = new();
/// <summary>
/// 搜索目标列表
/// </summary>
public ObservableCollection<string> SearchTargets { get; } = new() { nameof(ModInfoModel.Id) };
/// <summary>
/// 搜索目标
/// </summary>
public ObservableValue<string> SearchTarget { get; } = new();
#region CultureEdit #region CultureEdit
// TODO: 国际化标头 // TODO: 国际化标头
private const string ValueBindingFormat = "Datas[{0}].Value"; private const string ValueBindingFormat = "Datas[{0}].Value";