This commit is contained in:
Hakoyu 2023-10-15 23:11:04 +08:00
parent 171c49012c
commit c58ddf25fa
11 changed files with 529 additions and 165 deletions

View File

@ -23,19 +23,19 @@ public class FoodAnimeModel
/// <summary>
/// 后图像列表
/// </summary>
public ObservableCollection<ImageModel> BackImages { get; } = new();
public ObservableValue<FoodImagesPath> BackImagesPath { get; } = new();
public ObservableCollection<ImageModel> BackImages { get; set; } = new();
public ObservableValue<FoodImagesPath?> BackImagesPath { get; } = new();
/// <summary>
/// 前图像列表
/// </summary>
public ObservableCollection<ImageModel> FrontImages { get; } = new();
public ObservableValue<FoodImagesPath> FrontImagesPath { get; } = new();
public ObservableCollection<ImageModel> FrontImages { get; set; } = new();
public ObservableValue<FoodImagesPath?> FrontImagesPath { get; } = new();
/// <summary>
/// 食物定位列表
/// </summary>
public ObservableCollection<FoodLocationInfoModel> FoodLocations { get; } = new();
public ObservableCollection<FoodLocationModel> FoodLocations { get; } = new();
public FoodAnimeModel() { }
@ -46,7 +46,7 @@ public class FoodAnimeModel
{
//var index = int.Parse(item.Name.Substring(1));
var infos = item.Info.Split(',');
var foodLocationInfo = new FoodLocationInfoModel();
var foodLocationInfo = new FoodLocationModel();
foodLocationInfo.Duration.Value = int.Parse(infos[0]);
if (infos.Length > 1)
{

View File

@ -4,6 +4,7 @@ using LinePutScript.Localization.WPF;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Text;
@ -24,6 +25,11 @@ public class FoodAnimeTypeModel
/// </summary>
public ObservableValue<string> Name { get; } = new();
/// <summary>
/// 动作类型
/// </summary>
public GraphInfo.GraphType GraphType => GraphInfo.GraphType.Common;
/// <summary>
/// 开心动画
/// </summary>
@ -46,28 +52,73 @@ public class FoodAnimeTypeModel
public FoodAnimeTypeModel()
{
HappyAnimes.CollectionChanged += Animes_CollectionChanged;
//Name.ValueChanged += (_, _) =>
//{
// Id.Value = $"{GraphType.Value}_{Name.Value}";
//};
}
private void Animes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems is not null)
{
foreach (var model in e.NewItems.Cast<FoodAnimeModel>())
{
SetImagesPathValueChanged(model);
}
}
if (e.OldItems is not null)
{
foreach (var model in e.OldItems.Cast<FoodAnimeModel>())
{
SetImagesPathValueChanged(model);
}
}
}
private void SetImagesPathValueChanged(FoodAnimeModel model)
{
model.FrontImagesPath.ValueChanged += (o, n) =>
{
if (n is null)
return;
if (n.Mode.Value is GameSave.ModeType.Happy)
model.FrontImages = HappyAnimes[n.Index.Value].FrontImages;
else if (n.Mode.Value is GameSave.ModeType.Nomal)
model.FrontImages = NomalAnimes[n.Index.Value].FrontImages;
else if (n.Mode.Value is GameSave.ModeType.PoorCondition)
model.FrontImages = PoorConditionAnimes[n.Index.Value].FrontImages;
else if (n.Mode.Value is GameSave.ModeType.Ill)
model.FrontImages = IllAnimes[n.Index.Value].FrontImages;
};
model.BackImagesPath.ValueChanged += (o, n) =>
{
if (n is null)
return;
if (n.Mode.Value is GameSave.ModeType.Happy)
model.BackImages = HappyAnimes[n.Index.Value].BackImages;
else if (n.Mode.Value is GameSave.ModeType.Nomal)
model.BackImages = NomalAnimes[n.Index.Value].BackImages;
else if (n.Mode.Value is GameSave.ModeType.PoorCondition)
model.BackImages = PoorConditionAnimes[n.Index.Value].BackImages;
else if (n.Mode.Value is GameSave.ModeType.Ill)
model.BackImages = IllAnimes[n.Index.Value].BackImages;
};
}
public FoodAnimeTypeModel(string path)
: this()
{
Name.Value = Path.GetFileName(path);
var infoFile = Path.Combine(path, ModMakerInfo.InfoFile);
if (File.Exists(infoFile) is false)
if (
Directory.EnumerateFiles(path, ModMakerInfo.InfoFile, SearchOption.AllDirectories).Any()
is false
)
throw new Exception("信息文件\n{0}\n不存在".Translate(infoFile));
var lps = new LPS(infoFile);
var foodAnime = lps.FindAllLineInfo(nameof(FoodAnimation));
if (foodAnime.Any() is false)
throw new Exception("信息文件\n{0}\n未包含食物动画信息".Translate(infoFile));
var anime = lps.FindAllLineInfo(nameof(PNGAnimation));
foreach (var foodAnimation in foodAnime)
{
ParseFoodAnimeInfo(foodAnimation);
}
if (File.Exists(infoFile))
ParseInfoFile(path, infoFile);
}
public FoodAnimeTypeModel(FoodAnimeTypeModel model)
@ -104,21 +155,115 @@ public class FoodAnimeTypeModel
}
}
public void ParseFoodAnimeInfo(ILine line)
public void ParseInfoFile(string path, string infoPath)
{
var mode = (GameSave.ModeType)Enum.Parse(typeof(GameSave.ModeType), line.Find("mode").Info);
if (mode is GameSave.ModeType.Happy)
AddModeAnime(HappyAnimes, line);
else if (mode is GameSave.ModeType.Nomal)
AddModeAnime(NomalAnimes, line);
else if (mode is GameSave.ModeType.PoorCondition)
AddModeAnime(PoorConditionAnimes, line);
else if (mode is GameSave.ModeType.Ill)
AddModeAnime(IllAnimes, line);
var lps = new LPS(infoPath);
var foodAnimeInfos = lps.FindAllLineInfo(nameof(FoodAnimation));
if (foodAnimeInfos.Any() is false)
throw new Exception("信息文件\n{0}\n未包含食物动画信息".Translate(infoPath));
var pngAnimeInfos = lps.FindAllLineInfo(nameof(PNGAnimation))
.Select(
i =>
new PNGAnimeInfo(
i.Info,
i.Find("infoPath").Info,
(GameSave.ModeType)
Enum.Parse(typeof(GameSave.ModeType), i.Find("mode").Info, true)
)
)
.ToList();
foreach (var foodAnimation in foodAnimeInfos)
{
ParseFoodAnimeInfo(path, foodAnimation, pngAnimeInfos);
}
}
public void AddModeAnime(ObservableCollection<FoodAnimeModel> foodAnimes, ILine line)
public void ParseFoodAnimeInfo(string path, ILine line, List<PNGAnimeInfo> pngAnimeInfos)
{
foodAnimes.Add(new(line));
var mode = (GameSave.ModeType)
Enum.Parse(typeof(GameSave.ModeType), line.Find("mode").Info, true);
if (mode is GameSave.ModeType.Happy)
AddModeAnime(path, GameSave.ModeType.Happy, HappyAnimes, line, pngAnimeInfos);
else if (mode is GameSave.ModeType.Nomal)
AddModeAnime(path, GameSave.ModeType.Nomal, NomalAnimes, line, pngAnimeInfos);
else if (mode is GameSave.ModeType.PoorCondition)
AddModeAnime(
path,
GameSave.ModeType.PoorCondition,
PoorConditionAnimes,
line,
pngAnimeInfos
);
else if (mode is GameSave.ModeType.Ill)
AddModeAnime(path, GameSave.ModeType.Ill, IllAnimes, line, pngAnimeInfos);
}
public void AddModeAnime(
string path,
GameSave.ModeType mode,
ObservableCollection<FoodAnimeModel> foodAnimes,
ILine line,
List<PNGAnimeInfo> pngAnimeInfos
)
{
var anime = new FoodAnimeModel(line);
var frontLay = line.Find("front_lay").Info;
var backLay = line.Find("back_lay").Info;
var frontLayAnimes = pngAnimeInfos.Where(i => i.Name == frontLay).ToList();
var backLayAnimes = pngAnimeInfos.Where(i => i.Name == backLay).ToList();
// 尝试获取相同模式的动画
if (frontLayAnimes.FirstOrDefault(i => i.Mode == mode) is PNGAnimeInfo frontAnimeInfo)
{
anime.FrontImages = GetImages(path, frontAnimeInfo);
}
else
{
// 若没有则获取通用动画
anime.FrontImages = GetImages(
path,
frontLayAnimes.First(i => i.Mode == GameSave.ModeType.Nomal)
);
}
if (backLayAnimes.FirstOrDefault(i => i.Mode == mode) is PNGAnimeInfo backAnimeInfo)
{
anime.BackImages = GetImages(path, backAnimeInfo);
}
else
{
anime.BackImages = GetImages(
path,
backLayAnimes.First(i => i.Mode == GameSave.ModeType.Nomal)
);
}
foodAnimes.Add(anime);
static ObservableCollection<ImageModel> GetImages(string path, PNGAnimeInfo pngAnimeInfo)
{
return new(
Directory
.EnumerateFiles(Path.Combine(path, pngAnimeInfo.Path))
.Select(
i =>
new ImageModel(
Utils.LoadImageToMemoryStream(i),
int.Parse(Path.GetFileNameWithoutExtension(i).Split('_')[1])
)
)
);
}
}
}
public class PNGAnimeInfo
{
public string Name { get; }
public string Path { get; }
public GameSave.ModeType Mode { get; }
public PNGAnimeInfo(string name, string path, GameSave.ModeType mode)
{
Name = name;
Path = path;
Mode = mode;
}
}

View File

@ -10,7 +10,7 @@ namespace VPet.ModMaker.Models.ModModel;
/// <summary>
/// 食物图像模型
/// </summary>
public class FoodLocationInfoModel
public class FoodLocationModel
{
/// <summary>
/// 持续时间
@ -32,7 +32,7 @@ public class FoodLocationInfoModel
/// </summary>
public ObservableValue<double> Opacity { get; } = new(100);
public FoodLocationInfoModel()
public FoodLocationModel()
{
Rect.Width.ValueChanged += (o, n) =>
{
@ -40,9 +40,9 @@ public class FoodLocationInfoModel
};
}
public FoodLocationInfoModel Copy()
public FoodLocationModel Copy()
{
var model = new FoodLocationInfoModel();
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.Rotate.Value = Rotate.Value;

View File

@ -70,6 +70,11 @@ public class PetModel : I18nModel<I18nPetInfoModel>
/// </summary>
public ObservableCollection<AnimeTypeModel> Animes { get; } = new();
/// <summary>
///食物动画
/// </summary>
public ObservableCollection<FoodAnimeTypeModel> FoodAnimes { get; } = new();
public bool IsSimplePetModel { get; } = false;
public PetModel()

View File

@ -34,6 +34,11 @@ public class ObservableValue<T> : INotifyPropertyChanging, INotifyPropertyChange
}
}
/// <summary>
/// 含有值
/// </summary>
public bool HasValue => Value != null;
#region Ctor
/// <inheritdoc/>
public ObservableValue() { }

View File

@ -106,7 +106,7 @@
<Compile Include="Models\Expansions.cs" />
<Compile Include="Models\ModModel\FoodAnimeModel.cs" />
<Compile Include="Models\ModModel\FoodAnimeTypeModel.cs" />
<Compile Include="Models\ModModel\FoodLocationInfoModel.cs" />
<Compile Include="Models\ModModel\FoodLocationModel.cs" />
<Compile Include="Models\ModModel\FoodModel.cs" />
<Compile Include="Models\I18nHelper.cs" />
<Compile Include="Models\I18nModel.cs" />

View File

@ -286,7 +286,6 @@ public class AnimeEditWindowVM
return;
}
} while (Loop.Value);
Reset();
}
/// <summary>

View File

@ -7,8 +7,10 @@ using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;
using VPet.ModMaker.Models;
using VPet.ModMaker.Models.ModModel;
using VPet_Simulator.Core;
@ -22,25 +24,45 @@ public class FoodAnimeEditWindowVM
/// </summary>
public PetModel CurrentPet { get; set; }
/// <summary>
/// 食物图片
/// </summary>
public ObservableValue<BitmapImage> FoodImage { get; } = new();
/// <summary>
/// 比例
/// </summary>
public ObservableValue<double> LengthRatio { get; } = new(250.0 / 500.0);
/// <summary>
/// 旧动画
/// </summary>
public AnimeTypeModel OldAnime { get; set; }
public FoodAnimeTypeModel OldAnime { get; set; }
/// <summary>
/// 动画
/// </summary>
public ObservableValue<AnimeTypeModel> Anime { get; } = new(new());
public ObservableValue<FoodAnimeTypeModel> Anime { get; } = new(new());
/// <summary>
/// 当前图像模型
/// 当前顶层图像模型
/// </summary>
public ObservableValue<ImageModel> CurrentImageModel { get; } = new();
public ObservableValue<ImageModel> CurrentFrontImageModel { get; } = new();
/// <summary>
/// 当前底层图像模型
/// </summary>
public ObservableValue<ImageModel> CurrentBackImageModel { get; } = new();
/// <summary>
/// 当前食物定位模型
/// </summary>
public ObservableValue<FoodLocationModel> CurrentFoodLocationModel { get; } = new();
/// <summary>
/// 当前动画模型
/// </summary>
public ObservableValue<AnimeModel> CurrentAnimeModel { get; } = new();
public ObservableValue<FoodAnimeModel> CurrentAnimeModel { get; } = new();
/// <summary>
/// 当前模式
@ -100,13 +122,25 @@ public class FoodAnimeEditWindowVM
private bool _playing = false;
/// <summary>
/// 动画任务
/// 顶层动画任务
/// </summary>
private Task _playerTask;
private Task _frontPlayerTask;
/// <summary>
/// 底层动画任务
/// </summary>
private Task _backPlayerTask;
/// <summary>
/// 食物动画任务
/// </summary>
private Task _foodPlayerTask;
public FoodAnimeEditWindowVM()
{
_playerTask = new(Play);
_frontPlayerTask = new(FrontPlay);
_backPlayerTask = new(BackPlay);
_foodPlayerTask = new(FoodPlay);
CurrentAnimeModel.ValueChanged += CurrentAnimeModel_ValueChanged;
@ -121,18 +155,18 @@ public class FoodAnimeEditWindowVM
}
#region LoadAnime
private void Anime_ValueChanged(AnimeTypeModel oldValue, AnimeTypeModel newValue)
private void Anime_ValueChanged(FoodAnimeTypeModel oldValue, FoodAnimeTypeModel newValue)
{
CheckGraphType(newValue);
}
private void CheckGraphType(AnimeTypeModel model)
private void CheckGraphType(FoodAnimeTypeModel model)
{
if (AnimeTypeModel.HasMultiTypeAnimes.Contains(model.GraphType.Value))
HasMultiType.Value = true;
//if (FoodAnimeTypeModel.HasMultiTypeAnimes.Contains(model.GraphType.Value))
// HasMultiType.Value = true;
if (AnimeTypeModel.HasNameAnimes.Contains(model.GraphType.Value))
HasAnimeName.Value = true;
//if (FoodAnimeTypeModel.HasNameAnimes.Contains(model.GraphType.Value))
// HasAnimeName.Value = true;
}
#endregion
/// <summary>
@ -146,14 +180,14 @@ public class FoodAnimeEditWindowVM
MessageBox.Show("确定删除吗".Translate(), "", MessageBoxButton.YesNo) is MessageBoxResult.Yes
)
{
if (CurrentMode is GameSave.ModeType.Happy)
Anime.Value.HappyAnimes.Remove(value);
else if (CurrentMode is GameSave.ModeType.Nomal)
Anime.Value.NomalAnimes.Remove(value);
else if (CurrentMode is GameSave.ModeType.PoorCondition)
Anime.Value.PoorConditionAnimes.Remove(value);
else if (CurrentMode is GameSave.ModeType.Ill)
Anime.Value.IllAnimes.Remove(value);
//if (CurrentMode is GameSave.ModeType.Happy)
// Anime.Value.HappyAnimes.Remove(value);
//else if (CurrentMode is GameSave.ModeType.Nomal)
// Anime.Value.NomalAnimes.Remove(value);
//else if (CurrentMode is GameSave.ModeType.PoorCondition)
// Anime.Value.PoorConditionAnimes.Remove(value);
//else if (CurrentMode is GameSave.ModeType.Ill)
// Anime.Value.IllAnimes.Remove(value);
value.Close();
}
}
@ -223,18 +257,26 @@ public class FoodAnimeEditWindowVM
/// <param name="value">动画模型</param>
private void RemoveImageCommand_ExecuteEvent(AnimeModel value)
{
CurrentImageModel.Value.Close();
value.Images.Remove(CurrentImageModel.Value);
CurrentFrontImageModel.Value.Close();
value.Images.Remove(CurrentFrontImageModel.Value);
}
#region Player
private void CurrentAnimeModel_ValueChanged(AnimeModel oldValue, AnimeModel newValue)
#region FrontPlayer
private void CurrentAnimeModel_ValueChanged(FoodAnimeModel oldValue, FoodAnimeModel newValue)
{
StopCommand_ExecuteEvent();
if (oldValue is not null)
oldValue.Images.CollectionChanged -= Images_CollectionChanged;
{
oldValue.FrontImages.CollectionChanged -= Images_CollectionChanged;
oldValue.BackImages.CollectionChanged -= Images_CollectionChanged;
oldValue.FoodLocations.CollectionChanged -= Images_CollectionChanged;
}
if (newValue is not null)
newValue.Images.CollectionChanged += Images_CollectionChanged;
{
newValue.FrontImages.CollectionChanged += Images_CollectionChanged;
newValue.BackImages.CollectionChanged += Images_CollectionChanged;
newValue.FoodLocations.CollectionChanged += Images_CollectionChanged;
}
}
private void Images_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
@ -268,25 +310,60 @@ public class FoodAnimeEditWindowVM
return;
}
_playing = true;
_playerTask.Start();
_frontPlayerTask.Start();
_backPlayerTask.Start();
_foodPlayerTask.Start();
}
/// <summary>
/// 播放
/// 顶层播放
/// </summary>
private void Play()
private void FrontPlay()
{
do
{
foreach (var model in CurrentAnimeModel.Value.Images)
foreach (var model in CurrentAnimeModel.Value.FrontImages)
{
CurrentImageModel.Value = model;
CurrentFrontImageModel.Value = model;
Task.Delay(model.Duration.Value).Wait();
if (_playing is false)
return;
}
} while (Loop.Value);
}
/// <summary>
/// 底层
/// </summary>
private void BackPlay()
{
do
{
foreach (var model in CurrentAnimeModel.Value.BackImages)
{
CurrentBackImageModel.Value = model;
Task.Delay(model.Duration.Value).Wait();
if (_playing is false)
return;
}
} while (Loop.Value);
}
/// <summary>
/// 食物
/// </summary>
private void FoodPlay()
{
do
{
foreach (var model in CurrentAnimeModel.Value.FoodLocations)
{
CurrentFoodLocationModel.Value = model;
Task.Delay(model.Duration.Value).Wait();
if (_playing is false)
return;
}
} while (Loop.Value);
Reset();
}
/// <summary>
@ -295,7 +372,9 @@ public class FoodAnimeEditWindowVM
private void Reset()
{
_playing = false;
_playerTask = new(Play);
_frontPlayerTask = new(FrontPlay);
_backPlayerTask = new(BackPlay);
_foodPlayerTask = new(FoodPlay);
}
#endregion
}

View File

@ -46,18 +46,21 @@
<Expander.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" MinWidth="200" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox pu:TextBoxHelper.Watermark="{ll:Str 动画Id}" Text="{Binding Id.Value, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Grid.Column="1" Margin="10,0,0,0">
<TextBlock Margin="10,0,10,0">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource StringFormatConverter}" ConverterParameter="{}({0})">
<Binding Path="Images.Count" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBox
Grid.Column="1"
pu:TextBoxHelper.Watermark="{ll:Str 动画Id}"
Text="{Binding Id.Value, UpdateSourceTrigger=PropertyChanged}" />
<ComboBox
Grid.Column="2"
Margin="10,0,0,0"

View File

@ -32,15 +32,15 @@
<MenuItem
Command="{Binding PlacementTarget.Tag.AddImageCommand, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
Header="添加图片" />
Header="{ll:Str 添加图片}" />
<MenuItem
Command="{Binding PlacementTarget.Tag.ClearImageCommand, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
Header="清空图片" />
Header="{ll:Str 清空图片}" />
<MenuItem
Command="{Binding PlacementTarget.Tag.RemoveAnimeCommand, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
Header="删除此项" />
Header="{ll:Str 删除此项}" />
</ContextMenu>
</Expander.ContextMenu>
<Expander.Header>
@ -50,14 +50,17 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox pu:TextBoxHelper.Watermark="动画Id" Text="{Binding Id.Value, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Grid.Column="1" Margin="10,0,0,0">
<TextBlock Margin="10,0,10,0">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource StringFormatConverter}" ConverterParameter="{}({0})">
<Binding Path="Images.Count" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBox
Grid.Column="1"
pu:TextBoxHelper.Watermark="{ll:Str 动画Id}"
Text="{Binding Id.Value, UpdateSourceTrigger=PropertyChanged}" />
<ComboBox
Grid.Column="2"
Margin="10,0,0,0"
@ -66,60 +69,139 @@
Visibility="{Binding DataContext.HasMultiType.Value, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}, Converter={StaticResource FalseToHiddenConverter}}" />
</Grid>
</Expander.Header>
<ListBox
d:ItemsSource="{d:SampleData ItemCount=5}"
AllowDrop="True"
Drop="ListBox_Drop"
ItemsSource="{Binding Images, IsAsync=True}"
PreviewMouseMove="ListBox_PreviewMouseMove"
PreviewMouseWheel="ListBox_PreviewMouseWheel"
SelectedItem="{Binding DataContext.CurrentImageModel.Value, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}"
SelectionChanged="ListBox_SelectionChanged">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=Expander, Mode=FindAncestor}}" Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem
Command="{Binding PlacementTarget.Tag.RemoveImageCommand, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
Header="删除图片" />
</ContextMenu>
</Grid.ContextMenu>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Image
Width="150"
Height="150"
d:DataContext=""
DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}}"
Source="{Binding Image.Value, IsAsync=True}">
<Image.ToolTip>
<Image
Width="250"
Height="250"
Source="{Binding Image.Value, IsAsync=True}" />
</Image.ToolTip>
</Image>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Content="持续时间(ms)" />
<pu:NumberInput Grid.Column="1" Value="{Binding DataContext.Duration.Value, RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}}" />
</Grid>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" MinWidth="200" />
</Grid.ColumnDefinitions>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<GroupBox>
<GroupBox.Header>
<Label Content="{ll:Str 顶层图片}" />
</GroupBox.Header>
<ListBox
d:ItemsSource="{d:SampleData ItemCount=5}"
AllowDrop="True"
Drop="ListBox_Drop"
ItemsSource="{Binding Images, IsAsync=True}"
PreviewMouseMove="ListBox_PreviewMouseMove"
PreviewMouseWheel="ListBox_PreviewMouseWheel"
SelectedItem="{Binding DataContext.CurrentImageModel.Value, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}"
SelectionChanged="ListBox_SelectionChanged">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=Expander, Mode=FindAncestor}}" Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem
Command="{Binding PlacementTarget.Tag.RemoveImageCommand, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
Header="{ll:Str 删除图片}" />
</ContextMenu>
</Grid.ContextMenu>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Image
Width="150"
Height="150"
d:DataContext=""
DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}}"
Source="{Binding Image.Value, IsAsync=True}">
<Image.ToolTip>
<Image
Width="250"
Height="250"
Source="{Binding Image.Value, IsAsync=True}" />
</Image.ToolTip>
</Image>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Content="{ll:Str 持续时间(ms)}" />
<pu:NumberInput Grid.Column="1" Value="{Binding DataContext.Duration.Value, RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}}" />
</Grid>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</GroupBox>
<GroupBox Grid.Row="1">
<GroupBox.Header>
<Label Content="{ll:Str 底层图片}" />
</GroupBox.Header>
<ListBox
d:ItemsSource="{d:SampleData ItemCount=5}"
AllowDrop="True"
Drop="ListBox_Drop"
ItemsSource="{Binding Images, IsAsync=True}"
PreviewMouseMove="ListBox_PreviewMouseMove"
PreviewMouseWheel="ListBox_PreviewMouseWheel"
SelectedItem="{Binding DataContext.CurrentImageModel.Value, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}"
SelectionChanged="ListBox_SelectionChanged">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=Expander, Mode=FindAncestor}}" Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem
Command="{Binding PlacementTarget.Tag.RemoveImageCommand, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
Header="{ll:Str 删除图片}" />
</ContextMenu>
</Grid.ContextMenu>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Image
Width="150"
Height="150"
d:DataContext=""
DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}}"
Source="{Binding Image.Value, IsAsync=True}">
<Image.ToolTip>
<Image
Width="250"
Height="250"
Source="{Binding Image.Value, IsAsync=True}" />
</Image.ToolTip>
</Image>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Content="{ll:Str 持续时间(ms)}" />
<pu:NumberInput Grid.Column="1" Value="{Binding DataContext.Duration.Value, RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}}" />
</Grid>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</GroupBox>
</Grid>
<GroupBox Grid.Column="1">
<ListBox />
</GroupBox>
</Grid>
</Expander>
</DataTemplate>
</Window.Resources>
@ -134,38 +216,85 @@
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Image
x:Name="Image_Back"
Width="250"
Height="250"
Source="{Binding CurrentImageModel.Value.Image.Value}" />
<Image
x:Name="Image_Center"
Width="250"
Height="250"
Source="{Binding CurrentImageModel.Value.Image.Value}" />
<Image
x:Name="Image_Front"
Width="250"
Height="250"
Source="{Binding CurrentImageModel.Value.Image.Value}">
<Image.ToolTip>
<Grid>
<Image
Width="500"
Height="500"
Source="{Binding Source, ElementName=Image_Back}" />
<Image
Width="500"
Height="500"
Source="{Binding Source, ElementName=Image_Center}" />
<Image
Width="500"
Height="500"
Source="{Binding Source, ElementName=Image_Front}" />
</Grid>
</Image.ToolTip>
</Image>
<Grid>
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Header="{ll:Str 替换测试食物图片}" />
<MenuItem Header="{ll:Str 重置测试食物图片}" />
</ContextMenu>
</Grid.ContextMenu>
<Image
x:Name="Image_Back"
Width="250"
Height="250"
Source="{Binding CurrentBackImageModel.Value.Image.Value}" />
<Image
x:Name="Image_Center"
Width="250"
Height="250"
Opacity="{Binding CurrentFoodLocationModel.Value.Opacity.Value}"
RenderTransformOrigin="0,0">
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform />
<SkewTransform />
<RotateTransform Angle="{Binding CurrentFoodLocationModel.Value.Rotate.Value}" />
<TranslateTransform />
</TransformGroup>
</Image.RenderTransform>
<Image.Style>
<Style TargetType="Image">
<Setter Property="Width">
<Setter.Value>
<MultiBinding Converter="{StaticResource CalculatorConverter}" ConverterParameter="*">
<Binding Path="CurrentFoodLocationModel.Value.Rect.Value.Width.Value" />
<Binding Path="LengthRatio.Value" />
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="Height">
<Setter.Value>
<MultiBinding Converter="{StaticResource CalculatorConverter}" ConverterParameter="*">
<Binding Path="CurrentFoodLocationModel.Value.Rect.Value.Width.Value" />
<Binding Path="LengthRatio.Value" />
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="Margin">
<Setter.Value>
<MultiBinding Converter="{StaticResource RatioMarginConverter}">
<Binding Path="LengthRatio.Value" />
<Binding Path="CurrentFoodLocationModel.Value.Rect.Value.X.Value" />
<Binding Path="CurrentFoodLocationModel.Value.Rect.Value.Y.Value" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</Image.Style>
</Image>
<Image
x:Name="Image_Front"
Width="250"
Height="250"
Source="{Binding CurrentFrontImageModel.Value.Image.Value}">
<Image.ToolTip>
<Grid>
<Image
Width="500"
Height="500"
Source="{Binding Source, ElementName=Image_Back}" />
<Image
Width="500"
Height="500"
Source="{Binding Source, ElementName=Image_Center}" />
<Image
Width="500"
Height="500"
Source="{Binding Source, ElementName=Image_Front}" />
</Grid>
</Image.ToolTip>
</Image>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition />
@ -194,7 +323,7 @@
<!--<Label Content="{ll:Str 动画Id}" />
<TextBox Grid.Column="1" />-->
<Label Content="{ll:Str 动画类型}" />
<TextBlock Grid.Column="1" Text="{Binding Anime.Value.GraphType.Value}" />
<TextBlock Grid.Column="1" Text="{Binding Anime.Value.GraphType}" />
<Label
Grid.Row="1"
Content="{ll:Str 动画名称}"

View File

@ -38,7 +38,6 @@
Text="{Binding LowText.Value.Id.Value, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
<TextBox
x:Name="TextBox_Text"
Grid.Row="1"
d:Text="这是一个测试文本,这是一个测试文本,这是一个测试文本,这是一个测试文本,这是一个测试文本,这是一个测试文本,这是一个测试文本,"
pu:TextBoxHelper.Watermark="{ll:Str 文本}"