修复 多语言编辑器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.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
@ -202,4 +203,62 @@ public static class Extensions
{
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.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VPet.ModMaker.Models;
[DebuggerDisplay("{Id}, Count = {Datas.Count}")]
public class I18nData
{
public ObservableValue<string> Id { get; } = new();
public ObservableCollection<string> Cultures { 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)}";
};
ReferencePrice.AddNotifyReceiver(
ReferencePrice.AddNotifySender(
Strength,
StrengthFood,
StrengthDrink,
@ -109,9 +109,9 @@ public class FoodModel : I18nModel<I18nFoodModel>
Likability,
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)}";
DescriptionId.Value = $"{n}_{nameof(DescriptionId)}";
};
AnimeCount.AddNotifyReceiver(Animes);
AnimeCount.AddNotifyReceiver(FoodAnimes);
AnimeCount.NotifyReceived += (ref int v) =>
AnimeCount.AddNotifySender(Animes);
AnimeCount.AddNotifySender(FoodAnimes);
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()
{
IsOverLoad.AddNotifyReceiver(
IsOverLoad.AddNotifySender(
WorkType,
MoneyBase,
MoneyLevel,
@ -141,9 +141,9 @@ public class WorkModel : I18nModel<I18nWorkModel>
LevelLimit,
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;
}
/// <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.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("{Value}")]
public class ObservableValue<T>
[DebuggerDisplay("\\{ObservableValue, Value = {Value}\\}")]
public class ObservableValue
: INotifyPropertyChanging,
INotifyPropertyChanged,
IEquatable<ObservableValue<T>>
IEquatable<ObservableValue>
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private T _value = default!;
private object _value = default!;
/// <summary>
/// 当前值
/// </summary>
public T Value
public object Value
{
get => _value;
set
{
if (_value?.Equals(value) is true)
return;
NotifyPropertyChanging();
var oldValue = _value;
if (NotifyPropertyChanging(oldValue, value))
return;
_value = value;
NotifyPropertyChanged(oldValue, value);
NotifyPropertyChanged(oldValue!, value);
}
}
/// <summary>
///
/// 含值
/// </summary>
public bool HasValue => Value != null;
/// <summary>
/// 唯一标识符
/// </summary>
public Guid Guid { get; } = Guid.NewGuid();
#region Ctor
/// <inheritdoc/>
public ObservableValue() { }
/// <inheritdoc/>
/// <param name="value">初始值</param>
public ObservableValue(T value)
public ObservableValue(object value)
{
_value = value;
}
@ -60,16 +60,10 @@ public class ObservableValue<T>
/// <summary>
/// 通知属性改变前
/// </summary>
/// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param>
/// <returns>取消改变</returns>
private bool NotifyPropertyChanging(T oldValue, T newValue)
protected void NotifyPropertyChanging()
{
PropertyChanging?.Invoke(this, new(nameof(Value)));
var cancel = false;
// 若全部事件取消改变 则取消改变
ValueChanging?.Invoke(oldValue, newValue, ref cancel);
return cancel;
}
/// <summary>
@ -77,60 +71,121 @@ public class ObservableValue<T>
/// </summary>
/// <param name="oldValue">旧值</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)));
ValueChanged?.Invoke(oldValue, newValue);
if (oldValue is null || newValue is null)
PropertyChanged?.Invoke(this, new(nameof(HasValue)));
}
#endregion
#region NotifyReceiver
#region NotifySender
/// <summary>
/// 添加通知属性改变后接收器
/// 通知发送者
/// </summary>
public ICollection<ObservableValue> NotifySenders => _notifySenders.Values;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly Dictionary<Guid, ObservableValue> _notifySenders = new();
/// <summary>
/// 添加通知发送者
/// <para>
/// 添加的接口触发后会执行 <see cref="NotifyReceived"/>
/// 添加的发送者改变后会执行 <see cref="SenderPropertyChanged"/>
/// </para>
/// <para>示例:
/// <code><![CDATA[
/// ObservableValue<string> value1 = new();
/// ObservableValue<string> value2 = new();
/// value2.AddNotifyReceiver(value1);
/// value2.NotifyReceived += (ref string v) =>
/// value2.AddNotifySender(value1);
/// value2.NotifyReceived += (source, sender) =>
/// {
/// v = "B"; // trigger this
/// source.Value = sender.Value;
/// };
/// value1.Value = "A"; // execute this
/// // result: value1.Value == "A" , value2.Value == "B"
/// value1.Value = "A";
/// // value1.Value == "A", value2.Value == "A"
/// ]]>
/// </code></para>
/// </summary>
/// <param name="notifies">通知属性改变后接口</param>
public void AddNotifyReceiver(params INotifyPropertyChanged[] notifies)
/// <param name="items">发送者</param>
public void AddNotifySender(params ObservableValue[] items)
{
foreach (var notify in notifies)
notify.PropertyChanged += Notify_PropertyChanged;
foreach (var item in items)
{
item.PropertyChanged += Notify_SenderPropertyChanged;
_notifySenders.Add(item.Guid, item);
}
}
/// <summary>
/// 删除通知属性改变后接收器
/// 添加通知发送者
/// <para>
/// 注: 此方法添加的发送者不会被记录到 <see cref="NotifySenders"/> 中
/// </para>
/// </summary>
/// <param name="notifies">通知属性改变后接口</param>
public void RemoveNotifyReceiver(params INotifyPropertyChanged[] notifies)
/// <param name="notices">发送者</param>
public void AddNotifySender(params INotifyPropertyChanged[] notices)
{
foreach (var notify in notifies)
notify.PropertyChanged -= Notify_PropertyChanged;
foreach (var item in notices)
{
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;
NotifyReceived?.Invoke(ref temp);
Value = temp;
foreach (var item in items)
{
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
#region Other
/// <inheritdoc/>
public override string ToString()
@ -141,7 +196,7 @@ public class ObservableValue<T>
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return Equals(obj as ObservableValue<T>);
return Equals(obj as ObservableValue);
}
/// <inheritdoc/>
@ -151,9 +206,9 @@ public class ObservableValue<T>
}
/// <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>
@ -162,7 +217,7 @@ public class ObservableValue<T>
/// <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)
public static bool operator ==(ObservableValue value1, ObservableValue value2)
{
return value1.Value?.Equals(value2.Value) is true;
}
@ -173,7 +228,7 @@ public class ObservableValue<T>
/// <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)
public static bool operator !=(ObservableValue value1, ObservableValue value2)
{
return value1.Value?.Equals(value2.Value) is not true;
}
@ -192,41 +247,17 @@ public class ObservableValue<T>
public event PropertyChangedEventHandler? PropertyChanged;
/// <summary>
/// 值改变前事件
/// 通知接收事件
/// </summary>
public event ValueChangingEventHandler? ValueChanging;
/// <summary>
/// 值改变后事件
/// </summary>
public event ValueChangedEventHandler? ValueChanged;
/// <summary>
/// 通知接收器事件
/// </summary>
public event NotifyReceivedHandler? NotifyReceived;
public event NotifySenderPropertyChangedHandler? SenderPropertyChanged;
#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);
/// <summary>
/// 通知接收器
/// </summary>
/// <param name="value">引用值</param>
public delegate void NotifyReceivedHandler(ref T value);
/// <param name="source">源</param>
/// <param name="sender">发送者</param>
public delegate void NotifySenderPropertyChangedHandler(ObservableValue source, object? sender);
#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="SimpleObservable\ObservableCommandT.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\AnimeEdit\FoodAnimeEditWindowVM.cs" />
<Compile Include="ViewModels\ModEdit\AnimeEdit\AnimeEditWindowVM.cs" />

View File

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

View File

@ -28,11 +28,25 @@ public partial class I18nEditWindow : WindowX
{
ShowI18nDatas.Value = I18nDatas;
}
else
else if (SearchTarget.Value == nameof(ModInfoModel.Id))
{
ShowI18nDatas.Value = new(
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)
{
AddCulture(culture);
SearchTargets.Add(culture);
}
LoadFood(model);
LoadClickText(model);
@ -78,19 +93,54 @@ public partial class I18nEditWindow : WindowX
{
foreach (var food in model.Foods)
{
var nameData = new I18nData();
var descriptionData = new I18nData();
nameData.Id.Value = food.Id.Value;
descriptionData.Id.Value = food.DescriptionId.Value;
foreach (var culture in I18nHelper.Current.CultureNames)
if (AllData.TryGetValue(food.Id.Value, out var outData))
{
nameData.Cultures.Add(culture);
nameData.Datas.Add(food.I18nDatas[culture].Name);
descriptionData.Cultures.Add(culture);
descriptionData.Datas.Add(food.I18nDatas[culture].Description);
foreach (var culture in I18nHelper.Current.CultureNames.Enumerate())
{
if (outData.Datas[culture.Index].Group is null)
{
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)
{
var data = new I18nData();
data.Id.Value = text.Id.Value;
foreach (var culture in I18nHelper.Current.CultureNames)
if (AllData.TryGetValue(text.Id.Value, out var outData))
{
data.Cultures.Add(culture);
data.Datas.Add(text.I18nDatas[culture].Text);
foreach (var culture in I18nHelper.Current.CultureNames.Enumerate())
{
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)
{
var data = new I18nData();
data.Id.Value = text.Id.Value;
foreach (var culture in I18nHelper.Current.CultureNames)
if (AllData.TryGetValue(text.Id.Value, out var outData))
{
data.Cultures.Add(culture);
data.Datas.Add(text.I18nDatas[culture].Text);
foreach (var culture in I18nHelper.Current.CultureNames.Enumerate())
{
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)
{
var data = new I18nData();
var chooseData = new I18nData();
data.Id.Value = text.Id.Value;
chooseData.Id.Value = text.ChooseId.Value;
foreach (var culture in I18nHelper.Current.CultureNames)
if (AllData.TryGetValue(text.Id.Value, out var outData))
{
data.Cultures.Add(culture);
data.Datas.Add(text.I18nDatas[culture].Text);
chooseData.Cultures.Add(culture);
chooseData.Datas.Add(text.I18nDatas[culture].Choose);
foreach (var culture in I18nHelper.Current.CultureNames.Enumerate())
{
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);
}
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)
{
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)
if (pet.IsSimplePetModel)
continue;
if (AllData.TryGetValue(pet.Id.Value, out var outData))
{
data.Cultures.Add(culture);
data.Datas.Add(pet.I18nDatas[culture].Name);
petNameData.Cultures.Add(culture);
petNameData.Datas.Add(pet.I18nDatas[culture].PetName);
descriptionData.Cultures.Add(culture);
descriptionData.Datas.Add(pet.I18nDatas[culture].Description);
foreach (var culture in I18nHelper.Current.CultureNames.Enumerate())
{
if (outData.Datas[culture.Index].Group is null)
{
var group = new ObservableValueGroup<string>()
{
outData.Datas[culture.Index]
};
}
outData.Datas[culture.Index].Group!.Add(pet.I18nDatas[culture.Value].Name);
}
}
I18nDatas.Add(data);
I18nDatas.Add(petNameData);
I18nDatas.Add(descriptionData);
else
{
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();
public HashSet<string> Ids { get; } = new();
public Dictionary<string, I18nData> AllData { get; } = new();
public ObservableCollection<I18nData> I18nDatas { 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
// TODO: 国际化标头
private const string ValueBindingFormat = "Datas[{0}].Value";