- 保存宠物时, 若本体存在同名宠物, 则只会保存差异数据
修复:
- 模组宠物和本体宠物同时出现的问题
This commit is contained in:
Hakoyu 2023-12-11 01:29:44 +08:00
parent fb81811be0
commit 9546fb604a
64 changed files with 1434 additions and 646 deletions

View File

@ -9,6 +9,7 @@
<c:BrushToMediaColorConverter x:Key="BrushToMediaColorConverter" />
<c:MaxConverter x:Key="MaxConverter" />
<c:FalseToHiddenConverter x:Key="FalseToHiddenConverter" />
<c:FalseToCollapsedConverter x:Key="FalseToCollapsedConverter" />
<c:EqualsConverter x:Key="EqualsConverter" />
<c:NotEqualsConverter x:Key="NotEqualsConverter" />
<c:NullToFalseConverter x:Key="NullToFalseConverter" />

View File

@ -1,5 +1,4 @@
using HKW.Models;
using LinePutScript;
using LinePutScript;
using LinePutScript.Converter;
using LinePutScript.Dictionary;
using System;

View File

@ -1,5 +1,4 @@
using HKW.Models;
using LinePutScript.Converter;
using LinePutScript.Converter;
using System;
using System.Collections.Generic;
using System.IO;

View File

@ -9,7 +9,6 @@ using System.Windows.Controls;
using System.Windows;
using VPet_Simulator.Windows.Interface;
using VPet.ModMaker.Views;
using HKW.Models;
namespace VPet.ModMaker.Models;
@ -51,12 +50,7 @@ public class ModMaker : MainPlugin
ModMakerInfo.GameVersion = MW.version;
// 载入本体宠物
foreach (var pet in MW.Pets)
{
var petModel = new PetModel();
petModel.SourceId = pet.Name;
petModel.Id.Value = pet.Name + " (来自本体)".Translate();
ModMakerInfo.Pets.Add(petModel);
}
ModMakerInfo.MainPets.Add(pet.Name, new(pet, true));
//Maker.ModMaker = this;
Maker.Show();
Maker.Closed += Maker_Closed;

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VPet.ModMaker.Models.ModModel;
using VPet_Simulator.Core;
namespace VPet.ModMaker.Models;
@ -34,11 +35,24 @@ public static class ModMakerInfo
/// <summary>
/// 本体的宠物
/// <para>
/// (PetId, PetModel)
/// </para>
/// </summary>
public static List<PetModel> Pets { get; } = new();
public static Dictionary<string, PetModel> MainPets { get; } = new();
/// <summary>
/// 本地风格
/// </summary>
public static NativeStyles NativeStyles { get; } = new();
/// <summary>
/// 是含有名称的动画
/// </summary>
/// <param name="graphType"></param>
/// <returns></returns>
public static bool IsHasNameAnime(this GraphInfo.GraphType graphType)
{
return AnimeTypeModel.HasNameAnimes.Contains(graphType);
}
}

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

View File

@ -1,5 +1,6 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using HKW.HKWUtils;
using HKW.HKWUtils.Observable;
using LinePutScript.Converter;
using System;
using System.Collections.Generic;
@ -145,14 +146,14 @@ public class ClickTextModel : I18nModel<I18nClickTextModel>
Working.Value = clickText.Working;
WorkingState.Value = clickText.State;
DayTime.EnumValue.Value = clickText.DaiTime;
Like.SetValue(clickText.LikeMin, clickText.LikeMax);
Health.SetValue(clickText.HealthMin, clickText.HealthMax);
Level.SetValue(clickText.LevelMin, clickText.LevelMax);
Money.SetValue(clickText.MoneyMin, clickText.MoneyMax);
Food.SetValue(clickText.FoodMin, clickText.FoodMax);
Drink.SetValue(clickText.DrinkMin, clickText.DrinkMax);
Feel.SetValue(clickText.FeelMin, clickText.FeelMax);
Strength.SetValue(clickText.StrengthMin, clickText.StrengthMax);
Like = new(clickText.LikeMin, clickText.LikeMax);
Health = new(clickText.HealthMin, clickText.HealthMax);
Level = new(clickText.LevelMin, clickText.LevelMax);
Money = new(clickText.MoneyMin, clickText.MoneyMax);
Food = new(clickText.FoodMin, clickText.FoodMax);
Drink = new(clickText.DrinkMin, clickText.DrinkMax);
Feel = new(clickText.FeelMin, clickText.FeelMax);
Strength = new(clickText.StrengthMin, clickText.StrengthMax);
}
public ClickText ToClickText()
@ -164,22 +165,22 @@ public class ClickTextModel : I18nModel<I18nClickTextModel>
Working = Working.Value,
State = WorkingState.Value,
DaiTime = DayTime.EnumValue.Value,
LikeMax = Like.Max.Value,
LikeMin = Like.Min.Value,
HealthMin = Health.Min.Value,
HealthMax = Health.Max.Value,
LevelMin = Level.Min.Value,
LevelMax = Level.Max.Value,
MoneyMin = Money.Min.Value,
MoneyMax = Money.Max.Value,
FoodMin = Food.Min.Value,
FoodMax = Food.Max.Value,
DrinkMin = Drink.Min.Value,
DrinkMax = Drink.Max.Value,
FeelMin = Feel.Min.Value,
FeelMax = Feel.Max.Value,
StrengthMin = Strength.Min.Value,
StrengthMax = Strength.Max.Value,
LikeMax = Like.Max,
LikeMin = Like.Min,
HealthMin = Health.Min,
HealthMax = Health.Max,
LevelMin = Level.Min,
LevelMax = Level.Max,
MoneyMin = Money.Min,
MoneyMax = Money.Max,
FoodMin = Food.Min,
FoodMax = Food.Max,
DrinkMin = Drink.Min,
DrinkMax = Drink.Max,
FeelMin = Feel.Min,
FeelMax = Feel.Max,
StrengthMin = Strength.Min,
StrengthMax = Strength.Max,
};
}
}

View File

@ -48,7 +48,7 @@ public class FoodAnimeModel
foodLocationInfo.Duration.Value = int.Parse(infos[0]);
if (infos.Length > 1)
{
foodLocationInfo.Rect.SetValue(
foodLocationInfo.Rect = new(
double.Parse(infos[1]),
double.Parse(infos[2]),
double.Parse(infos[3]),

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using LinePutScript;
using LinePutScript.Localization.WPF;
using System;

View File

@ -20,7 +20,7 @@ public class FoodLocationModel
/// <summary>
/// 范围
/// </summary>
public ObservableRect<double> Rect { get; } = new();
public ObservableRect<double> Rect { get; set; } = new();
/// <summary>
/// 旋转角度
@ -34,9 +34,9 @@ public class FoodLocationModel
public FoodLocationModel()
{
Rect.Width.ValueChanged += (s, e) =>
Rect.PropertyChangedX += (s, e) =>
{
Rect.Height.Value = e.NewValue;
Rect.Height = (int)e.NewValue;
};
}
@ -44,7 +44,7 @@ public class FoodLocationModel
{
var model = new FoodLocationModel();
model.Duration.Value = Duration.Value;
model.Rect.SetValue(Rect.X.Value, Rect.Y.Value, Rect.Width.Value, Rect.Height.Value);
model.Rect = new(Rect.X, Rect.Y, Rect.Width, Rect.Height);
model.Rotate.Value = Rotate.Value;
model.Opacity.Value = Opacity.Value;
return model;
@ -52,6 +52,6 @@ public class FoodLocationModel
public override string ToString()
{
return $"{Duration.Value},{Rect.X.Value},{Rect.Y.Value},{Rect.Width.Value},{Rotate.Value},{Opacity.Value}";
return $"{Duration.Value}, {Rect.X}, {Rect.Y}, {Rect.Width}, {Rotate.Value}, {Opacity.Value}";
}
}

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using LinePutScript;
using LinePutScript.Converter;
using System;

View File

@ -1,5 +1,6 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using HKW.HKWUtils;
using HKW.HKWUtils.Observable;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@ -1,5 +1,6 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using HKW.HKWUtils;
using HKW.HKWUtils.Observable;
using LinePutScript;
using LinePutScript.Converter;
using LinePutScript.Localization.WPF;
@ -20,6 +21,7 @@ using VPet_Simulator.Windows.Interface;
namespace VPet.ModMaker.Models;
// TODO: 本体模组显示开关
/// <summary>
/// 模组信息模型
/// </summary>
@ -142,6 +144,7 @@ public class ModInfoModel : I18nModel<I18nModInfoModel>
foreach (var selectText in loader.SelectTexts)
SelectTexts.Add(new(selectText));
// 载入模组宠物
foreach (var pet in loader.Pets)
{
var petModel = new PetModel(pet);
@ -149,14 +152,16 @@ public class ModInfoModel : I18nModel<I18nModInfoModel>
foreach (var p in pet.path)
LoadAnime(petModel, p);
}
// 插入本体宠物
foreach (var pet in ModMakerInfo.Pets)
foreach (var pet in ModMakerInfo.MainPets)
{
// 确保Id不重复
if (Pets.All(i => i.Id.Value != pet.SourceId))
Pets.Insert(0, pet);
if (Pets.All(i => i.Id.Value != pet.Key))
Pets.Insert(0, pet.Value);
}
// 载入本地化
foreach (var lang in loader.I18nDatas)
I18nDatas.Add(lang.Key, lang.Value);
OtherI18nDatas = loader.OtherI18nDatas;

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using HKW.HKWUtils;
using HKW.HKWUtils.Observable;
using LinePutScript;
using LinePutScript.Converter;
using LinePutScript.Localization.WPF;
@ -23,9 +23,9 @@ namespace VPet.ModMaker.Models;
public class PetModel : I18nModel<I18nPetInfoModel>
{
/// <summary>
/// 显示的Id 若不为空则判断为来自本体的宠物
/// 来自本体
/// </summary>
public string? SourceId { get; set; } = null;
public ObservableValue<bool> FromMain { get; set; } = new(false);
/// <summary>
/// Id
@ -95,8 +95,6 @@ public class PetModel : I18nModel<I18nPetInfoModel>
public ObservableValue<int> AnimeCount { get; } = new();
public bool IsSimplePetModel { get; } = false;
public PetModel()
{
PetNameId.Value = $"{Id.Value}_{nameof(PetNameId)}";
@ -131,68 +129,62 @@ public class PetModel : I18nModel<I18nPetInfoModel>
CurrentI18nData.Value = I18nDatas[I18nHelper.Current.CultureName.Value];
}
public PetModel(PetLoader loader)
public PetModel(PetLoader loader, bool fromMain = false)
: this()
{
Id.Value = loader.Name;
PetNameId.Value = loader.PetName;
DescriptionId.Value = loader.Intor;
TouchHeadRect.Value.SetValue(
TouchHeadRect.Value = new(
loader.Config.TouchHeadLocate.X,
loader.Config.TouchHeadLocate.Y,
loader.Config.TouchHeadSize.Width,
loader.Config.TouchHeadSize.Height
);
TouchBodyRect.Value.SetValue(
TouchBodyRect.Value = new(
loader.Config.TouchBodyLocate.X,
loader.Config.TouchBodyLocate.Y,
loader.Config.TouchBodySize.Width,
loader.Config.TouchBodySize.Height
);
TouchRaisedRect.Value.Happy.Value.SetValue(
TouchRaisedRect.Value.Happy = new(
loader.Config.TouchRaisedLocate[0].X,
loader.Config.TouchRaisedLocate[0].Y,
loader.Config.TouchRaisedSize[0].Width,
loader.Config.TouchRaisedSize[0].Height
);
TouchRaisedRect.Value.Nomal.Value.SetValue(
TouchRaisedRect.Value.Nomal = new(
loader.Config.TouchRaisedLocate[1].X,
loader.Config.TouchRaisedLocate[1].Y,
loader.Config.TouchRaisedSize[1].Width,
loader.Config.TouchRaisedSize[1].Height
);
TouchRaisedRect.Value.PoorCondition.Value.SetValue(
TouchRaisedRect.Value.PoorCondition = new(
loader.Config.TouchRaisedLocate[2].X,
loader.Config.TouchRaisedLocate[2].Y,
loader.Config.TouchRaisedSize[2].Width,
loader.Config.TouchRaisedSize[2].Height
);
TouchRaisedRect.Value.Ill.Value.SetValue(
TouchRaisedRect.Value.Ill = new(
loader.Config.TouchRaisedLocate[3].X,
loader.Config.TouchRaisedLocate[3].Y,
loader.Config.TouchRaisedSize[3].Width,
loader.Config.TouchRaisedSize[3].Height
);
RaisePoint.Value.Happy.Value.SetValue(
loader.Config.RaisePoint[0].X,
loader.Config.RaisePoint[0].Y
);
RaisePoint.Value.Nomal.Value.SetValue(
loader.Config.RaisePoint[1].X,
loader.Config.RaisePoint[1].Y
);
RaisePoint.Value.PoorCondition.Value.SetValue(
RaisePoint.Value.Happy = new(loader.Config.RaisePoint[0].X, loader.Config.RaisePoint[0].Y);
RaisePoint.Value.Nomal = new(loader.Config.RaisePoint[1].X, loader.Config.RaisePoint[1].Y);
RaisePoint.Value.PoorCondition = new(
loader.Config.RaisePoint[2].X,
loader.Config.RaisePoint[2].Y
);
RaisePoint.Value.Ill.Value.SetValue(
loader.Config.RaisePoint[3].X,
loader.Config.RaisePoint[3].Y
);
RaisePoint.Value.Ill = new(loader.Config.RaisePoint[3].X, loader.Config.RaisePoint[3].Y);
// 如果这个宠物数据来自本体, 则不载入 Work 和 Move
if (FromMain.Value = fromMain)
return;
foreach (var work in loader.Config.Works)
Works.Add(new(work));
@ -200,13 +192,6 @@ public class PetModel : I18nModel<I18nPetInfoModel>
Moves.Add(new(move));
}
public PetModel(PetLoader loader, bool isSimplePet)
: this()
{
Id.Value = loader.Name;
IsSimplePetModel = isSimplePet;
}
public void Close()
{
foreach (var anime in Animes)
@ -222,13 +207,6 @@ public class PetModel : I18nModel<I18nPetInfoModel>
/// <param name="path">路径</param>
public void Save(string path)
{
if (IsSimplePetModel)
{
Id.Value = SourceId;
SaveSimplePetInfo(path);
Id.Value = SourceId + " (来自本体)".Translate();
return;
}
foreach (var cultureName in I18nHelper.Current.CultureNames)
{
ModInfoModel.SaveI18nDatas[cultureName].TryAdd(
@ -248,7 +226,11 @@ public class PetModel : I18nModel<I18nPetInfoModel>
if (File.Exists(petFile) is false)
File.Create(petFile).Close();
var lps = new LPS();
SavePetInfo(lps);
// 如果本体中存在相同的宠物, 则只保存差异信息
if (ModMakerInfo.MainPets.TryGetValue(Id.Value, out var mainPet))
SaveDifferentPetInfo(lps, mainPet);
else
SavePetInfo(lps);
SaveWorksInfo(lps);
SaveMoveInfo(lps);
File.WriteAllText(petFile, lps.ToString());
@ -257,18 +239,6 @@ public class PetModel : I18nModel<I18nPetInfoModel>
SaveAnime(path);
}
private void SaveSimplePetInfo(string path)
{
if (Works.Count == 0 && Moves.Count == 0 && Animes.Count == 0)
return;
var petFile = Path.Combine(path, $"{Id.Value}.lps");
var lps = new LPS { new Line("pet", Id.Value) { new Sub("path", Id.Value), } };
SaveWorksInfo(lps);
SaveMoveInfo(lps);
File.WriteAllText(petFile, lps.ToString());
SaveAnime(path);
}
private void SaveAnime(string path)
{
var petAnimePath = Path.Combine(path, Id.Value);
@ -311,12 +281,42 @@ public class PetModel : I18nModel<I18nPetInfoModel>
}
}
#region SavePetInfo
/// <summary>
/// 保存宠物信息
/// </summary>
/// <param name="lps"></param>
/// <param name="pet"></param>
private void SavePetInfo(LPS lps)
{
SavePetBasicInfo(lps);
SavePetTouchHeadInfo(lps);
SavePetTouchBodyInfo(lps);
SavePetTouchRaisedInfo(lps);
SavePetRaisePointInfo(lps);
}
/// <summary>
/// 保存差异宠物信息
/// <para>
/// 用于本体存在同名宠物的情况下
/// </para>
/// </summary>
/// <param name="lps"></param>
/// <param name="mainPet">本体宠物</param>
private void SaveDifferentPetInfo(LPS lps, PetModel mainPet)
{
SavePetBasicInfo(lps);
if (TouchHeadRect != mainPet.TouchHeadRect)
SavePetTouchHeadInfo(lps);
if (TouchBodyRect != mainPet.TouchBodyRect)
SavePetTouchBodyInfo(lps);
if (TouchRaisedRect != mainPet.TouchRaisedRect)
SavePetTouchRaisedInfo(lps);
if (RaisePoint != mainPet.RaisePoint)
SavePetRaisePointInfo(lps);
}
private void SavePetBasicInfo(LPS lps)
{
lps.Add(
new Line("pet", Id.Value)
@ -326,65 +326,82 @@ public class PetModel : I18nModel<I18nPetInfoModel>
new Sub("petname", PetNameId.Value)
}
);
}
private void SavePetTouchHeadInfo(LPS lps)
{
lps.Add(
new Line("touchhead")
{
new Sub("px", TouchHeadRect.Value.X.Value),
new Sub("py", TouchHeadRect.Value.Y.Value),
new Sub("sw", TouchHeadRect.Value.Width.Value),
new Sub("sh", TouchHeadRect.Value.Height.Value),
}
);
lps.Add(
new Line("touchbody")
{
new Sub("px", TouchBodyRect.Value.X.Value),
new Sub("py", TouchBodyRect.Value.Y.Value),
new Sub("sw", TouchBodyRect.Value.Width.Value),
new Sub("sh", TouchBodyRect.Value.Height.Value),
}
);
lps.Add(
new Line("touchraised")
{
new Sub("happy_px", TouchRaisedRect.Value.Happy.Value.X.Value),
new Sub("happy_py", TouchRaisedRect.Value.Happy.Value.Y.Value),
new Sub("happy_sw", TouchRaisedRect.Value.Happy.Value.Width.Value),
new Sub("happy_sh", TouchRaisedRect.Value.Happy.Value.Height.Value),
//
new Sub("nomal_px", TouchRaisedRect.Value.Nomal.Value.X.Value),
new Sub("nomal_py", TouchRaisedRect.Value.Nomal.Value.Y.Value),
new Sub("nomal_sw", TouchRaisedRect.Value.Nomal.Value.Width.Value),
new Sub("nomal_sh", TouchRaisedRect.Value.Nomal.Value.Height.Value),
//
new Sub("poorcondition_px", TouchRaisedRect.Value.PoorCondition.Value.X.Value),
new Sub("poorcondition_py", TouchRaisedRect.Value.PoorCondition.Value.Y.Value),
new Sub("poorcondition_sw", TouchRaisedRect.Value.PoorCondition.Value.Width.Value),
new Sub("poorcondition_sh", TouchRaisedRect.Value.PoorCondition.Value.Height.Value),
//
new Sub("ill_px", TouchRaisedRect.Value.Ill.Value.X.Value),
new Sub("ill_py", TouchRaisedRect.Value.Ill.Value.Y.Value),
new Sub("ill_sw", TouchRaisedRect.Value.Ill.Value.Width.Value),
new Sub("ill_sh", TouchRaisedRect.Value.Ill.Value.Height.Value),
}
);
lps.Add(
new Line("raisepoint")
{
new Sub("happy_x", RaisePoint.Value.Happy.Value.X.Value),
new Sub("happy_y", RaisePoint.Value.Happy.Value.Y.Value),
//
new Sub("nomal_x", RaisePoint.Value.Nomal.Value.X.Value),
new Sub("nomal_y", RaisePoint.Value.Nomal.Value.Y.Value),
//
new Sub("poorcondition_x", RaisePoint.Value.PoorCondition.Value.X.Value),
new Sub("poorcondition_y", RaisePoint.Value.PoorCondition.Value.Y.Value),
//
new Sub("ill_x", RaisePoint.Value.Ill.Value.X.Value),
new Sub("ill_y", RaisePoint.Value.Ill.Value.Y.Value),
new Sub("px", TouchHeadRect.Value.X),
new Sub("py", TouchHeadRect.Value.Y),
new Sub("sw", TouchHeadRect.Value.Width),
new Sub("sh", TouchHeadRect.Value.Height),
}
);
}
private void SavePetTouchBodyInfo(LPS lps)
{
lps.Add(
new Line("touchbody")
{
new Sub("px", TouchBodyRect.Value.X),
new Sub("py", TouchBodyRect.Value.Y),
new Sub("sw", TouchBodyRect.Value.Width),
new Sub("sh", TouchBodyRect.Value.Height),
}
);
}
private void SavePetTouchRaisedInfo(LPS lps)
{
lps.Add(
new Line("touchraised")
{
new Sub("happy_px", TouchRaisedRect.Value.Happy.X),
new Sub("happy_py", TouchRaisedRect.Value.Happy.Y),
new Sub("happy_sw", TouchRaisedRect.Value.Happy.Width),
new Sub("happy_sh", TouchRaisedRect.Value.Happy.Height),
//
new Sub("nomal_px", TouchRaisedRect.Value.Nomal.X),
new Sub("nomal_py", TouchRaisedRect.Value.Nomal.Y),
new Sub("nomal_sw", TouchRaisedRect.Value.Nomal.Width),
new Sub("nomal_sh", TouchRaisedRect.Value.Nomal.Height),
//
new Sub("poorcondition_px", TouchRaisedRect.Value.PoorCondition.X),
new Sub("poorcondition_py", TouchRaisedRect.Value.PoorCondition.Y),
new Sub("poorcondition_sw", TouchRaisedRect.Value.PoorCondition.Width),
new Sub("poorcondition_sh", TouchRaisedRect.Value.PoorCondition.Height),
//
new Sub("ill_px", TouchRaisedRect.Value.Ill.X),
new Sub("ill_py", TouchRaisedRect.Value.Ill.Y),
new Sub("ill_sw", TouchRaisedRect.Value.Ill.Width),
new Sub("ill_sh", TouchRaisedRect.Value.Ill.Height),
}
);
}
private void SavePetRaisePointInfo(LPS lps)
{
lps.Add(
new Line("raisepoint")
{
new Sub("happy_x", RaisePoint.Value.Happy.X),
new Sub("happy_y", RaisePoint.Value.Happy.Y),
//
new Sub("nomal_x", RaisePoint.Value.Nomal.X),
new Sub("nomal_y", RaisePoint.Value.Nomal.Y),
//
new Sub("poorcondition_x", RaisePoint.Value.PoorCondition.X),
new Sub("poorcondition_y", RaisePoint.Value.PoorCondition.Y),
//
new Sub("ill_x", RaisePoint.Value.Ill.X),
new Sub("ill_y", RaisePoint.Value.Ill.Y),
}
);
}
#endregion
#endregion
}
@ -405,13 +422,41 @@ public class I18nPetInfoModel
}
public class ObservableMultiStateRect
: ObservableClass<ObservableMultiStateRect>,
IEquatable<ObservableMultiStateRect>
{
public ObservableValue<ObservableRect<double>> Happy { get; } = new(new());
public ObservableValue<ObservableRect<double>> Nomal { get; } = new(new());
public ObservableValue<ObservableRect<double>> PoorCondition { get; } = new(new());
public ObservableValue<ObservableRect<double>> Ill { get; } = new(new());
private ObservableRect<double> _happy;
public ObservableRect<double> Happy
{
get => _happy;
set => SetProperty(ref _happy, value);
}
private ObservableRect<double> _nomal;
public ObservableRect<double> Nomal
{
get => _nomal;
set => SetProperty(ref _nomal, value);
}
private ObservableRect<double> _poorCondition;
public ObservableRect<double> PoorCondition
{
get => _poorCondition;
set => SetProperty(ref _poorCondition, value);
}
private ObservableRect<double> _ill;
public ObservableRect<double> Ill
{
get => _ill;
set => SetProperty(ref _ill, value);
}
public ObservableMultiStateRect() { }
public ObservableMultiStateRect()
{
Happy = new();
Nomal = new();
PoorCondition = new();
Ill = new();
}
public ObservableMultiStateRect(
ObservableRect<double> happy,
@ -420,31 +465,101 @@ public class ObservableMultiStateRect
ObservableRect<double> ill
)
{
Happy.Value = happy;
Nomal.Value = nomal;
PoorCondition.Value = poorCondition;
Ill.Value = ill;
Happy = happy;
Nomal = nomal;
PoorCondition = poorCondition;
Ill = ill;
}
public ObservableMultiStateRect Copy()
{
var result = new ObservableMultiStateRect();
result.Happy.Value = Happy.Value.Copy();
result.Nomal.Value = Nomal.Value.Copy();
result.PoorCondition.Value = PoorCondition.Value.Copy();
result.Ill.Value = Ill.Value.Copy();
return result;
return new()
{
Happy = Happy.Copy(),
Nomal = Nomal.Copy(),
PoorCondition = PoorCondition.Copy(),
Ill = Ill.Copy(),
};
}
#region Other
/// <inheritdoc/>
public override int GetHashCode()
{
return HashCode.Combine(Happy, Nomal, PoorCondition, Ill);
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is ObservableMultiStateRect temp
&& EqualityComparer<ObservableRect<double>>.Default.Equals(Happy, temp.Happy)
&& EqualityComparer<ObservableRect<double>>.Default.Equals(Nomal, temp.Nomal)
&& EqualityComparer<ObservableRect<double>>.Default.Equals(
PoorCondition,
temp.PoorCondition
)
&& EqualityComparer<ObservableRect<double>>.Default.Equals(Ill, temp.Ill);
}
/// <inheritdoc/>
public bool Equals(ObservableMultiStateRect? other)
{
return Equals(obj: other);
}
/// <inheritdoc/>
public static bool operator ==(ObservableMultiStateRect a, ObservableMultiStateRect b)
{
return Equals(a, b);
}
/// <inheritdoc/>
public static bool operator !=(ObservableMultiStateRect a, ObservableMultiStateRect b)
{
return Equals(a, b) is not true;
}
#endregion
}
public class ObservableMultiStatePoint
: ObservableClass<ObservableMultiStatePoint>,
IEquatable<ObservableMultiStatePoint>
{
public ObservableValue<ObservablePoint<double>> Happy { get; } = new(new());
public ObservableValue<ObservablePoint<double>> Nomal { get; } = new(new());
public ObservableValue<ObservablePoint<double>> PoorCondition { get; } = new(new());
public ObservableValue<ObservablePoint<double>> Ill { get; } = new(new());
private ObservablePoint<double> _happy;
public ObservablePoint<double> Happy
{
get => _happy;
set => SetProperty(ref _happy, value);
}
private ObservablePoint<double> _nomal;
public ObservablePoint<double> Nomal
{
get => _nomal;
set => SetProperty(ref _nomal, value);
}
private ObservablePoint<double> _poorCondition;
public ObservablePoint<double> PoorCondition
{
get => _poorCondition;
set => SetProperty(ref _poorCondition, value);
}
private ObservablePoint<double> _ill;
public ObservablePoint<double> Ill
{
get => _ill;
set => SetProperty(ref _ill, value);
}
public ObservableMultiStatePoint() { }
public ObservableMultiStatePoint()
{
Happy = new();
Nomal = new();
PoorCondition = new();
Ill = new();
}
public ObservableMultiStatePoint(
ObservablePoint<double> happy,
@ -453,19 +568,61 @@ public class ObservableMultiStatePoint
ObservablePoint<double> ill
)
{
Happy.Value = happy;
Nomal.Value = nomal;
PoorCondition.Value = poorCondition;
Ill.Value = ill;
Happy = happy;
Nomal = nomal;
PoorCondition = poorCondition;
Ill = ill;
}
public ObservableMultiStatePoint Copy()
{
var result = new ObservableMultiStatePoint();
result.Happy.Value = Happy.Value.Copy();
result.Nomal.Value = Nomal.Value.Copy();
result.PoorCondition.Value = PoorCondition.Value.Copy();
result.Ill.Value = Ill.Value.Copy();
return result;
return new()
{
Happy = Happy.Copy(),
Nomal = Nomal.Copy(),
PoorCondition = PoorCondition.Copy(),
Ill = Ill.Copy(),
};
}
#region Other
/// <inheritdoc/>
public override int GetHashCode()
{
return HashCode.Combine(Happy, Nomal, PoorCondition, Ill);
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is ObservableMultiStatePoint temp
&& EqualityComparer<ObservablePoint<double>>.Default.Equals(Happy, temp.Happy)
&& EqualityComparer<ObservablePoint<double>>.Default.Equals(Nomal, temp.Nomal)
&& EqualityComparer<ObservablePoint<double>>.Default.Equals(
PoorCondition,
temp.PoorCondition
)
&& EqualityComparer<ObservablePoint<double>>.Default.Equals(Ill, temp.Ill);
}
/// <inheritdoc/>
public bool Equals(ObservableMultiStatePoint? other)
{
return Equals(obj: other);
}
/// <inheritdoc/>
public static bool operator ==(ObservableMultiStatePoint a, ObservableMultiStatePoint b)
{
return Equals(a, b);
}
/// <inheritdoc/>
public static bool operator !=(ObservableMultiStatePoint a, ObservableMultiStatePoint b)
{
return Equals(a, b) is not true;
}
#endregion
}

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@ -130,14 +130,14 @@ public class SelectTextModel : I18nModel<I18nSelectTextModel>
Mode.EnumValue.Value = text.Mode;
Tags.Value = text.Tags is null ? string.Empty : string.Join(", ", text.Tags);
ToTags.Value = text.ToTags is null ? string.Empty : string.Join(", ", text.ToTags);
Like.SetValue(text.LikeMin, text.LikeMax);
Health.SetValue(text.HealthMin, text.HealthMax);
Level.SetValue(text.LevelMin, text.LevelMax);
Money.SetValue(text.MoneyMin, text.MoneyMax);
Food.SetValue(text.FoodMin, text.FoodMax);
Drink.SetValue(text.DrinkMin, text.DrinkMax);
Feel.SetValue(text.FeelMin, text.FeelMax);
Strength.SetValue(text.StrengthMin, text.StrengthMax);
Like = new(text.LikeMin, text.LikeMax);
Health = new(text.HealthMin, text.HealthMax);
Level = new(text.LevelMin, text.LevelMax);
Money = new(text.MoneyMin, text.MoneyMax);
Food = new(text.FoodMin, text.FoodMax);
Drink = new(text.DrinkMin, text.DrinkMax);
Feel = new(text.FeelMin, text.FeelMax);
Strength = new(text.StrengthMin, text.StrengthMax);
}
private readonly static char[] rs_splitChar = { ',', ' ' };
@ -151,22 +151,22 @@ public class SelectTextModel : I18nModel<I18nSelectTextModel>
Mode = Mode.EnumValue.Value,
Tags = new(Tags.Value.Split(rs_splitChar, StringSplitOptions.RemoveEmptyEntries)),
ToTags = new(ToTags.Value.Split(rs_splitChar, StringSplitOptions.RemoveEmptyEntries)),
LikeMax = Like.Max.Value,
LikeMin = Like.Min.Value,
HealthMin = Health.Min.Value,
HealthMax = Health.Max.Value,
LevelMin = Level.Min.Value,
LevelMax = Level.Max.Value,
MoneyMin = Money.Min.Value,
MoneyMax = Money.Max.Value,
FoodMin = Food.Min.Value,
FoodMax = Food.Max.Value,
DrinkMin = Drink.Min.Value,
DrinkMax = Drink.Max.Value,
FeelMin = Feel.Min.Value,
FeelMax = Feel.Max.Value,
StrengthMin = Strength.Min.Value,
StrengthMax = Strength.Max.Value,
LikeMax = Like.Max,
LikeMin = Like.Min,
HealthMin = Health.Min,
HealthMax = Health.Max,
LevelMin = Level.Min,
LevelMax = Level.Max,
MoneyMin = Money.Min,
MoneyMax = Money.Max,
FoodMin = Food.Min,
FoodMax = Food.Max,
DrinkMin = Drink.Min,
DrinkMax = Drink.Max,
FeelMin = Feel.Min,
FeelMax = Feel.Max,
StrengthMin = Strength.Min,
StrengthMax = Strength.Max,
};
}
}

View File

@ -1,31 +1,31 @@
using HKW.HKWUtils.Observable;
//using HKW.HKWUtils.Observable;
namespace VPet.ModMaker.Models;
//namespace VPet.ModMaker.Models;
public class ObservablePoint<T>
{
public ObservableValue<T> X { get; } = new();
public ObservableValue<T> Y { get; } = new();
//public class ObservablePoint<T>
//{
// public ObservableValue<T> X { get; } = new();
// public ObservableValue<T> Y { get; } = new();
public ObservablePoint() { }
// public ObservablePoint() { }
public ObservablePoint(T x, T y)
{
X.Value = x;
Y.Value = y;
}
// public ObservablePoint(T x, T y)
// {
// X.Value = x;
// Y.Value = y;
// }
public void SetValue(T x, T y)
{
X.Value = x;
Y.Value = y;
}
// public void SetValue(T x, T y)
// {
// X.Value = x;
// Y.Value = y;
// }
public ObservablePoint<T> Copy()
{
var result = new ObservablePoint<T>();
result.X.Value = X.Value;
result.Y.Value = Y.Value;
return result;
}
}
// public ObservablePoint<T> Copy()
// {
// var result = new ObservablePoint<T>();
// result.X.Value = X.Value;
// result.Y.Value = Y.Value;
// return result;
// }
//}

View File

@ -1,62 +1,62 @@
using HKW.HKWUtils.Observable;
//using HKW.HKWUtils.Observable;
namespace HKW.Models;
//namespace HKW.Models;
/// <summary>
/// 可观察的范围
/// </summary>
/// <typeparam name="T">类型</typeparam>
public class ObservableRange<T>
{
/// <summary>
/// 最小值
/// </summary>
public ObservableValue<T> Min { get; } = new();
///// <summary>
///// 可观察的范围
///// </summary>
///// <typeparam name="T">类型</typeparam>
//public class ObservableRange<T>
//{
// /// <summary>
// /// 最小值
// /// </summary>
// public ObservableValue<T> Min { get; } = new();
/// <summary>
/// 最大值
/// </summary>
public ObservableValue<T> Max { get; } = new();
// /// <summary>
// /// 最大值
// /// </summary>
// public ObservableValue<T> Max { get; } = new();
/// <summary>
/// 信息
/// </summary>
public ObservableValue<string> Info { get; } = new();
// /// <summary>
// /// 信息
// /// </summary>
// public ObservableValue<string> Info { get; } = new();
public ObservableRange()
{
Min.ValueChanged += ValueChanged;
Max.ValueChanged += ValueChanged;
}
// public ObservableRange()
// {
// Min.ValueChanged += ValueChanged;
// Max.ValueChanged += ValueChanged;
// }
public ObservableRange(T min, T max)
: this()
{
SetValue(min, max);
}
// public ObservableRange(T min, T max)
// : this()
// {
// SetValue(min, max);
// }
private void ValueChanged(ObservableValue<T> sender, ValueChangedEventArgs<T> e)
{
Info.Value = $"({Min.Value}, {Max.Value})";
}
// private void ValueChanged(ObservableValue<T> sender, ValueChangedEventArgs<T> e)
// {
// Info.Value = $"({Min.Value}, {Max.Value})";
// }
/// <summary>
/// 设置值
/// </summary>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
public void SetValue(T min, T max)
{
Min.Value = min;
Max.Value = max;
}
// /// <summary>
// /// 设置值
// /// </summary>
// /// <param name="min">最小值</param>
// /// <param name="max">最大值</param>
// public void SetValue(T min, T max)
// {
// Min.Value = min;
// Max.Value = max;
// }
/// <summary>
/// 复制
/// </summary>
/// <returns></returns>
public ObservableRange<T> Copy()
{
return new(Min.Value, Max.Value);
}
}
// /// <summary>
// /// 复制
// /// </summary>
// /// <returns></returns>
// public ObservableRange<T> Copy()
// {
// return new(Min.Value, Max.Value);
// }
//}

View File

@ -1,39 +1,41 @@
using HKW.HKWUtils.Observable;
//using HKW.HKWUtils.Observable;
namespace VPet.ModMaker.Models;
//namespace VPet.ModMaker.Models;
public class ObservableRect<T>
{
public ObservableValue<T> X { get; } = new();
public ObservableValue<T> Y { get; } = new();
public ObservableValue<T> Width { get; } = new();
public ObservableValue<T> Height { get; } = new();
//public class ObservableRect<T>
//{
// public ObservableValue<T> X { get; } = new();
// public ObservableValue<T> Y { get; } = new();
// public ObservableValue<T> Width { get; } = new();
// public ObservableValue<T> Height { get; } = new();
public ObservableRect() { }
// public bool Changed { get; set; } = true;
public ObservableRect(T x, T y, T width, T hetght)
{
X.Value = x;
Y.Value = y;
Width.Value = width;
Height.Value = hetght;
}
// public ObservableRect() { }
public void SetValue(T x, T y, T width, T hetght)
{
X.Value = x;
Y.Value = y;
Width.Value = width;
Height.Value = hetght;
}
// public ObservableRect(T x, T y, T width, T hetght)
// {
// X.Value = x;
// Y.Value = y;
// Width.Value = width;
// Height.Value = hetght;
// }
public ObservableRect<T> Copy()
{
var result = new ObservableRect<T>();
result.X.Value = X.Value;
result.Y.Value = Y.Value;
result.Width.Value = Width.Value;
result.Height.Value = Height.Value;
return result;
}
}
// public void SetValue(T x, T y, T width, T hetght)
// {
// X.Value = x;
// Y.Value = y;
// Width.Value = width;
// Height.Value = hetght;
// }
// public ObservableRect<T> Copy()
// {
// var result = new ObservableRect<T>();
// result.X.Value = X.Value;
// result.Y.Value = Y.Value;
// result.Width.Value = Width.Value;
// result.Height.Value = Height.Value;
// return result;
// }
//}

View File

@ -0,0 +1,12 @@
namespace HKW.HKWUtils.Observable;
/// <summary>
/// 通知属性改变后接口
/// </summary>
public interface INotifyPropertyChangedX<TSender>
{
/// <summary>
/// 通知属性改变后事件
/// </summary>
public event PropertyChangedXEventHandler<TSender>? PropertyChangedX;
}

View File

@ -0,0 +1,12 @@
namespace HKW.HKWUtils.Observable;
/// <summary>
/// 通知属性改变前接口
/// </summary>
public interface INotifyPropertyChangingX<TSender>
{
/// <summary>
/// 属性改变前事件
/// </summary>
public event PropertyChangingXEventHandler<TSender>? PropertyChangingX;
}

View File

@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace HKW.HKWUtils.Observable;
/// <summary>
/// 可观察对象
/// <para>示例:<code><![CDATA[
/// public class ObservableClassExample : ObservableClass<ObservableClassExample>
/// {
/// int _value = 0;
/// public int Value
/// {
/// get => _value;
/// set => SetProperty(ref _value, value);
/// }
/// }]]></code></para>
/// </summary>
public abstract class ObservableClass<TObject>
: INotifyPropertyChanging,
INotifyPropertyChanged,
INotifyPropertyChangingX<TObject>,
INotifyPropertyChangedX<TObject>
where TObject : ObservableClass<TObject>
{
#region OnPropertyChange
/// <summary>
/// 设置属性值
/// </summary>
/// <param name="value">值</param>
/// <param name="newValue">新值</param>
/// <param name="propertyName">属性名称</param>
/// <returns>成功为 <see langword="true"/> 失败为 <see langword="false"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected virtual bool SetProperty<TValue>(
ref TValue value,
TValue newValue,
[CallerMemberName] string propertyName = null!
)
{
if (EqualityComparer<TValue>.Default.Equals(value, newValue) is true)
return false;
var oldValue = value;
if (OnPropertyChanging(oldValue, newValue, propertyName))
return false;
value = newValue;
OnPropertyChanged(oldValue, newValue, propertyName);
return true;
}
/// <summary>
/// 属性改变前
/// </summary>
/// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param>
/// <param name="propertyName">属性名称</param>
/// <returns>取消为 <see langword="true"/> 否则为 <see langword="false"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected virtual bool OnPropertyChanging(
object? oldValue,
object? newValue,
[CallerMemberName] string propertyName = null!
)
{
PropertyChanging?.Invoke(this, new(propertyName));
if (PropertyChangingX is null)
return false;
var e = new PropertyChangingXEventArgs(propertyName, oldValue, newValue);
PropertyChangingX?.Invoke((TObject)this, e);
if (e.Cancel)
PropertyChanged?.Invoke(this, new(propertyName));
return e.Cancel;
}
/// <summary>
/// 属性改变后
/// </summary>
/// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param>
/// <param name="propertyName">属性名称</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected virtual void OnPropertyChanged(
object? oldValue,
object? newValue,
[CallerMemberName] string propertyName = null!
)
{
PropertyChanged?.Invoke(this, new(propertyName));
PropertyChangedX?.Invoke((TObject)this, new(propertyName, oldValue, newValue));
}
#endregion
#region Event
/// <inheritdoc/>
public event PropertyChangingEventHandler? PropertyChanging;
/// <inheritdoc/>
public event PropertyChangedEventHandler? PropertyChanged;
/// <inheritdoc/>
public event PropertyChangingXEventHandler<TObject>? PropertyChangingX;
/// <inheritdoc/>
public event PropertyChangedXEventHandler<TObject>? PropertyChangedX;
#endregion
}

View File

@ -0,0 +1,33 @@
namespace HKW.HKWUtils.Observable;
/// <summary>
/// 属性改变后事件参数
/// </summary>
public class PropertyChangedXEventArgs : EventArgs
{
/// <summary>
/// 属性名
/// </summary>
public string PropertyName { get; }
/// <summary>
/// 旧值
/// </summary>
public object? OldValue { get; }
/// <summary>
/// 新值
/// </summary>
public object? NewValue { get; }
/// <inheritdoc/>
/// <param name="propertyName">属性名</param>
/// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param>
public PropertyChangedXEventArgs(string propertyName, object? oldValue, object? newValue)
{
PropertyName = propertyName;
OldValue = oldValue;
NewValue = newValue;
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HKW.HKWUtils.Observable;
/// <summary>
/// 属性改变后事件
/// </summary>
/// <param name="sender">发送者</param>
/// <param name="e">参数</param>
public delegate void PropertyChangedXEventHandler<TSender>(
TSender sender,
PropertyChangedXEventArgs e
);

View File

@ -0,0 +1,35 @@
using System.ComponentModel;
namespace HKW.HKWUtils.Observable;
/// <summary>
/// 属性改变前事件参数
/// </summary>
public class PropertyChangingXEventArgs : CancelEventArgs
{
/// <summary>
/// 属性名
/// </summary>
public string PropertyName { get; }
/// <summary>
/// 旧值
/// </summary>
public object? OldValue { get; }
/// <summary>
/// 新值
/// </summary>
public object? NewValue { get; }
/// <inheritdoc/>
/// <param name="propertyName">属性名</param>
/// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param>
public PropertyChangingXEventArgs(string propertyName, object? oldValue, object? newValue)
{
PropertyName = propertyName;
OldValue = oldValue;
NewValue = newValue;
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HKW.HKWUtils.Observable;
/// <summary>
/// 属性改变前事件
/// </summary>
/// <param name="sender">发送者</param>
/// <param name="e">参数</param>
public delegate void PropertyChangingXEventHandler<TSender>(
TSender sender,
PropertyChangingXEventArgs e
);

View File

@ -7,10 +7,10 @@ namespace HKW.HKWUtils.Observable;
/// <summary>
/// 异步执行命令事件
/// </summary>
public delegate Task AsyncExecuteEventHandler();
public delegate Task ExecuteAsyncEventHandler();
/// <summary>
/// 异步执行命令事件
/// </summary>
/// <param name="parameter">值</param>
public delegate Task AsyncExecuteEventHandler<T>(T parameter);
public delegate Task ExecuteAsyncEventHandler<T>(T parameter);

View File

@ -10,41 +10,43 @@ namespace HKW.HKWUtils.Observable;
/// <summary>
/// 可观察命令
/// </summary>
[DebuggerDisplay("\\{ObservableCommand, CanExecute = {CanExecuteProperty.Value}\\}")]
public class ObservableCommand : ICommand
[DebuggerDisplay("\\{ObservableCommand, CanExecute = {IsCanExecute.Value}\\}")]
public class ObservableCommand : ObservableClass<ObservableCommand>, ICommand
{
bool _isCanExecute = true;
/// <summary>
/// 能执行的属性
/// </summary>
public ObservableValue<bool> CanExecuteProperty { get; } = new(true);
public bool IsCanExecute
{
get => _isCanExecute;
set => SetProperty(ref _isCanExecute, value);
}
bool _currentCanExecute = true;
/// <summary>
/// 当前可执行状态
/// <para>
/// 在执行异步事件时会强制为 <see langword="false"/>, 但异步结束后会恢复为 <see cref="IsCanExecute"/> 的值
/// </para>
/// </summary>
public ObservableValue<bool> CurrentCanExecute { get; } = new(true);
public bool CurrentCanExecute
{
get => _currentCanExecute;
private set => SetProperty(ref _currentCanExecute, value);
}
/// <inheritdoc/>
public ObservableCommand()
{
CanExecuteProperty.PropertyChanged += InvokeCanExecuteChanged;
CurrentCanExecute.PropertyChanged += InvokeCanExecuteChanged;
CurrentCanExecute.ValueChanging += CurrentCanExecute_ValueChanging;
PropertyChanged += OnCanExecuteChanged;
}
private void CurrentCanExecute_ValueChanging(
ObservableValue<bool> sender,
ValueChangingEventArgs<bool> e
)
private void OnCanExecuteChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.NewValue is true && CanExecuteProperty.Value is false)
e.Cancel = true;
else
e.Cancel = false;
}
private void InvokeCanExecuteChanged(object? sender, PropertyChangedEventArgs e)
{
CanExecuteChanged?.Invoke(sender, e);
CanExecuteChanged?.Invoke(this, new());
}
#region ICommand
@ -55,7 +57,7 @@ public class ObservableCommand : ICommand
/// <returns>能被执行为 <see langword="true"/> 否则为 <see langword="false"/></returns>
public bool CanExecute(object? parameter)
{
return CurrentCanExecute.Value && CanExecuteProperty.Value;
return CurrentCanExecute && IsCanExecute;
}
/// <summary>
@ -64,71 +66,36 @@ public class ObservableCommand : ICommand
/// <param name="parameter">参数</param>
public async void Execute(object? parameter)
{
if (IsCanExecute is not true)
return;
ExecuteCommand?.Invoke();
await ExecuteAsync();
}
/// <summary>
/// 执行异步方法, 会在等待中关闭按钮的可执行性, 完成后恢复
/// 执行异步方法, 会在等待中修改 <see cref="CurrentCanExecute"/>, 完成后恢复
/// <para>
/// 若要在执行此方法时触发 <see cref="ExecuteCommand"/> 事件, 请将 <paramref name="runAlone"/> 设置为 <see langword="true"/>
/// </para>
/// </summary>
/// <returns>等待</returns>
private async Task ExecuteAsync()
/// <param name="runAlone">设置为 <see langword="true"/> 时触发 <see cref="ExecuteCommand"/> 事件</param>
/// <returns>任务</returns>
public async Task ExecuteAsync(bool runAlone = false)
{
if (AsyncExecuteCommand is null)
if (IsCanExecute is not true)
return;
CurrentCanExecute.Value = false;
if (runAlone)
ExecuteCommand?.Invoke();
if (ExecuteAsyncCommand is null)
return;
CurrentCanExecute = false;
foreach (
var asyncEvent in AsyncExecuteCommand
var asyncEvent in ExecuteAsyncCommand
.GetInvocationList()
.Cast<AsyncExecuteEventHandler>()
.Cast<ExecuteAsyncEventHandler>()
)
await asyncEvent.Invoke();
CurrentCanExecute.Value = true;
}
#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.EnumValue = "A"; // execute this
/// // result: value.EnumValue == "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 args = new CancelEventArgs();
NotifyCanExecuteReceived?.Invoke(this, args);
CanExecuteProperty.Value = args.Cancel;
CurrentCanExecute = true;
}
#endregion
@ -146,11 +113,6 @@ public class ObservableCommand : ICommand
/// <summary>
/// 异步执行事件
/// </summary>
public event AsyncExecuteEventHandler? AsyncExecuteCommand;
/// <summary>
/// 可执行通知接收器事件
/// </summary>
public event NotifyReceivedEventHandler? NotifyCanExecuteReceived;
public event ExecuteAsyncEventHandler? ExecuteAsyncCommand;
#endregion
}

View File

@ -8,110 +8,112 @@ using System.Windows.Input;
namespace HKW.HKWUtils.Observable;
/// <summary>
/// 参数的可观察命令
/// 具有参数的可观察命令
/// </summary>
/// <typeparam name="T">参数类型</typeparam>
[DebuggerDisplay("\\{ObservableCommand, CanExecute = {CanExecuteProperty.Value}\\}")]
public class ObservableCommand<T> : ICommand
where T : notnull
[DebuggerDisplay("\\{ObservableCommand, CanExecute = {IsCanExecute.Value}\\}")]
public class ObservableCommand<T> : ObservableClass<ObservableCommand>, ICommand
{
/// <inheritdoc cref="ObservableCommand.CanExecuteProperty"/>
public ObservableValue<bool> CanExecuteProperty { get; } = new(true);
bool _isCanExecute = true;
/// <summary>
/// 能执行的属性
/// </summary>
public bool IsCanExecute
{
get => _isCanExecute;
set => SetProperty(ref _isCanExecute, value);
}
bool _currentCanExecute = true;
/// <summary>
/// 当前可执行状态
/// <para>
/// 在执行异步事件时会强制为 <see langword="false"/>, 但异步结束后会恢复为 <see cref="IsCanExecute"/> 的值
/// </para>
/// </summary>
public ObservableValue<bool> CurrentCanExecute { get; } = new(true);
public bool CurrentCanExecute
{
get => _currentCanExecute;
private set => SetProperty(ref _currentCanExecute, value);
}
/// <inheritdoc/>
public ObservableCommand()
{
CanExecuteProperty.PropertyChanged += InvokeCanExecuteChanged;
CurrentCanExecute.PropertyChanged += InvokeCanExecuteChanged;
CurrentCanExecute.ValueChanging += CurrentCanExecute_ValueChanging;
PropertyChanged += OnCanExecuteChanged;
}
private void CurrentCanExecute_ValueChanging(
ObservableValue<bool> sender,
ValueChangingEventArgs<bool> e
)
private void OnCanExecuteChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.NewValue is true && CanExecuteProperty.Value is false)
e.Cancel = true;
else
e.Cancel = false;
}
private void InvokeCanExecuteChanged(object? sender, PropertyChangedEventArgs e)
{
CanExecuteChanged?.Invoke(sender, e);
CanExecuteChanged?.Invoke(this, new());
}
#region ICommand
/// <inheritdoc cref="ObservableCommand.CanExecute(object?)"/>
/// <summary>
/// 能否被执行
/// </summary>
/// <param name="parameter">参数</param>
/// <returns>能被执行为 <see langword="true"/> 否则为 <see langword="false"/></returns>
public bool CanExecute(object? parameter)
{
return CurrentCanExecute.Value && CanExecuteProperty.Value;
return CurrentCanExecute && IsCanExecute;
}
/// <inheritdoc cref="ObservableCommand.Execute(object?)"/>
/// <summary>
/// 执行方法
/// </summary>
/// <param name="parameter">参数</param>
public async void Execute(object? parameter)
{
if (IsCanExecute is not true)
return;
ExecuteCommand?.Invoke((T)parameter!);
await ExecuteAsync((T)parameter!);
}
/// <inheritdoc cref="ObservableCommand.ExecuteAsync"/>
/// <summary>
/// 执行异步方法, 会在等待中修改 <see cref="CurrentCanExecute"/>, 完成后恢复
/// <para>
/// 若要在执行此方法时触发 <see cref="ExecuteCommand"/> 事件, 请将 <paramref name="runAlone"/> 设置为 <see langword="true"/>
/// </para>
/// </summary>
/// <param name="parameter">参数</param>
private async Task ExecuteAsync(T parameter)
/// <param name="runAlone">设置为 <see langword="true"/> 时触发 <see cref="ExecuteCommand"/> 事件</param>
/// <returns>任务</returns>
public async Task ExecuteAsync(T parameter, bool runAlone = false)
{
if (AsyncExecuteCommand is null)
if (IsCanExecute is not true)
return;
CurrentCanExecute.Value = false;
if (runAlone)
ExecuteCommand?.Invoke(parameter);
if (ExecuteAsyncCommand is null)
return;
CurrentCanExecute = false;
foreach (
var asyncEvent in AsyncExecuteCommand
var asyncEvent in ExecuteAsyncCommand
.GetInvocationList()
.Cast<AsyncExecuteEventHandler<T>>()
.Cast<ExecuteAsyncEventHandler<T>>()
)
await asyncEvent.Invoke(parameter);
CurrentCanExecute.Value = true;
}
#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 args = new CancelEventArgs();
NotifyCanExecuteReceived?.Invoke(this, args);
CanExecuteProperty.Value = args.Cancel;
CurrentCanExecute = true;
}
#endregion
#region Event
/// <inheritdoc cref="ObservableCommand.CanExecuteChanged"/>
/// <summary>
/// 能否执行属性改变后事件
/// </summary>
public event EventHandler? CanExecuteChanged;
/// <inheritdoc cref="ObservableCommand.ExecuteCommand"/>
/// <summary>
/// 执行事件
/// </summary>
public event ExecuteEventHandler<T>? ExecuteCommand;
/// <inheritdoc cref="ObservableCommand.AsyncExecuteCommand"/>
public event AsyncExecuteEventHandler<T>? AsyncExecuteCommand;
/// <inheritdoc cref="ObservableCommand.NotifyCanExecuteReceived"/>
public event NotifyReceivedEventHandler? NotifyCanExecuteReceived;
/// <summary>
/// 异步执行事件
/// </summary>
public event ExecuteAsyncEventHandler<T>? ExecuteAsyncCommand;
#endregion
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace HKW.HKWUtils.Observable;
@ -169,55 +170,36 @@ public class ObservableValue<T>
}
/// <inheritdoc/>
public override bool Equals(object? obj)
public override int GetHashCode()
{
return Equals(obj as ObservableValue<T>);
return Value?.GetHashCode() ?? 0;
}
/// <inheritdoc/>
public override int GetHashCode()
public override bool Equals(object? obj)
{
return Guid.GetHashCode();
return obj is ObservableValue<T> value
&& EqualityComparer<T>.Default.Equals(Value, value.Value);
}
/// <inheritdoc/>
public bool Equals(ObservableValue<T>? other)
{
return Guid.Equals(other?.Guid) is true;
return other is ObservableValue<T> value
&& EqualityComparer<T>.Default.Equals(Value, value.Value);
}
/// <summary>
/// 值相等
/// </summary>
/// <param name="other">其它可观察值</param>
/// <returns>相等为 <see langword="true"/> 否则为 <see langword="false"/></returns>
public bool ValueEquals(ObservableValue<T> other)
/// <inheritdoc/>
public static bool operator ==(ObservableValue<T> a, ObservableValue<T> b)
{
return Value?.Equals(other.Value) is true;
return EqualityComparer<T>.Default.Equals(a.Value, b.Value);
}
/// <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)
/// <inheritdoc/>
public static bool operator !=(ObservableValue<T> a, ObservableValue<T> b)
{
return value1.Value?.Equals(value2.Value) is true;
return (a == b) is not 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

9
VPet.ModMaker/Usings.cs Normal file
View File

@ -0,0 +1,9 @@
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;
global using global::HKW.HKWUtils;
global using global::HKW.HKWUtils.Observable;

View File

@ -12,10 +12,9 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using VPet.ModMaker.Models.ModModel;
using VPet_Simulator.Core;
namespace HKW.Models;
namespace HKW.HKWUtils;
/// <summary>
/// 拓展
@ -45,10 +44,9 @@ public static class Extensions
/// <param name="source">图像资源</param>
public static void CloseStream(this ImageSource source)
{
if (source is BitmapImage image)
{
image.StreamSource?.Close();
}
if (source is not BitmapImage image)
return;
image.StreamSource?.Close();
}
/// <summary>
@ -140,16 +138,6 @@ public static class Extensions
return true;
}
/// <summary>
/// 是含有名称的动画
/// </summary>
/// <param name="graphType"></param>
/// <returns></returns>
public static bool IsHasNameAnime(this GraphInfo.GraphType graphType)
{
return AnimeTypeModel.HasNameAnimes.Contains(graphType);
}
/// <summary>
/// 流内容对比
/// </summary>

View File

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HKW.HKWUtils;
/// <summary>
/// 哈希值
/// </summary>
public class HashCode
{
/// <summary>
/// 默认种子
/// </summary>
public const int DefaultSeed = 114514;
/// <summary>
/// 默认系数
/// </summary>
public const int DefaultFactor = 1919810;
/// <summary>
/// 组合哈希值
/// </summary>
/// <param name="values">值</param>
/// <returns>组合的哈希值</returns>
public static int Combine(params object[] values)
{
return CustomHash(DefaultSeed, DefaultFactor, values.Select(v => v.GetHashCode()));
}
/// <summary>
/// 组合哈希值
/// </summary>
/// <param name="seed">种子</param>
/// <param name="factor">系数</param>
/// <param name="values">值</param>
/// <returns>组合的哈希值</returns>
public static int Combine(int seed, int factor, params object[] values)
{
return CustomHash(seed, factor, values.Select(v => v.GetHashCode()));
}
/// <summary>
/// 自定义组合哈希
/// </summary>
/// <param name="seed">种子</param>
/// <param name="factor">系数</param>
/// <param name="collection">哈希集合</param>
/// <returns>组合的哈希</returns>
public static int CustomHash(int seed, int factor, IEnumerable<int> collection)
{
int hash = seed;
foreach (int i in collection)
hash = unchecked((hash * factor) + i);
return hash;
}
/// <summary>
/// 自定义组合哈希
/// </summary>
/// <param name="seed">种子</param>
/// <param name="factor">系数</param>
/// <param name="values">哈希集合</param>
/// <returns>组合的哈希</returns>
public static int CustomHash(int seed, int factor, params int[] values)
{
return CustomHash(seed, factor, collection: values);
}
}

View File

@ -0,0 +1,81 @@
using HKW.HKWUtils.Observable;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace HKW.HKWUtils;
/// <summary>
/// 可观察的枚举标签模型
/// </summary>
/// <typeparam name="T">枚举类型</typeparam>
public class ObservableEnumFlags<T> : ObservableClass<ObservableEnumFlags<T>>
where T : Enum
{
private T _EnumValue;
public T EnumValue
{
get => _EnumValue;
set => SetProperty(ref _EnumValue, value);
}
/// <summary>
/// 添加枚举命令
/// </summary>
public ObservableCommand<T> AddCommand { get; } = new();
/// <summary>
/// 删除枚举命令
/// </summary>
public ObservableCommand<T> RemoveCommand { get; } = new();
/// <summary>
/// 枚举类型
/// </summary>
public Type EnumType = typeof(T);
/// <summary>
/// 枚举基类
/// </summary>
public Type UnderlyingType { get; } = Enum.GetUnderlyingType(typeof(T));
public ObservableEnumFlags()
{
if (Attribute.IsDefined(EnumType, typeof(FlagsAttribute)) is false)
throw new Exception($"此枚举类型未使用特性 [{nameof(FlagsAttribute)}]");
AddCommand.ExecuteCommand += AddCommand_Execute;
RemoveCommand.ExecuteCommand += RemoveCommand_Execute;
}
public ObservableEnumFlags(T value)
: this()
{
EnumValue = value;
}
private void AddCommand_Execute(T v)
{
if (UnderlyingType == typeof(int))
{
EnumValue = (T)
Enum.Parse(EnumType, (Convert.ToInt32(EnumValue) | Convert.ToInt32(v)).ToString());
}
else
throw new NotImplementedException($"Value type: {UnderlyingType}");
}
private void RemoveCommand_Execute(T v)
{
if (UnderlyingType == typeof(int))
{
EnumValue = (T)
Enum.Parse(EnumType, (Convert.ToInt32(EnumValue) & ~Convert.ToInt32(v)).ToString());
}
else
throw new NotImplementedException($"Value type: {UnderlyingType}");
}
}

View File

@ -0,0 +1,79 @@
using HKW.HKWUtils.Observable;
namespace HKW.HKWUtils;
/// <summary>
/// 可观察地点
/// </summary>
/// <typeparam name="T">类型</typeparam>
public class ObservablePoint<T>
: ObservableClass<ObservablePoint<T>>,
IEquatable<ObservablePoint<T>>
{
private T _x;
public T X
{
get => _x;
set => SetProperty(ref _x, value);
}
private T _y;
public T Y
{
get => _y;
set => SetProperty(ref _y, value);
}
public ObservablePoint() { }
public ObservablePoint(T x, T y)
{
X = x;
Y = y;
}
/// <summary>
/// 复制一个新的对象
/// </summary>
/// <returns>新对象</returns>
public ObservablePoint<T> Copy()
{
return new(X, Y);
}
#region Other
/// <inheritdoc/>
public override int GetHashCode()
{
return HashCode.Combine(X, Y);
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is ObservablePoint<T> temp
&& EqualityComparer<T>.Default.Equals(X, temp.X)
&& EqualityComparer<T>.Default.Equals(Y, temp.Y);
}
/// <inheritdoc/>
public bool Equals(ObservablePoint<T>? other)
{
return Equals(obj: other);
}
/// <inheritdoc/>
public static bool operator ==(ObservablePoint<T> a, ObservablePoint<T> b)
{
return Equals(a, b);
}
/// <inheritdoc/>
public static bool operator !=(ObservablePoint<T> a, ObservablePoint<T> b)
{
return Equals(a, b) is not true;
}
#endregion
}

View File

@ -0,0 +1,82 @@
using HKW.HKWUtils;
using HKW.HKWUtils.Observable;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace HKW.HKWUtils;
/// <summary>
/// 可观察的范围
/// </summary>
/// <typeparam name="T">类型</typeparam>
public class ObservableRange<T>
: ObservableClass<ObservableRange<T>>,
IEquatable<ObservableRange<T>>
{
private T _min;
public T Min
{
get => _min;
set => SetProperty(ref _min, value);
}
private T _max;
public T Max
{
get => _max;
set => SetProperty(ref _max, value);
}
public ObservableRange() { }
public ObservableRange(T min, T max)
{
_min = min;
_max = max;
}
/// <summary>
/// 复制一个新的对象
/// </summary>
/// <returns>新对象</returns>
public ObservableRange<T> Copy()
{
return new(Min, Max);
}
#region Other
/// <inheritdoc/>
public override int GetHashCode()
{
return HashCode.Combine(Min, Max);
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is ObservableRange<T> temp
&& EqualityComparer<T>.Default.Equals(Min, temp.Min)
&& EqualityComparer<T>.Default.Equals(Max, temp.Max);
}
/// <inheritdoc/>
public bool Equals(ObservableRange<T>? other)
{
return Equals(obj: other);
}
/// <inheritdoc/>
public static bool operator ==(ObservableRange<T> a, ObservableRange<T> b)
{
return Equals(a, b);
}
/// <inheritdoc/>
public static bool operator !=(ObservableRange<T> a, ObservableRange<T> b)
{
return Equals(a, b) is not true;
}
#endregion
}

View File

@ -0,0 +1,91 @@
using HKW.HKWUtils.Observable;
namespace HKW.HKWUtils;
public class ObservableRect<T> : ObservableClass<ObservableRect<T>>, IEquatable<ObservableRect<T>>
{
private T _x;
public T X
{
get => _x;
set => SetProperty(ref _x, value);
}
private T _y;
public T Y
{
get => _y;
set => SetProperty(ref _y, value);
}
private T _width;
public T Width
{
get => _width;
set => SetProperty(ref _width, value);
}
private T _heigth;
public T Height
{
get => _heigth;
set => SetProperty(ref _heigth, value);
}
public ObservableRect() { }
public ObservableRect(T x, T y, T width, T hetght)
{
X = x;
Y = y;
Width = width;
Height = hetght;
}
/// <summary>
/// 复制一个新的对象
/// </summary>
/// <returns>新对象</returns>
public ObservableRect<T> Copy()
{
return new(X, Y, Width, Height);
}
#region Other
/// <inheritdoc/>
public override int GetHashCode()
{
return HashCode.Combine(X, Y, Width, Height);
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is ObservableRect<T> temp
&& EqualityComparer<T>.Default.Equals(X, temp.X)
&& EqualityComparer<T>.Default.Equals(Y, temp.Y)
&& EqualityComparer<T>.Default.Equals(Width, temp.Width)
&& EqualityComparer<T>.Default.Equals(Height, temp.Height);
}
/// <inheritdoc/>
public bool Equals(ObservableRect<T>? other)
{
return Equals(obj: other);
}
/// <inheritdoc/>
public static bool operator ==(ObservableRect<T> a, ObservableRect<T> b)
{
return Equals(a, b);
}
/// <inheritdoc/>
public static bool operator !=(ObservableRect<T> a, ObservableRect<T> b)
{
return Equals(a, b) is not true;
}
#endregion
}

View File

@ -5,9 +5,8 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Media.Imaging;
using VPet.ModMaker;
namespace HKW.Models;
namespace HKW.HKWUtils;
/// <summary>
/// 工具

View File

@ -98,6 +98,7 @@
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="Usings.cs" />
<Compile Include="Converters\BrushToMediaColorConverter.cs" />
<Compile Include="Converters\CalculatorConverter.cs" />
<Compile Include="Converters\EqualsConverter.cs" />
@ -110,12 +111,37 @@
<Compile Include="Converters\RatioMarginConverter.cs" />
<Compile Include="Converters\MaxConverter.cs" />
<Compile Include="Converters\MarginConverter.cs" />
<Compile Include="SimpleObservable\ObservableClass\INotifyPropertyChangedX.cs" />
<Compile Include="SimpleObservable\ObservableClass\INotifyPropertyChangingX.cs" />
<Compile Include="SimpleObservable\ObservableClass\ObservableClass.cs" />
<Compile Include="SimpleObservable\ObservableClass\PropertyChangedXEventArgs.cs" />
<Compile Include="SimpleObservable\ObservableClass\PropertyChangedXEventHandler.cs" />
<Compile Include="SimpleObservable\ObservableClass\PropertyChangingXEventArgs.cs" />
<Compile Include="SimpleObservable\ObservableClass\PropertyChangingXEventHandler.cs" />
<Compile Include="SimpleObservable\ObservableCommand\ExecuteAsyncEventHandler.cs" />
<Compile Include="SimpleObservable\ObservableCommand\ExecuteEventHandler.cs" />
<Compile Include="SimpleObservable\ObservableCommand\NotifyReceivedEventHandler.cs" />
<Compile Include="SimpleObservable\ObservableCommand\ObservableCommand.cs" />
<Compile Include="SimpleObservable\ObservableCommand\ObservableCommandT.cs" />
<Compile Include="SimpleObservable\ObservableValue\NotifySenderPropertyChangedHandler.cs" />
<Compile Include="SimpleObservable\ObservableValue\ObservableValue.cs" />
<Compile Include="SimpleObservable\ObservableValue\ObservableValueGroup.cs" />
<Compile Include="SimpleObservable\ObservableValue\ValueChangedEventArgs.cs" />
<Compile Include="SimpleObservable\ObservableValue\ValueChangedEventHandler.cs" />
<Compile Include="SimpleObservable\ObservableValue\ValueChangingEventArgs.cs" />
<Compile Include="SimpleObservable\ObservableValue\ValueChangingEventHandler.cs" />
<Compile Include="Utils\ObservableEnumFlags.cs" />
<Compile Include="Utils\Expansions.cs" />
<Compile Include="Utils\HashCode.cs" />
<Compile Include="Utils\ObservableRange.cs" />
<Compile Include="Utils\ObservablePoint.cs" />
<Compile Include="Utils\ObservableRect.cs" />
<Compile Include="Utils\Utils.cs" />
<Compile Include="Models\EnumFlagsVM.cs" />
<Compile Include="Models\I18nData.cs" />
<Compile Include="Models\ModModel\AnimeModel.cs" />
<Compile Include="Models\ModModel\AnimeTypeModel.cs" />
<Compile Include="Models\ModModel\ClickTextModel.cs" />
<Compile Include="Models\Expansions.cs" />
<Compile Include="Models\ModModel\FoodAnimeModel.cs" />
<Compile Include="Models\ModModel\FoodAnimeTypeModel.cs" />
<Compile Include="Models\ModModel\FoodLocationModel.cs" />
@ -136,18 +162,6 @@
<Compile Include="Models\ModModel\WorkModel.cs" />
<Compile Include="Resources\NativeResources.cs" />
<Compile Include="NativeStyles.cs" />
<Compile Include="SimpleObservable\ObservableCommand\AsyncExecuteEventHandler.cs" />
<Compile Include="SimpleObservable\ObservableCommand\ExecuteEventHandler.cs" />
<Compile Include="SimpleObservable\ObservableCommand\NotifyReceivedEventHandler.cs" />
<Compile Include="SimpleObservable\ObservableCommand\ObservableCommand.cs" />
<Compile Include="SimpleObservable\ObservableCommand\ObservableCommandT.cs" />
<Compile Include="SimpleObservable\ObservableValue\NotifySenderPropertyChangedHandler.cs" />
<Compile Include="SimpleObservable\ObservableValue\ObservableValue.cs" />
<Compile Include="SimpleObservable\ObservableValue\ObservableValueGroup.cs" />
<Compile Include="SimpleObservable\ObservableValue\ValueChangedEventArgs.cs" />
<Compile Include="SimpleObservable\ObservableValue\ValueChangedEventHandler.cs" />
<Compile Include="SimpleObservable\ObservableValue\ValueChangingEventArgs.cs" />
<Compile Include="SimpleObservable\ObservableValue\ValueChangingEventHandler.cs" />
<Compile Include="ViewModels\ModEdit\AddCultureWindowVM.cs" />
<Compile Include="ViewModels\ModEdit\AnimeEdit\FoodAnimeEditWindowVM.cs" />
<Compile Include="ViewModels\ModEdit\AnimeEdit\AnimeEditWindowVM.cs" />
@ -217,7 +231,6 @@
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<Compile Include="Models\Utils.cs" />
<Compile Include="Views\ModEdit\FoodEdit\FoodEditWindow.xaml.cs">
<DependentUpon>FoodEditWindow.xaml</DependentUpon>
</Compile>

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using LinePutScript.Localization.WPF;
using System;
using System.Collections.Generic;

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using LinePutScript.Localization.WPF;
using Microsoft.Win32;
using System;
@ -123,7 +123,7 @@ public class AnimeEditWindowVM
CurrentAnimeModel.ValueChanged += CurrentAnimeModel_ValueChanged;
PlayCommand.AsyncExecuteCommand += PlayCommand_AsyncExecuteEvent;
PlayCommand.ExecuteAsyncCommand += PlayCommand_AsyncExecuteEvent;
StopCommand.ExecuteCommand += StopCommand_ExecuteEvent;
AddAnimeCommand.ExecuteCommand += AddAnimeCommand_ExecuteEvent;
RemoveAnimeCommand.ExecuteCommand += RemoveAnimeCommand_ExecuteEvent;

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using LinePutScript.Localization.WPF;
using Panuon.WPF.UI;
using System;

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using LinePutScript.Localization.WPF;
using Microsoft.Win32;
using System;
@ -24,7 +24,6 @@ public class FoodAnimeEditWindowVM
/// </summary>
public PetModel CurrentPet { get; set; }
// TODO: 使用内部资源
/// <summary>
/// 默认食物图片
/// </summary>
@ -211,7 +210,7 @@ public class FoodAnimeEditWindowVM
CurrentAnimeModel.ValueChanged += CurrentAnimeModel_ValueChanged;
PlayCommand.AsyncExecuteCommand += PlayCommand_AsyncExecuteEvent;
PlayCommand.ExecuteAsyncCommand += PlayCommand_AsyncExecuteEvent;
StopCommand.ExecuteCommand += StopCommand_ExecuteEvent;
ReplaceFoodImageCommand.ExecuteCommand += ChangeFoodImageCommand_ExecuteEvent;
ResetFoodImageCommand.ExecuteCommand += ResetFoodImageCommand_ExecuteEvent;

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using LinePutScript.Localization.WPF;
using System;
using System.Collections.Generic;

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using LinePutScript.Localization.WPF;
using Microsoft.Win32;
using System;

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using LinePutScript.Localization.WPF;
using System;
using System.Collections.Generic;

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@ -296,7 +296,7 @@ public class I18nEditWindowVM
{
foreach (var pet in model.Pets)
{
if (pet.IsSimplePetModel)
if (pet.FromMain.Value)
continue;
AddData(pet.Id, pet, (m) => m.Name);
AddData(pet.PetNameId, pet, (m) => m.PetName);

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using LinePutScript.Localization.WPF;
using System;
using System.Collections.Generic;

View File

@ -18,7 +18,6 @@ using Panuon.WPF.UI;
using VPet.ModMaker.Views.ModEdit.I18nEdit;
using System.Globalization;
using Ookii.Dialogs.Wpf;
using HKW.Models;
namespace VPet.ModMaker.ViewModels.ModEdit;

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using LinePutScript.Localization.WPF;
using Microsoft.Win32;
using System;

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using LinePutScript.Localization.WPF;
using System;
using System.Collections.Generic;

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using LinePutScript.Localization.WPF;
using Microsoft.Win32;
using System;

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using LinePutScript.Localization.WPF;
using System;
using System.Collections.Generic;
@ -66,11 +66,12 @@ public class PetPageVM
public void Edit(PetModel model)
{
if (model.IsSimplePetModel)
{
MessageBox.Show("这是本体自带的宠物, 无法编辑".Translate());
if (
model.FromMain.Value
&& MessageBox.Show("这是本体自带的宠物, 确定要编辑吗".Translate(), "", MessageBoxButton.YesNo)
is not MessageBoxResult.Yes
)
return;
}
var window = new PetEditWindow();
var vm = window.ViewModel;
vm.OldPet = model;
@ -86,7 +87,7 @@ public class PetPageVM
private void Remove(PetModel model)
{
if (model.IsSimplePetModel)
if (model.FromMain.Value)
{
MessageBox.Show("这是本体自带的宠物, 无法删除".Translate());
return;

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using LinePutScript.Localization.WPF;
using System;
using System.Collections.Generic;

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using LinePutScript.Localization.WPF;
using Microsoft.Win32;
using System;

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using LinePutScript.Localization.WPF;
using System;
using System.Collections.Generic;

View File

@ -1,5 +1,4 @@
using HKW.Models;
using LinePutScript.Localization.WPF;
using LinePutScript.Localization.WPF;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@ -39,7 +39,6 @@
x:Name="ComboBox_Pet"
Grid.Column="1"
pu:ComboBoxHelper.Watermark="{ll:Str 选择宠物}"
DisplayMemberPath="Id.Value"
ItemsSource="{Binding Pets}"
SelectedItem="{Binding CurrentPet.Value}"
Style="{DynamicResource StandardComboBoxStyle}">
@ -48,6 +47,14 @@
<Setter Property="ToolTip" Value="{Binding CurrentI18nData.Value.Name.Value}" />
</Style>
</ComboBox.ItemContainerStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Id.Value}" />
<TextBlock IsEnabled="{Binding FromMain, Converter={StaticResource FalseToCollapsedConverter}}" Text="{ll:Str {} (来自本体)}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
<Grid Grid.Row="1">

View File

@ -348,7 +348,7 @@
<pu:NumberInput
Grid.Row="3"
Grid.Column="1"
Value="{Binding Rect.Width.Value}" />
Value="{Binding Rect.Width}" />
<Grid Grid.Row="4" Grid.ColumnSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition />
@ -360,7 +360,7 @@
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Content="X:" />
<pu:NumberInput Grid.Column="1" Value="{Binding Rect.X.Value}" />
<pu:NumberInput Grid.Column="1" Value="{Binding Rect.X}" />
</Grid>
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
@ -368,7 +368,7 @@
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Content="Y:" />
<pu:NumberInput Grid.Column="1" Value="{Binding Rect.Y.Value}" />
<pu:NumberInput Grid.Column="1" Value="{Binding Rect.Y}" />
</Grid>
</Grid>
</Grid>
@ -380,7 +380,6 @@
</Expander>
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
@ -433,7 +432,7 @@
<Setter Property="Width">
<Setter.Value>
<MultiBinding Converter="{StaticResource CalculatorConverter}" ConverterParameter="*">
<Binding Path="CurrentFoodLocationModel.Value.Rect.Width.Value" />
<Binding Path="CurrentFoodLocationModel.Value.Rect.Width" />
<Binding Path="LengthRatio.Value" />
</MultiBinding>
</Setter.Value>
@ -442,8 +441,8 @@
<Setter.Value>
<MultiBinding Converter="{StaticResource RatioMarginConverter}">
<Binding Path="LengthRatio.Value" />
<Binding Path="CurrentFoodLocationModel.Value.Rect.X.Value" />
<Binding Path="CurrentFoodLocationModel.Value.Rect.Y.Value" />
<Binding Path="CurrentFoodLocationModel.Value.Rect.X" />
<Binding Path="CurrentFoodLocationModel.Value.Rect.Y" />
</MultiBinding>
</Setter.Value>
</Setter>
@ -481,7 +480,7 @@
<Setter Property="Width">
<Setter.Value>
<MultiBinding Converter="{StaticResource CalculatorConverter}" ConverterParameter="*">
<Binding Path="CurrentFoodLocationModel.Value.Rect.Width.Value" />
<Binding Path="CurrentFoodLocationModel.Value.Rect.Width" />
<Binding Source="1" />
</MultiBinding>
</Setter.Value>
@ -490,8 +489,8 @@
<Setter.Value>
<MultiBinding Converter="{StaticResource RatioMarginConverter}">
<Binding Source="1" />
<Binding Path="CurrentFoodLocationModel.Value.Rect.X.Value" />
<Binding Path="CurrentFoodLocationModel.Value.Rect.Y.Value" />
<Binding Path="CurrentFoodLocationModel.Value.Rect.X" />
<Binding Path="CurrentFoodLocationModel.Value.Rect.Y" />
</MultiBinding>
</Setter.Value>
</Setter>

View File

@ -1,5 +1,4 @@
using HKW.Models;
using LinePutScript.Localization.WPF;
using LinePutScript.Localization.WPF;
using System;
using System.Collections;
using System.Collections.Generic;

View File

@ -1,5 +1,5 @@
using HKW.HKWUtils.Observable;
using HKW.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

View File

@ -39,7 +39,6 @@
x:Name="ComboBox_Pet"
Grid.Column="1"
pu:ComboBoxHelper.Watermark="{ll:Str 选择宠物}"
DisplayMemberPath="Id.Value"
ItemsSource="{Binding Pets}"
SelectedItem="{Binding CurrentPet.Value}"
Style="{DynamicResource StandardComboBoxStyle}">
@ -48,6 +47,14 @@
<Setter Property="ToolTip" Value="{Binding CurrentI18nData.Value.Name.Value}" />
</Style>
</ComboBox.ItemContainerStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Id.Value}" />
<TextBlock IsEnabled="{Binding FromMain.Value, Converter={StaticResource FalseToCollapsedConverter}}" Text="{ll:Str {} (来自本体)}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
<Grid Grid.Row="1">

View File

@ -72,7 +72,7 @@
<Setter Property="Width">
<Setter.Value>
<MultiBinding Converter="{StaticResource CalculatorConverter}" ConverterParameter="*">
<Binding Path="Pet.Value.TouchHeadRect.Value.Width.Value" />
<Binding Path="Pet.Value.TouchHeadRect.Value.Width" />
<Binding Path="LengthRatio.Value" />
</MultiBinding>
</Setter.Value>
@ -80,7 +80,7 @@
<Setter Property="Height">
<Setter.Value>
<MultiBinding Converter="{StaticResource CalculatorConverter}" ConverterParameter="*">
<Binding Path="Pet.Value.TouchHeadRect.Value.Height.Value" />
<Binding Path="Pet.Value.TouchHeadRect.Value.Height" />
<Binding Path="LengthRatio.Value" />
</MultiBinding>
</Setter.Value>
@ -89,8 +89,8 @@
<Setter.Value>
<MultiBinding Converter="{StaticResource RatioMarginConverter}">
<Binding Path="LengthRatio.Value" />
<Binding Path="Pet.Value.TouchHeadRect.Value.X.Value" />
<Binding Path="Pet.Value.TouchHeadRect.Value.Y.Value" />
<Binding Path="Pet.Value.TouchHeadRect.Value.X" />
<Binding Path="Pet.Value.TouchHeadRect.Value.Y" />
</MultiBinding>
</Setter.Value>
</Setter>
@ -103,7 +103,7 @@
<Setter Property="Width">
<Setter.Value>
<MultiBinding Converter="{StaticResource CalculatorConverter}" ConverterParameter="*">
<Binding Path="Pet.Value.TouchBodyRect.Value.Width.Value" />
<Binding Path="Pet.Value.TouchBodyRect.Value.Width" />
<Binding Path="LengthRatio.Value" />
</MultiBinding>
</Setter.Value>
@ -111,7 +111,7 @@
<Setter Property="Height">
<Setter.Value>
<MultiBinding Converter="{StaticResource CalculatorConverter}" ConverterParameter="*">
<Binding Path="Pet.Value.TouchBodyRect.Value.Height.Value" />
<Binding Path="Pet.Value.TouchBodyRect.Value.Height" />
<Binding Path="LengthRatio.Value" />
</MultiBinding>
</Setter.Value>
@ -120,8 +120,8 @@
<Setter.Value>
<MultiBinding Converter="{StaticResource RatioMarginConverter}">
<Binding Path="LengthRatio.Value" />
<Binding Path="Pet.Value.TouchBodyRect.Value.X.Value" />
<Binding Path="Pet.Value.TouchBodyRect.Value.Y.Value" />
<Binding Path="Pet.Value.TouchBodyRect.Value.X" />
<Binding Path="Pet.Value.TouchBodyRect.Value.Y" />
</MultiBinding>
</Setter.Value>
</Setter>
@ -134,7 +134,7 @@
<Setter Property="Width">
<Setter.Value>
<MultiBinding Converter="{StaticResource CalculatorConverter}" ConverterParameter="*">
<Binding Path="Pet.Value.TouchRaisedRect.Value.Happy.Value.Width.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.Happy.Width" />
<Binding Path="LengthRatio.Value" />
</MultiBinding>
</Setter.Value>
@ -142,7 +142,7 @@
<Setter Property="Height">
<Setter.Value>
<MultiBinding Converter="{StaticResource CalculatorConverter}" ConverterParameter="*">
<Binding Path="Pet.Value.TouchRaisedRect.Value.Happy.Value.Height.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.Happy.Height" />
<Binding Path="LengthRatio.Value" />
</MultiBinding>
</Setter.Value>
@ -151,8 +151,8 @@
<Setter.Value>
<MultiBinding Converter="{StaticResource RatioMarginConverter}">
<Binding Path="LengthRatio.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.Happy.Value.X.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.Happy.Value.Y.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.Happy.X" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.Happy.Y" />
</MultiBinding>
</Setter.Value>
</Setter>
@ -165,7 +165,7 @@
<Setter Property="Width">
<Setter.Value>
<MultiBinding Converter="{StaticResource CalculatorConverter}" ConverterParameter="*">
<Binding Path="Pet.Value.TouchRaisedRect.Value.Nomal.Value.Width.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.Nomal.Width" />
<Binding Path="LengthRatio.Value" />
</MultiBinding>
</Setter.Value>
@ -173,7 +173,7 @@
<Setter Property="Height">
<Setter.Value>
<MultiBinding Converter="{StaticResource CalculatorConverter}" ConverterParameter="*">
<Binding Path="Pet.Value.TouchRaisedRect.Value.Nomal.Value.Height.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.Nomal.Height" />
<Binding Path="LengthRatio.Value" />
</MultiBinding>
</Setter.Value>
@ -182,8 +182,8 @@
<Setter.Value>
<MultiBinding Converter="{StaticResource RatioMarginConverter}">
<Binding Path="LengthRatio.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.Nomal.Value.X.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.Nomal.Value.Y.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.Nomal.X" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.Nomal.Y" />
</MultiBinding>
</Setter.Value>
</Setter>
@ -196,7 +196,7 @@
<Setter Property="Width">
<Setter.Value>
<MultiBinding Converter="{StaticResource CalculatorConverter}" ConverterParameter="*">
<Binding Path="Pet.Value.TouchRaisedRect.Value.PoorCondition.Value.Width.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.PoorCondition.Width" />
<Binding Path="LengthRatio.Value" />
</MultiBinding>
</Setter.Value>
@ -204,7 +204,7 @@
<Setter Property="Height">
<Setter.Value>
<MultiBinding Converter="{StaticResource CalculatorConverter}" ConverterParameter="*">
<Binding Path="Pet.Value.TouchRaisedRect.Value.PoorCondition.Value.Height.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.PoorCondition.Height" />
<Binding Path="LengthRatio.Value" />
</MultiBinding>
</Setter.Value>
@ -213,8 +213,8 @@
<Setter.Value>
<MultiBinding Converter="{StaticResource RatioMarginConverter}">
<Binding Path="LengthRatio.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.PoorCondition.Value.X.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.PoorCondition.Value.Y.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.PoorCondition.X" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.PoorCondition.Y" />
</MultiBinding>
</Setter.Value>
</Setter>
@ -227,7 +227,7 @@
<Setter Property="Width">
<Setter.Value>
<MultiBinding Converter="{StaticResource CalculatorConverter}" ConverterParameter="*">
<Binding Path="Pet.Value.TouchRaisedRect.Value.Ill.Value.Width.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.Ill.Width" />
<Binding Path="LengthRatio.Value" />
</MultiBinding>
</Setter.Value>
@ -235,7 +235,7 @@
<Setter Property="Height">
<Setter.Value>
<MultiBinding Converter="{StaticResource CalculatorConverter}" ConverterParameter="*">
<Binding Path="Pet.Value.TouchRaisedRect.Value.Ill.Value.Height.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.Ill.Height" />
<Binding Path="LengthRatio.Value" />
</MultiBinding>
</Setter.Value>
@ -244,8 +244,8 @@
<Setter.Value>
<MultiBinding Converter="{StaticResource RatioMarginConverter}">
<Binding Path="LengthRatio.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.Ill.Value.X.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.Ill.Value.Y.Value" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.Ill.X" />
<Binding Path="Pet.Value.TouchRaisedRect.Value.Ill.Y" />
</MultiBinding>
</Setter.Value>
</Setter>
@ -264,8 +264,8 @@
<Setter.Value>
<MultiBinding Converter="{StaticResource RatioMarginConverter}">
<Binding Path="LengthRatio.Value" />
<Binding Path="Pet.Value.RaisePoint.Value.Happy.Value.X.Value" />
<Binding Path="Pet.Value.RaisePoint.Value.Happy.Value.Y.Value" />
<Binding Path="Pet.Value.RaisePoint.Value.Happy.X" />
<Binding Path="Pet.Value.RaisePoint.Value.Happy.Y" />
</MultiBinding>
</Setter.Value>
</Setter>
@ -284,8 +284,8 @@
<Setter.Value>
<MultiBinding Converter="{StaticResource RatioMarginConverter}">
<Binding Path="LengthRatio.Value" />
<Binding Path="Pet.Value.RaisePoint.Value.Nomal.Value.X.Value" />
<Binding Path="Pet.Value.RaisePoint.Value.Nomal.Value.Y.Value" />
<Binding Path="Pet.Value.RaisePoint.Value.Nomal.X" />
<Binding Path="Pet.Value.RaisePoint.Value.Nomal.Y" />
</MultiBinding>
</Setter.Value>
</Setter>
@ -304,8 +304,8 @@
<Setter.Value>
<MultiBinding Converter="{StaticResource RatioMarginConverter}">
<Binding Path="LengthRatio.Value" />
<Binding Path="Pet.Value.RaisePoint.Value.PoorCondition.Value.X.Value" />
<Binding Path="Pet.Value.RaisePoint.Value.PoorCondition.Value.Y.Value" />
<Binding Path="Pet.Value.RaisePoint.Value.PoorCondition.X" />
<Binding Path="Pet.Value.RaisePoint.Value.PoorCondition.Y" />
</MultiBinding>
</Setter.Value>
</Setter>
@ -324,8 +324,8 @@
<Setter.Value>
<MultiBinding Converter="{StaticResource RatioMarginConverter}">
<Binding Path="LengthRatio.Value" />
<Binding Path="Pet.Value.RaisePoint.Value.Ill.Value.X.Value" />
<Binding Path="Pet.Value.RaisePoint.Value.Ill.Value.Y.Value" />
<Binding Path="Pet.Value.RaisePoint.Value.Ill.X" />
<Binding Path="Pet.Value.RaisePoint.Value.Ill.Y" />
</MultiBinding>
</Setter.Value>
</Setter>
@ -414,12 +414,12 @@
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Background="{x:Null}" Content="x:" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.TouchHeadRect.Value.X.Value}" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.TouchHeadRect.Value.X}" />
<Label
Grid.Column="2"
Background="{x:Null}"
Content="y:" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.TouchHeadRect.Value.Y.Value, Mode=TwoWay}" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.TouchHeadRect.Value.Y, Mode=TwoWay}" />
<Label
Grid.Row="1"
Background="{x:Null}"
@ -427,7 +427,7 @@
<pu:NumberInput
Grid.Row="1"
Grid.Column="1"
Value="{Binding Pet.Value.TouchHeadRect.Value.Width.Value, Mode=TwoWay}" />
Value="{Binding Pet.Value.TouchHeadRect.Value.Width, Mode=TwoWay}" />
<Label
Grid.Row="1"
Grid.Column="2"
@ -436,7 +436,7 @@
<pu:NumberInput
Grid.Row="1"
Grid.Column="3"
Value="{Binding Pet.Value.TouchHeadRect.Value.Height.Value, Mode=TwoWay}" />
Value="{Binding Pet.Value.TouchHeadRect.Value.Height, Mode=TwoWay}" />
</Grid>
</Grid>
<Grid>
@ -461,12 +461,12 @@
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Background="{x:Null}" Content="x:" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.TouchBodyRect.Value.X.Value}" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.TouchBodyRect.Value.X}" />
<Label
Grid.Column="2"
Background="{x:Null}"
Content="y:" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.TouchBodyRect.Value.Y.Value, Mode=TwoWay}" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.TouchBodyRect.Value.Y, Mode=TwoWay}" />
<Label
Grid.Row="1"
Background="{x:Null}"
@ -474,7 +474,7 @@
<pu:NumberInput
Grid.Row="1"
Grid.Column="1"
Value="{Binding Pet.Value.TouchBodyRect.Value.Width.Value, Mode=TwoWay}" />
Value="{Binding Pet.Value.TouchBodyRect.Value.Width, Mode=TwoWay}" />
<Label
Grid.Row="1"
Grid.Column="2"
@ -483,7 +483,7 @@
<pu:NumberInput
Grid.Row="1"
Grid.Column="3"
Value="{Binding Pet.Value.TouchBodyRect.Value.Height.Value, Mode=TwoWay}" />
Value="{Binding Pet.Value.TouchBodyRect.Value.Height, Mode=TwoWay}" />
</Grid>
</Grid>
<Expander Header="{ll:Str 提起范围}">
@ -516,12 +516,12 @@
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Background="{x:Null}" Content="x:" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.TouchRaisedRect.Value.Happy.Value.X.Value, Mode=TwoWay}" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.TouchRaisedRect.Value.Happy.X, Mode=TwoWay}" />
<Label
Grid.Column="2"
Background="{x:Null}"
Content="y:" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.TouchRaisedRect.Value.Happy.Value.Y.Value, Mode=TwoWay}" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.TouchRaisedRect.Value.Happy.Y, Mode=TwoWay}" />
<Label
Grid.Row="1"
Background="{x:Null}"
@ -529,7 +529,7 @@
<pu:NumberInput
Grid.Row="1"
Grid.Column="1"
Value="{Binding Pet.Value.TouchRaisedRect.Value.Happy.Value.Width.Value, Mode=TwoWay}" />
Value="{Binding Pet.Value.TouchRaisedRect.Value.Happy.Width, Mode=TwoWay}" />
<Label
Grid.Row="1"
Grid.Column="2"
@ -538,7 +538,7 @@
<pu:NumberInput
Grid.Row="1"
Grid.Column="3"
Value="{Binding Pet.Value.TouchRaisedRect.Value.Happy.Value.Height.Value, Mode=TwoWay}" />
Value="{Binding Pet.Value.TouchRaisedRect.Value.Happy.Height, Mode=TwoWay}" />
</Grid>
<ToggleButton
x:Name="ToggleButton_TouchRaisedRect_NomalState"
@ -562,12 +562,12 @@
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Background="{x:Null}" Content="x:" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.TouchRaisedRect.Value.Nomal.Value.X.Value, Mode=TwoWay}" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.TouchRaisedRect.Value.Nomal.X, Mode=TwoWay}" />
<Label
Grid.Column="2"
Background="{x:Null}"
Content="y:" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.TouchRaisedRect.Value.Nomal.Value.Y.Value, Mode=TwoWay}" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.TouchRaisedRect.Value.Nomal.Y, Mode=TwoWay}" />
<Label
Grid.Row="1"
Background="{x:Null}"
@ -575,7 +575,7 @@
<pu:NumberInput
Grid.Row="1"
Grid.Column="1"
Value="{Binding Pet.Value.TouchRaisedRect.Value.Nomal.Value.Width.Value, Mode=TwoWay}" />
Value="{Binding Pet.Value.TouchRaisedRect.Value.Nomal.Width, Mode=TwoWay}" />
<Label
Grid.Row="1"
Grid.Column="2"
@ -584,7 +584,7 @@
<pu:NumberInput
Grid.Row="1"
Grid.Column="3"
Value="{Binding Pet.Value.TouchRaisedRect.Value.Nomal.Value.Height.Value, Mode=TwoWay}" />
Value="{Binding Pet.Value.TouchRaisedRect.Value.Nomal.Height, Mode=TwoWay}" />
</Grid>
<ToggleButton
x:Name="ToggleButton_TouchRaisedRect_PoorConditionState"
@ -608,12 +608,12 @@
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Background="{x:Null}" Content="x:" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.TouchRaisedRect.Value.PoorCondition.Value.X.Value, Mode=TwoWay}" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.TouchRaisedRect.Value.PoorCondition.X, Mode=TwoWay}" />
<Label
Grid.Column="2"
Background="{x:Null}"
Content="y:" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.TouchRaisedRect.Value.PoorCondition.Value.Y.Value, Mode=TwoWay}" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.TouchRaisedRect.Value.PoorCondition.Y, Mode=TwoWay}" />
<Label
Grid.Row="1"
Background="{x:Null}"
@ -621,7 +621,7 @@
<pu:NumberInput
Grid.Row="1"
Grid.Column="1"
Value="{Binding Pet.Value.TouchRaisedRect.Value.PoorCondition.Value.Width.Value, Mode=TwoWay}" />
Value="{Binding Pet.Value.TouchRaisedRect.Value.PoorCondition.Width, Mode=TwoWay}" />
<Label
Grid.Row="1"
Grid.Column="2"
@ -630,7 +630,7 @@
<pu:NumberInput
Grid.Row="1"
Grid.Column="3"
Value="{Binding Pet.Value.TouchRaisedRect.Value.PoorCondition.Value.Height.Value, Mode=TwoWay}" />
Value="{Binding Pet.Value.TouchRaisedRect.Value.PoorCondition.Height, Mode=TwoWay}" />
</Grid>
<ToggleButton
x:Name="ToggleButton_TouchRaisedRect_IllState"
@ -654,12 +654,12 @@
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Background="{x:Null}" Content="x:" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.TouchRaisedRect.Value.Ill.Value.X.Value, Mode=TwoWay}" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.TouchRaisedRect.Value.Ill.X, Mode=TwoWay}" />
<Label
Grid.Column="2"
Background="{x:Null}"
Content="y:" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.TouchRaisedRect.Value.Ill.Value.Y.Value, Mode=TwoWay}" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.TouchRaisedRect.Value.Ill.Y, Mode=TwoWay}" />
<Label
Grid.Row="1"
Background="{x:Null}"
@ -667,7 +667,7 @@
<pu:NumberInput
Grid.Row="1"
Grid.Column="1"
Value="{Binding Pet.Value.TouchRaisedRect.Value.Ill.Value.Width.Value, Mode=TwoWay}" />
Value="{Binding Pet.Value.TouchRaisedRect.Value.Ill.Width, Mode=TwoWay}" />
<Label
Grid.Row="1"
Grid.Column="2"
@ -676,7 +676,7 @@
<pu:NumberInput
Grid.Row="1"
Grid.Column="3"
Value="{Binding Pet.Value.TouchRaisedRect.Value.Ill.Value.Height.Value, Mode=TwoWay}" />
Value="{Binding Pet.Value.TouchRaisedRect.Value.Ill.Height, Mode=TwoWay}" />
</Grid>
</Grid>
</Expander>
@ -709,12 +709,12 @@
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Background="{x:Null}" Content="x:" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.RaisePoint.Value.Happy.Value.X.Value, Mode=TwoWay}" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.RaisePoint.Value.Happy.X, Mode=TwoWay}" />
<Label
Grid.Column="2"
Background="{x:Null}"
Content="y:" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.RaisePoint.Value.Happy.Value.Y.Value, Mode=TwoWay}" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.RaisePoint.Value.Happy.Y, Mode=TwoWay}" />
</Grid>
<ToggleButton
x:Name="ToggleButton_RaisePoint_Nomal"
@ -737,12 +737,12 @@
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Background="{x:Null}" Content="x:" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.RaisePoint.Value.Nomal.Value.X.Value, Mode=TwoWay}" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.RaisePoint.Value.Nomal.X, Mode=TwoWay}" />
<Label
Grid.Column="2"
Background="{x:Null}"
Content="y:" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.RaisePoint.Value.Nomal.Value.Y.Value, Mode=TwoWay}" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.RaisePoint.Value.Nomal.Y, Mode=TwoWay}" />
</Grid>
<ToggleButton
x:Name="ToggleButton_RaisePoint_PoorCondition"
@ -765,12 +765,12 @@
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Background="{x:Null}" Content="x:" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.RaisePoint.Value.PoorCondition.Value.X.Value, Mode=TwoWay}" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.RaisePoint.Value.PoorCondition.X, Mode=TwoWay}" />
<Label
Grid.Column="2"
Background="{x:Null}"
Content="y:" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.RaisePoint.Value.PoorCondition.Value.Y.Value, Mode=TwoWay}" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.RaisePoint.Value.PoorCondition.Y, Mode=TwoWay}" />
</Grid>
<ToggleButton
x:Name="ToggleButton_RaisePoint_Ill"
@ -793,12 +793,12 @@
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Background="{x:Null}" Content="x:" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.RaisePoint.Value.Ill.Value.X.Value, Mode=TwoWay}" />
<pu:NumberInput Grid.Column="1" Value="{Binding Pet.Value.RaisePoint.Value.Ill.X, Mode=TwoWay}" />
<Label
Grid.Column="2"
Background="{x:Null}"
Content="y:" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.RaisePoint.Value.Ill.Value.Y.Value, Mode=TwoWay}" />
<pu:NumberInput Grid.Column="3" Value="{Binding Pet.Value.RaisePoint.Value.Ill.Y, Mode=TwoWay}" />
</Grid>
</Grid>
</Expander>

View File

@ -43,7 +43,6 @@
Grid.Column="1"
MinWidth="150"
pu:ComboBoxHelper.Watermark="{ll:Str 选择宠物}"
DisplayMemberPath="Id.Value"
ItemsSource="{Binding Pets}"
SelectedItem="{Binding CurrentPet.Value}"
Style="{DynamicResource StandardComboBoxStyle}">
@ -52,6 +51,14 @@
<Setter Property="ToolTip" Value="{Binding CurrentI18nData.Value.Name.Value}" />
</Style>
</ComboBox.ItemContainerStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Id.Value}" />
<TextBlock IsEnabled="{Binding FromMain.Value, Converter={StaticResource FalseToCollapsedConverter}}" Text="{ll:Str {} (来自本体)}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
<Grid Grid.Row="1">