Продолжение перехода на WPF
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System.ComponentModel;
|
||||
using Pf2eDataScrapper.Helpers;
|
||||
using Pf2eDataScrapper.WPF.ViewModels.EntityViewStructure;
|
||||
|
||||
namespace Pf2eDataScrapper.Forms.Controls.EntityTreeViewer;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using FontAwesome.Sharp;
|
||||
using Pf2eDataScrapper.WPF.ViewModels.EntityViewStructure;
|
||||
using Font = System.Drawing.Font;
|
||||
using FontStyle = System.Drawing.FontStyle;
|
||||
using Image = System.Drawing.Image;
|
||||
|
||||
@@ -1,627 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using LinqToDB.Extensions;
|
||||
using Pf2eModel.Model.Common.I18n;
|
||||
|
||||
namespace Pf2eDataScrapper.Forms.Controls.EntityTreeViewer;
|
||||
|
||||
/// <summary>
|
||||
/// Класс для построения структуры данных класса.
|
||||
/// </summary>
|
||||
internal class StructureBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Список обработанных объектов.
|
||||
/// </summary>
|
||||
private readonly List<StructureProcessedObject> _processedObjects = [];
|
||||
|
||||
/// <summary>
|
||||
/// Создание поля структуры.
|
||||
/// </summary>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
/// <param name="depth">Глубина, на котором поле отображается.</param>
|
||||
/// <returns>Поле структуры.</returns>
|
||||
public StructureFieldComplex Create(string name, object value, int depth = 0)
|
||||
{
|
||||
_ = RegisterProcessed(value);
|
||||
|
||||
StructureFieldComplex field = new(this, value, name, depth);
|
||||
|
||||
CompleteProcessed(value, field);
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Регистрация обработанного объекта.
|
||||
/// </summary>
|
||||
/// <param name="value">Регистрируемый объект.</param>
|
||||
/// <returns>`true`, если объект был добавлен; `false`, если объект был добавлен ранее.</returns>
|
||||
public bool RegisterProcessed(object value)
|
||||
{
|
||||
if (_processedObjects.Any(p => p.Value == value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
StructureProcessedObject processed = new(value);
|
||||
|
||||
_processedObjects.Add(processed);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Дополнить обработанный объект.
|
||||
/// </summary>
|
||||
/// <param name="value">Значение.</param>
|
||||
/// <param name="field">Поле объекта.</param>
|
||||
public void CompleteProcessed(object value, StructureFieldComplex field)
|
||||
{
|
||||
StructureProcessedObject? q = _processedObjects.FirstOrDefault(p => p.Value == value);
|
||||
|
||||
if (q == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
q.Field = field;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Выбор поля структуры по значению.
|
||||
/// </summary>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
/// <param name="depth">Глубина, на котором поле отображается.</param>
|
||||
/// <returns></returns>
|
||||
public StructureField Resolve(string name, object? value, int depth)
|
||||
{
|
||||
switch (GetFieldType(value))
|
||||
{
|
||||
case StructureFieldType.Null:
|
||||
return new StructureFieldNull(this, name, depth);
|
||||
|
||||
case StructureFieldType.Primitive:
|
||||
return new StructureFieldPrimitive(this, value!, name, depth);
|
||||
|
||||
case StructureFieldType.Enum:
|
||||
return new StructureFieldEnum(this, value!, name, depth);
|
||||
|
||||
case StructureFieldType.Reference:
|
||||
break;
|
||||
|
||||
case StructureFieldType.Complex:
|
||||
default:
|
||||
|
||||
StructureProcessedObject? processed = _processedObjects.FirstOrDefault(p =>
|
||||
p.Value == value
|
||||
);
|
||||
|
||||
if (processed is null)
|
||||
{
|
||||
return Create(name, value!, depth);
|
||||
}
|
||||
|
||||
StructureFieldReference reference = new StructureFieldReference(
|
||||
this,
|
||||
name,
|
||||
null,
|
||||
depth
|
||||
);
|
||||
processed.AddReference(reference);
|
||||
return reference;
|
||||
}
|
||||
|
||||
throw new("Impossible");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить тип поля структуры по значению.
|
||||
/// </summary>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
/// <returns>Тип поля структуры.</returns>
|
||||
public static StructureFieldType GetFieldType(object? value)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
return StructureFieldType.Null;
|
||||
}
|
||||
|
||||
Type type = value.GetType();
|
||||
|
||||
if (type.IsEnum)
|
||||
{
|
||||
return StructureFieldType.Enum;
|
||||
}
|
||||
|
||||
if (type.IsPrimitive || type == typeof(string))
|
||||
{
|
||||
return StructureFieldType.Primitive;
|
||||
}
|
||||
|
||||
return StructureFieldType.Complex;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Класс для хранения обработанного объекта.
|
||||
/// </summary>
|
||||
/// <param name="value">Объект.</param>
|
||||
internal class StructureProcessedObject(object value)
|
||||
{
|
||||
public readonly object Value = value;
|
||||
|
||||
private StructureFieldComplex? _field;
|
||||
|
||||
public StructureFieldComplex? Field
|
||||
{
|
||||
get => _field;
|
||||
set
|
||||
{
|
||||
_field = value;
|
||||
foreach (StructureFieldReference reference in _references)
|
||||
{
|
||||
reference.Reference = Field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<StructureFieldReference> _references = [];
|
||||
|
||||
public void AddReference(StructureFieldReference reference)
|
||||
{
|
||||
_references.Add(reference);
|
||||
|
||||
if (_field is not null)
|
||||
{
|
||||
reference.Reference = Field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тип поля структуры.
|
||||
/// </summary>
|
||||
internal enum StructureFieldType
|
||||
{
|
||||
/// <summary>
|
||||
/// Пустое поле.
|
||||
/// </summary>
|
||||
Null,
|
||||
|
||||
/// <summary>
|
||||
/// Простое поле.
|
||||
/// </summary>
|
||||
Primitive,
|
||||
|
||||
/// <summary>
|
||||
/// Поле с перечислением.
|
||||
/// </summary>
|
||||
Enum,
|
||||
|
||||
/// <summary>
|
||||
/// Поле с объектом.
|
||||
/// </summary>
|
||||
Complex,
|
||||
|
||||
/// <summary>
|
||||
/// Поле с ссылкой на объект.
|
||||
/// </summary>
|
||||
Reference,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тип лестницы глубины.
|
||||
/// </summary>
|
||||
internal enum LadderType
|
||||
{
|
||||
/// <summary>
|
||||
/// Пустой элемент: [ ].
|
||||
/// </summary>
|
||||
Empty,
|
||||
|
||||
/// <summary>
|
||||
/// Последний элемент: [└].
|
||||
/// </summary>
|
||||
Last,
|
||||
|
||||
/// <summary>
|
||||
/// Проходной элемент: [├] или [│].
|
||||
/// </summary>
|
||||
Through,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Базовый класс для полей структуры.
|
||||
/// </summary>
|
||||
/// <param name="builder">Класс построения структуры данных.</param>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="depth">Глубина поля.</param>
|
||||
internal abstract class StructureField(StructureBuilder builder, string name, int depth)
|
||||
{
|
||||
/// <summary>
|
||||
/// Название поля.
|
||||
/// </summary>
|
||||
public readonly string Name = name;
|
||||
|
||||
/// <summary>
|
||||
/// Тип поля структуры.
|
||||
/// </summary>
|
||||
public abstract StructureFieldType FieldType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Класс построения структуры данных.
|
||||
/// </summary>
|
||||
public readonly StructureBuilder Builder = builder;
|
||||
|
||||
/// <summary>
|
||||
/// Флаг, указывающий, отображается ли поле.
|
||||
/// </summary>
|
||||
public virtual bool Visible { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Глубина поля структуры.
|
||||
/// </summary>
|
||||
public readonly int Depth = depth;
|
||||
|
||||
/// <summary>
|
||||
/// Получить строку, представляющую тип поля.
|
||||
/// </summary>
|
||||
/// <returns>Строка, представляющая тип поля.</returns>
|
||||
public abstract string GetTypeString();
|
||||
|
||||
/// <summary>
|
||||
/// Получить строку, представляющую значение поля.
|
||||
/// </summary>
|
||||
/// <returns>Строка, представляющая значение поля.</returns>
|
||||
public abstract string GetValueString();
|
||||
|
||||
/// <summary>
|
||||
/// Получить набор строк, используя результат функция.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Тип результирующей строки.</typeparam>
|
||||
/// <param name="func">Функция, оборажения поля в результирующую строку.</param>
|
||||
/// <returns>Коллекция, содержащая набор строк.</returns>
|
||||
public virtual ICollection<T> GetRows<T>(Func<StructureField, T> func)
|
||||
{
|
||||
StructureField[] q = [this];
|
||||
return q.Select(func).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Пустое поле структуры.
|
||||
/// </summary>
|
||||
/// <param name="builder">Класс построения структуры данных.</param>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="depth">Глубина поля.</param>
|
||||
internal class StructureFieldNull(StructureBuilder builder, string name, int depth)
|
||||
: StructureField(builder, name, depth)
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override StructureFieldType FieldType => StructureFieldType.Null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetTypeString() => "null";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetValueString() => "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Простое поле структуры.
|
||||
/// </summary>
|
||||
/// <param name="builder">Класс построения структуры данных.</param>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="depth">Глубина поля.</param>
|
||||
internal class StructureFieldPrimitive(
|
||||
StructureBuilder builder,
|
||||
object value,
|
||||
string name,
|
||||
int depth
|
||||
) : StructureField(builder, name, depth)
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override StructureFieldType FieldType => StructureFieldType.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// Значение поля.
|
||||
/// </summary>
|
||||
public readonly object Value = value;
|
||||
|
||||
/// <summary>
|
||||
/// Тип значения поля.
|
||||
/// </summary>
|
||||
public readonly Type Type = value.GetType();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetTypeString()
|
||||
{
|
||||
return Type.GetTypeCode(Type) switch
|
||||
{
|
||||
TypeCode.Boolean => "bool",
|
||||
TypeCode.Int32 => "int",
|
||||
TypeCode.String => "string",
|
||||
TypeCode.Double => "double",
|
||||
_ => Type.Name,
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetValueString()
|
||||
{
|
||||
if (Type == typeof(string))
|
||||
{
|
||||
return "\"" + ((string)Value).Replace("\"", "\\\"") + "\"";
|
||||
}
|
||||
|
||||
return Value.ToString() ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Поле структуры с перечислением.
|
||||
/// </summary>
|
||||
/// <param name="builder">Класс построения структуры данных.</param>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="depth">Глубина поля.</param>
|
||||
internal class StructureFieldEnum(StructureBuilder builder, object value, string name, int depth)
|
||||
: StructureField(builder, name, depth)
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override StructureFieldType FieldType => StructureFieldType.Enum;
|
||||
|
||||
/// <summary>
|
||||
/// Значение поля.
|
||||
/// </summary>
|
||||
public readonly object Value = value;
|
||||
|
||||
/// <summary>
|
||||
/// Тип значения поля.
|
||||
/// </summary>
|
||||
public readonly Type Type = value.GetType();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetTypeString() => "enum";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetValueString() => $"{Type.Name}.{Value}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Поле стркутуры с объектом.
|
||||
/// </summary>
|
||||
internal class StructureFieldComplex : StructureField
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override StructureFieldType FieldType => StructureFieldType.Complex;
|
||||
|
||||
/// <summary>
|
||||
/// Набор полей объекта.
|
||||
/// </summary>
|
||||
public readonly List<StructureField> Fields = [];
|
||||
|
||||
/// <summary>
|
||||
/// Тип значения поля.
|
||||
/// </summary>
|
||||
public readonly Type Type;
|
||||
|
||||
/// <summary>
|
||||
/// Флаг, указывающий, отображается ли поля объекта.
|
||||
/// </summary>
|
||||
public bool Collapsed
|
||||
{
|
||||
get => _collapsed;
|
||||
set
|
||||
{
|
||||
_collapsed = value;
|
||||
UpdateFieldVisibility();
|
||||
}
|
||||
}
|
||||
private bool _collapsed;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Visible
|
||||
{
|
||||
get => _visible;
|
||||
set
|
||||
{
|
||||
_visible = value;
|
||||
UpdateFieldVisibility();
|
||||
}
|
||||
}
|
||||
private bool _visible = true;
|
||||
|
||||
/// <summary>
|
||||
/// Конструктор класса поля структуры с объектом.
|
||||
/// </summary>
|
||||
/// <param name="builder">Класс построения структуры данных.</param>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="depth">Глубина поля.</param>
|
||||
public StructureFieldComplex(StructureBuilder builder, object value, string name, int depth)
|
||||
: base(builder, name, depth)
|
||||
{
|
||||
Type = value.GetType();
|
||||
|
||||
SetFields((dynamic)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обновить видимость полей структуры.
|
||||
/// </summary>
|
||||
private void UpdateFieldVisibility()
|
||||
{
|
||||
foreach (StructureField field in Fields)
|
||||
{
|
||||
field.Visible = Visible && !Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Установить поля структуры `LocaleString`.
|
||||
/// </summary>
|
||||
/// <param name="localeString">Объект строки локализации.</param>
|
||||
private void SetFields(LocaleString localeString)
|
||||
{
|
||||
SetFieldsFromRecords(
|
||||
localeString
|
||||
.Select(field => new FieldRecord(field.Locale.ToString(), field.Value))
|
||||
.ToList()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Установить поля структуры `IEnumerable`.
|
||||
/// </summary>
|
||||
/// <param name="enumerable">Объект-перечисление.</param>
|
||||
private void SetFields(IEnumerable enumerable)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
List<FieldRecord> records = [];
|
||||
|
||||
foreach (object? item in enumerable)
|
||||
{
|
||||
records.Add(new($"[{i++}]", item));
|
||||
}
|
||||
|
||||
SetFieldsFromRecords(records);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Установить поля структуры общего объекта.
|
||||
/// </summary>
|
||||
/// <param name="value">Объект.</param>
|
||||
private void SetFields(object value)
|
||||
{
|
||||
Type type = value.GetType();
|
||||
PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
SetFieldsFromRecords(
|
||||
properties
|
||||
.Where(property =>
|
||||
property.GetAttribute<Newtonsoft.Json.JsonIgnoreAttribute>() is null
|
||||
)
|
||||
.Select(property => new FieldRecord(property.Name, property.GetValue(value, null)))
|
||||
.ToList()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Установить поля структуры из списка записей.
|
||||
/// </summary>
|
||||
/// <param name="records"></param>
|
||||
private void SetFieldsFromRecords(List<FieldRecord> records)
|
||||
{
|
||||
foreach (
|
||||
FieldRecord record in records.Where(record =>
|
||||
record.FieldType == StructureFieldType.Complex
|
||||
)
|
||||
)
|
||||
{
|
||||
record.Allowed = Builder.RegisterProcessed(record.Value!);
|
||||
}
|
||||
|
||||
foreach (FieldRecord record in records)
|
||||
{
|
||||
Fields.Add(
|
||||
record is { FieldType: StructureFieldType.Complex, Allowed: true }
|
||||
? Builder.Create(record.Name, record.Value!, Depth + 1)
|
||||
: Builder.Resolve(record.Name, record.Value!, Depth + 1)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Запись поля структуры.
|
||||
/// </summary>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
private class FieldRecord(string name, object? value)
|
||||
{
|
||||
/// <summary>
|
||||
/// Название поля.
|
||||
/// </summary>
|
||||
public readonly string Name = name;
|
||||
|
||||
/// <summary>
|
||||
/// Значение поля.
|
||||
/// </summary>
|
||||
public readonly object? Value = value;
|
||||
|
||||
/// <summary>
|
||||
/// Тип поля структуры.
|
||||
/// </summary>
|
||||
public StructureFieldType FieldType => StructureBuilder.GetFieldType(Value);
|
||||
|
||||
/// <summary>
|
||||
/// Флаг, указывающий, использовать ли поле.
|
||||
/// </summary>
|
||||
public bool Allowed = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetTypeString()
|
||||
{
|
||||
if (Type.IsGenericEnumerableType())
|
||||
{
|
||||
Type elementType = Type.GetGenericArguments().Single();
|
||||
|
||||
return $"{elementType.Name}[]";
|
||||
}
|
||||
else
|
||||
{
|
||||
return Type.Name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetValueString()
|
||||
{
|
||||
if (Type.IsGenericEnumerableType())
|
||||
{
|
||||
return Fields.Count.ToString() + (Fields.Count == 1 ? " item" : " items");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override ICollection<T> GetRows<T>(Func<StructureField, T> func)
|
||||
{
|
||||
return [.. base.GetRows(func), .. Fields.SelectMany(f => f.GetRows(func)).ToList()];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Поле структуры с ссылкой на объект.
|
||||
/// </summary>
|
||||
/// <param name="builder">Класс построения структуры данных.</param>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="reference">Ссылка на объект.</param>
|
||||
/// <param name="depth">Глубина поля.</param>
|
||||
internal class StructureFieldReference(
|
||||
StructureBuilder builder,
|
||||
string name,
|
||||
StructureFieldComplex? reference,
|
||||
int depth
|
||||
) : StructureField(builder, name, depth)
|
||||
{
|
||||
/// <summary>
|
||||
/// Ссылка на объект.
|
||||
/// </summary>
|
||||
public StructureFieldComplex? Reference = reference;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override StructureFieldType FieldType => StructureFieldType.Reference;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetTypeString() => Reference?.GetTypeString() ?? "???";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetValueString() => "<Reference>";
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Forms.Integration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Pf2eDataScrapper.App;
|
||||
using Pf2eDataScrapper.Helpers;
|
||||
@@ -76,11 +77,12 @@ internal sealed class ImportApplication : ApplicationContext
|
||||
|
||||
case ApplicationStage.InitialSettings:
|
||||
_startFormWpf = ActivatorUtilities.CreateInstance<StartForm>(ServiceProvider);
|
||||
_startFormWpf.WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
_startFormWpf.WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||
_startFormWpf.Closing += Wpf_HandleFormClosing;
|
||||
_startFormWpf.ApplicationStateChange += ApplicationStateChange;
|
||||
_startFormWpf.Show();
|
||||
|
||||
ElementHost.EnableModelessKeyboardInterop(_startFormWpf);
|
||||
CurrentWpfForm = _startFormWpf;
|
||||
break;
|
||||
|
||||
@@ -92,22 +94,25 @@ internal sealed class ImportApplication : ApplicationContext
|
||||
_progressFormWpf = ActivatorUtilities.CreateInstance<ProgressFormWpf>(
|
||||
ServiceProvider
|
||||
);
|
||||
_progressFormWpf.WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
_progressFormWpf.WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||
_progressFormWpf.Closing += Wpf_HandleFormClosing;
|
||||
_progressFormWpf.ApplicationStateChange += ApplicationStateChange;
|
||||
|
||||
CurrentWpfForm = _progressFormWpf;
|
||||
_progressFormWpf.Show();
|
||||
|
||||
ElementHost.EnableModelessKeyboardInterop(_progressFormWpf);
|
||||
CurrentWpfForm = _progressFormWpf;
|
||||
|
||||
executor.Start();
|
||||
break;
|
||||
|
||||
case ApplicationStage.Viewer:
|
||||
_entityForm = ActivatorUtilities.CreateInstance<EntityForm>(ServiceProvider);
|
||||
_entityForm.WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
_entityForm.WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||
_entityForm.Closing += Wpf_HandleFormClosing;
|
||||
//_entityForm.ApplicationStateChange += ApplicationStateChange;
|
||||
_entityForm.Show();
|
||||
|
||||
ElementHost.EnableModelessKeyboardInterop(_entityForm);
|
||||
CurrentWpfForm = _entityForm;
|
||||
break;
|
||||
|
||||
|
||||
@@ -30,8 +30,6 @@ internal static class Program
|
||||
ImportApplication app = ActivatorUtilities.CreateInstance<ImportApplication>(
|
||||
serviceProvider
|
||||
);
|
||||
|
||||
Application.Run(app);
|
||||
}
|
||||
|
||||
private static IServiceCollection RegisterServices()
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
xmlns:local="clr-namespace:Pf2eDataScrapper.WPF.Components"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="clr-namespace:Pf2eDataScrapper.WPF.ViewModels"
|
||||
Padding="3,3,3,3"
|
||||
d:DataContext="{d:DesignInstance Type=viewModels:DbEntityListViewModel}"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="400"
|
||||
@@ -14,10 +15,11 @@
|
||||
<UserControl.Background>
|
||||
<SolidColorBrush Color="{DynamicResource {x:Static SystemColors.ControlColorKey}}" />
|
||||
</UserControl.Background>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="25" />
|
||||
<RowDefinition Height="30" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
@@ -26,33 +28,48 @@
|
||||
<Button
|
||||
Width="25"
|
||||
Margin="0,0,3,0"
|
||||
Click="FirstButton_OnClick"
|
||||
Content="<<"
|
||||
DockPanel.Dock="Left" />
|
||||
DockPanel.Dock="Left"
|
||||
IsEnabled="{Binding HasPrevious}" />
|
||||
|
||||
<Button
|
||||
Width="25"
|
||||
Margin="0,0,3,0"
|
||||
Click="PreviousButton_OnClick"
|
||||
Content="<"
|
||||
DockPanel.Dock="Left" />
|
||||
DockPanel.Dock="Left"
|
||||
IsEnabled="{Binding HasPrevious}" />
|
||||
|
||||
<Button
|
||||
Width="25"
|
||||
Margin="3,0,0,0"
|
||||
Click="LastButton_OnClick"
|
||||
Content=">>"
|
||||
DockPanel.Dock="Right" />
|
||||
DockPanel.Dock="Right"
|
||||
IsEnabled="{Binding HasNext}" />
|
||||
|
||||
<Button
|
||||
Width="25"
|
||||
Margin="3,0,0,0"
|
||||
Click="NextButton_OnClick"
|
||||
Content=">"
|
||||
DockPanel.Dock="Right" />
|
||||
DockPanel.Dock="Right"
|
||||
IsEnabled="{Binding HasNext}" />
|
||||
|
||||
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
|
||||
<Label VerticalContentAlignment="Center" Content="Страница" />
|
||||
<TextBox
|
||||
Width="30"
|
||||
VerticalContentAlignment="Center"
|
||||
Text="{Binding Page, StringFormat=\{0:D\}}" />
|
||||
PreviewTextInput="TextBox_PreviewTextInput">
|
||||
<TextBox.Text>
|
||||
<Binding
|
||||
Mode="TwoWay"
|
||||
Path="Page"
|
||||
UpdateSourceTrigger="PropertyChanged" />
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
<Label VerticalContentAlignment="Center" Content="из" />
|
||||
<Label VerticalContentAlignment="Center" Content="{Binding PageCount, Mode=OneWay}" />
|
||||
|
||||
@@ -62,6 +79,7 @@
|
||||
|
||||
<TextBox
|
||||
Grid.Row="1"
|
||||
Margin="0,3,0,3"
|
||||
VerticalAlignment="Center"
|
||||
Text="TextBox"
|
||||
TextWrapping="Wrap" />
|
||||
@@ -69,7 +87,8 @@
|
||||
<ListView
|
||||
Grid.Row="2"
|
||||
d:ItemsSource="{d:SampleData ItemCount=50}"
|
||||
ItemsSource="{Binding Entities}">
|
||||
ItemsSource="{Binding Entities}"
|
||||
SelectedItem="{Binding SelectedEntity}">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
<GridViewColumn
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Pf2eDataScrapper.WPF.ViewModels;
|
||||
using System.Windows;
|
||||
using Pf2eDataScrapper.WPF.ViewModels;
|
||||
|
||||
namespace Pf2eDataScrapper.WPF.Components;
|
||||
|
||||
@@ -25,4 +26,32 @@ internal partial class DbEntityListControl : System.Windows.Controls.UserControl
|
||||
}
|
||||
|
||||
//https://www.youtube.com/watch?v=54ZmhbpjBmg
|
||||
|
||||
private void FirstButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel?.FirstPage();
|
||||
}
|
||||
|
||||
private void PreviousButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel?.PreviousPage();
|
||||
}
|
||||
|
||||
private void NextButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel?.NextPage();
|
||||
}
|
||||
|
||||
private void LastButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel?.LastPage();
|
||||
}
|
||||
|
||||
private void TextBox_PreviewTextInput(
|
||||
object sender,
|
||||
System.Windows.Input.TextCompositionEventArgs e
|
||||
)
|
||||
{
|
||||
e.Handled = !int.TryParse(e.Text, out int result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
<UserControl
|
||||
x:Class="Pf2eDataScrapper.WPF.Components.EntityViewControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:conv="clr-namespace:Pf2eDataScrapper.WPF.ViewModels.EntityViewStructure"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Pf2eDataScrapper.WPF.Components"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="clr-namespace:Pf2eDataScrapper.WPF.ViewModels"
|
||||
d:DataContext="{d:DesignInstance Type=viewModels:EntityViewViewModel}"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="300"
|
||||
x:ClassModifier="internal"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<UserControl.BorderBrush>
|
||||
<SolidColorBrush Color="{DynamicResource {x:Static SystemColors.ActiveBorderColorKey}}" />
|
||||
</UserControl.BorderBrush>
|
||||
<UserControl.Background>
|
||||
<SolidColorBrush Color="{DynamicResource {x:Static SystemColors.ControlColorKey}}" />
|
||||
</UserControl.Background>
|
||||
|
||||
<UserControl.Resources>
|
||||
<conv:LadderToImageConverter x:Key="LadderToImageConverter" />
|
||||
|
||||
<Style TargetType="{x:Type DockPanel}">
|
||||
<Setter Property="Background" Value="{x:Static SystemColors.ControlBrush}" />
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{x:Static SystemColors.ControlLightBrush}" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
|
||||
<ScrollViewer>
|
||||
<ItemsControl ItemsSource="{Binding Rows}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
|
||||
<DockPanel Style="{StaticResource {x:Type DockPanel}}">
|
||||
<DockPanel.Height>
|
||||
<DynamicResource ResourceKey="ItemHeight" />
|
||||
</DockPanel.Height>
|
||||
<StackPanel Width="200" Orientation="Horizontal">
|
||||
<Image>
|
||||
<Image.Source>
|
||||
<DrawingImage>
|
||||
<DrawingImage.Drawing>
|
||||
<MultiBinding Converter="{StaticResource LadderToImageConverter}" ConverterParameter="{Binding ItemHeight, RelativeSource={RelativeSource AncestorType=local:EntityViewControl}}">
|
||||
<Binding Path="Depth" />
|
||||
<Binding Path="ItemHeight" RelativeSource="{RelativeSource AncestorType=local:EntityViewControl}"></Binding>
|
||||
<DynamicResource ResourceKey="ItemHeight" />
|
||||
</MultiBinding>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</Image.Source>
|
||||
</Image>
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock HorizontalAlignment="Center" Text="{Binding Type}" />
|
||||
<TextBlock HorizontalAlignment="Center" Text="{Binding Value}" />
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using Pf2eDataScrapper.Forms.Controls.EntityTreeViewer;
|
||||
using Pf2eDataScrapper.Helpers;
|
||||
using Pf2eDataScrapper.WPF.ViewModels;
|
||||
using Pf2eDataScrapper.WPF.ViewModels.EntityViewStructure;
|
||||
using Color = System.Windows.Media.Color;
|
||||
using SystemColors = System.Windows.SystemColors;
|
||||
|
||||
namespace Pf2eDataScrapper.WPF.Components;
|
||||
|
||||
internal partial class EntityViewControl : System.Windows.Controls.UserControl
|
||||
{
|
||||
public EntityViewViewModel ViewModel
|
||||
{
|
||||
get => _viewModel;
|
||||
set
|
||||
{
|
||||
_viewModel = value;
|
||||
DataContext = ViewModel;
|
||||
}
|
||||
}
|
||||
private EntityViewViewModel _viewModel;
|
||||
|
||||
#region "Поля элемента управления"
|
||||
|
||||
/// <summary>
|
||||
/// Высота строки поля структуры.
|
||||
/// </summary>
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
|
||||
[Category("Entity Render")]
|
||||
[Description("The height of each item in the tree view.")]
|
||||
public int ItemHeight
|
||||
{
|
||||
get => _itemHeight;
|
||||
set
|
||||
{
|
||||
_itemHeight = value;
|
||||
Resources["ItemHeight"] = ItemHeight;
|
||||
}
|
||||
}
|
||||
private int _itemHeight = 20;
|
||||
|
||||
/// <summary>
|
||||
/// Ширина отступа глубины.
|
||||
/// </summary>
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
|
||||
[Category("Entity Render")]
|
||||
[Description("The size of tab.")]
|
||||
public int DepthSize { get; set; } = 20;
|
||||
|
||||
/// <summary>
|
||||
/// Размер шрифта типа поля сущности.
|
||||
/// </summary>
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
|
||||
[Category("Entity Render")]
|
||||
[Description("The size of tab.")]
|
||||
public float TypeFontSize { get; set; } = 9.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Размер кнопки плюс/минус.
|
||||
/// </summary>
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
|
||||
[Category("Entity Render")]
|
||||
[Description("The size of plus/minus button.")]
|
||||
public int ButtonSize { get; set; } = 20;
|
||||
|
||||
/// <summary>
|
||||
/// Ширина имени поля сущности.
|
||||
/// </summary>
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
[Browsable(false)]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public int NameWidth { get; private set; } = 200;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public EntityViewControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Resources["ItemHeight"] = ItemHeight;
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,15 @@ namespace Pf2eDataScrapper.WPF.Stores;
|
||||
|
||||
public class SelectedEntityStore
|
||||
{
|
||||
private Entity? _selectedEntityType;
|
||||
private Entity? _selectedEntity;
|
||||
|
||||
public Entity? SelectedEntityType
|
||||
public Entity? SelectedEntity
|
||||
{
|
||||
get => _selectedEntityType;
|
||||
get => _selectedEntity;
|
||||
set
|
||||
{
|
||||
_selectedEntityType = value;
|
||||
SelectedEntityTypeChanged?.Invoke(SelectedEntityType);
|
||||
_selectedEntity = value;
|
||||
SelectedEntityTypeChanged?.Invoke(SelectedEntity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.ObjectModel;
|
||||
using AngleSharp.Browser;
|
||||
using LinqToDB.DataProvider.Access;
|
||||
using Pf2eDataScrapper.WPF.Stores;
|
||||
using Pf2eModel;
|
||||
using Pf2eModel.Model.Base;
|
||||
@@ -11,9 +12,16 @@ internal class DbEntityListViewModel : ViewModelBase
|
||||
{
|
||||
public Pf2eDbContext Context { get; }
|
||||
public SelectedEntityTypeStore SelectedEntityTypeStore { get; }
|
||||
public SelectedEntityStore SelectedEntityStore { get; }
|
||||
|
||||
private EntityType? SelectedEntityType => SelectedEntityTypeStore.SelectedEntityType;
|
||||
|
||||
public Entity? SelectedEntity
|
||||
{
|
||||
get => SelectedEntityStore.SelectedEntity;
|
||||
set => SelectedEntityStore.SelectedEntity = value;
|
||||
}
|
||||
|
||||
public int Page
|
||||
{
|
||||
get => _page;
|
||||
@@ -21,6 +29,9 @@ internal class DbEntityListViewModel : ViewModelBase
|
||||
{
|
||||
_page = Math.Clamp(value, 1, PageCount);
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(HasPrevious));
|
||||
OnPropertyChanged(nameof(HasNext));
|
||||
Reload();
|
||||
}
|
||||
}
|
||||
private int _page = 1;
|
||||
@@ -33,6 +44,8 @@ internal class DbEntityListViewModel : ViewModelBase
|
||||
_entitiesCount = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(PageCount));
|
||||
OnPropertyChanged(nameof(HasPrevious));
|
||||
OnPropertyChanged(nameof(HasNext));
|
||||
}
|
||||
}
|
||||
private int _entitiesCount = 1;
|
||||
@@ -45,6 +58,7 @@ internal class DbEntityListViewModel : ViewModelBase
|
||||
_perPage = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(PageCount));
|
||||
Reload();
|
||||
}
|
||||
}
|
||||
private int _perPage = 30;
|
||||
@@ -56,6 +70,7 @@ internal class DbEntityListViewModel : ViewModelBase
|
||||
{
|
||||
_search = value;
|
||||
OnPropertyChanged();
|
||||
Reload();
|
||||
}
|
||||
}
|
||||
private string _search = "";
|
||||
@@ -75,11 +90,13 @@ internal class DbEntityListViewModel : ViewModelBase
|
||||
|
||||
public DbEntityListViewModel(
|
||||
Pf2eDbContext context,
|
||||
SelectedEntityTypeStore selectedEntityTypeStore
|
||||
SelectedEntityTypeStore selectedEntityTypeStore,
|
||||
SelectedEntityStore selectedEntityStore
|
||||
)
|
||||
{
|
||||
Context = context;
|
||||
SelectedEntityTypeStore = selectedEntityTypeStore;
|
||||
SelectedEntityStore = selectedEntityStore;
|
||||
|
||||
SelectedEntityTypeStore.SelectedEntityTypeChanged +=
|
||||
SelectedEntityTypeStore_SelectedEntityTypeChanged;
|
||||
@@ -101,15 +118,34 @@ internal class DbEntityListViewModel : ViewModelBase
|
||||
return;
|
||||
}
|
||||
|
||||
Query();
|
||||
Page = 1;
|
||||
Reload();
|
||||
}
|
||||
|
||||
#region "Методы кнопок"
|
||||
|
||||
public void FirstPage() => Page = 1;
|
||||
|
||||
public void PreviousPage() => Page--;
|
||||
|
||||
public void NextPage() => Page++;
|
||||
|
||||
public void LastPage() => Page = PageCount;
|
||||
|
||||
public bool HasPrevious => Page != 1;
|
||||
|
||||
public bool HasNext => Page != PageCount;
|
||||
|
||||
#endregion
|
||||
|
||||
#region "Запросы"
|
||||
|
||||
/// <summary>
|
||||
/// Поток, в котором будет выполняться запрос к базе данных.
|
||||
/// </summary>
|
||||
private Thread? _queryThread;
|
||||
|
||||
private void Query()
|
||||
public void Reload()
|
||||
{
|
||||
Abort();
|
||||
_queryThread = new(PerformQuery);
|
||||
@@ -143,13 +179,17 @@ internal class DbEntityListViewModel : ViewModelBase
|
||||
e.Type == SelectedEntityType && Pf2eDbContext.LocaleSearch(e.Name, Search)
|
||||
);
|
||||
|
||||
Page = 1;
|
||||
EntitiesCount = query.Count();
|
||||
Entities = query.OrderBy(e => e.Id).Skip((Page - 1) * PerPage).Take(PerPage).ToList();
|
||||
Entities =
|
||||
[
|
||||
.. query.OrderBy(e => e.Id).Skip((Page - 1) * PerPage).Take(PerPage).ToList(),
|
||||
];
|
||||
}
|
||||
catch (ThreadInterruptedException)
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -45,5 +45,7 @@ internal class EntityFormViewModel : ViewModelBase
|
||||
{
|
||||
EntityTypes = Context.Entities.Select(e => e.Type).Distinct().ToList();
|
||||
OnPropertyChanged(nameof(EntityTypes));
|
||||
|
||||
SelectedEntityTypeStore.SelectedEntityType = EntityTypes.Any() ? EntityTypes.First() : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace Pf2eDataScrapper.WPF.ViewModels.EntityViewStructure;
|
||||
|
||||
/// <summary>
|
||||
/// Тип лестницы глубины.
|
||||
/// </summary>
|
||||
internal enum LadderType
|
||||
{
|
||||
/// <summary>
|
||||
/// Пустой элемент: [ ].
|
||||
/// </summary>
|
||||
Empty,
|
||||
|
||||
/// <summary>
|
||||
/// Последний элемент: [└].
|
||||
/// </summary>
|
||||
Last,
|
||||
|
||||
/// <summary>
|
||||
/// Проходной элемент: [├] или [│].
|
||||
/// </summary>
|
||||
Through,
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
namespace Pf2eDataScrapper.WPF.ViewModels.EntityViewStructure;
|
||||
|
||||
/// <summary>
|
||||
/// Класс для построения структуры данных класса.
|
||||
/// </summary>
|
||||
internal class StructureBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Список обработанных объектов.
|
||||
/// </summary>
|
||||
private readonly List<StructureProcessedObject> _processedObjects = [];
|
||||
|
||||
/// <summary>
|
||||
/// Создание поля структуры.
|
||||
/// </summary>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
/// <param name="depth">Глубина, на котором поле отображается.</param>
|
||||
/// <returns>Поле структуры.</returns>
|
||||
public StructureFieldComplex Create(string name, object value, int depth = 0)
|
||||
{
|
||||
_ = RegisterProcessed(value);
|
||||
|
||||
StructureFieldComplex field = new(this, value, name, depth);
|
||||
|
||||
CompleteProcessed(value, field);
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Регистрация обработанного объекта.
|
||||
/// </summary>
|
||||
/// <param name="value">Регистрируемый объект.</param>
|
||||
/// <returns>`true`, если объект был добавлен; `false`, если объект был добавлен ранее.</returns>
|
||||
public bool RegisterProcessed(object value)
|
||||
{
|
||||
if (_processedObjects.Any(p => p.Value == value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
StructureProcessedObject processed = new(value);
|
||||
|
||||
_processedObjects.Add(processed);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Дополнить обработанный объект.
|
||||
/// </summary>
|
||||
/// <param name="value">Значение.</param>
|
||||
/// <param name="field">Поле объекта.</param>
|
||||
public void CompleteProcessed(object value, StructureFieldComplex field)
|
||||
{
|
||||
StructureProcessedObject? q = _processedObjects.FirstOrDefault(p => p.Value == value);
|
||||
|
||||
if (q == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
q.Field = field;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Выбор поля структуры по значению.
|
||||
/// </summary>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
/// <param name="depth">Глубина, на котором поле отображается.</param>
|
||||
/// <returns></returns>
|
||||
public StructureField Resolve(string name, object? value, int depth)
|
||||
{
|
||||
switch (GetFieldType(value))
|
||||
{
|
||||
case StructureFieldType.Null:
|
||||
return new StructureFieldNull(this, name, depth);
|
||||
|
||||
case StructureFieldType.Primitive:
|
||||
return new StructureFieldPrimitive(this, value!, name, depth);
|
||||
|
||||
case StructureFieldType.Enum:
|
||||
return new StructureFieldEnum(this, value!, name, depth);
|
||||
|
||||
case StructureFieldType.Reference:
|
||||
break;
|
||||
|
||||
case StructureFieldType.Complex:
|
||||
default:
|
||||
|
||||
StructureProcessedObject? processed = _processedObjects.FirstOrDefault(p =>
|
||||
p.Value == value
|
||||
);
|
||||
|
||||
if (processed is null)
|
||||
{
|
||||
return Create(name, value!, depth);
|
||||
}
|
||||
|
||||
StructureFieldReference reference = new StructureFieldReference(
|
||||
this,
|
||||
name,
|
||||
null,
|
||||
depth
|
||||
);
|
||||
processed.AddReference(reference);
|
||||
return reference;
|
||||
}
|
||||
|
||||
throw new("Impossible");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить тип поля структуры по значению.
|
||||
/// </summary>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
/// <returns>Тип поля структуры.</returns>
|
||||
public static StructureFieldType GetFieldType(object? value)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
return StructureFieldType.Null;
|
||||
}
|
||||
|
||||
Type type = value.GetType();
|
||||
|
||||
if (type.IsEnum)
|
||||
{
|
||||
return StructureFieldType.Enum;
|
||||
}
|
||||
|
||||
if (type.IsPrimitive || type == typeof(string))
|
||||
{
|
||||
return StructureFieldType.Primitive;
|
||||
}
|
||||
|
||||
return StructureFieldType.Complex;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
namespace Pf2eDataScrapper.WPF.ViewModels.EntityViewStructure;
|
||||
|
||||
/// <summary>
|
||||
/// Базовый класс для полей структуры.
|
||||
/// </summary>
|
||||
/// <param name="builder">Класс построения структуры данных.</param>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="depth">Глубина поля.</param>
|
||||
internal abstract class StructureField(StructureBuilder builder, string name, int depth)
|
||||
{
|
||||
/// <summary>
|
||||
/// Название поля.
|
||||
/// </summary>
|
||||
public readonly string Name = name;
|
||||
|
||||
/// <summary>
|
||||
/// Тип поля структуры.
|
||||
/// </summary>
|
||||
public abstract StructureFieldType FieldType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Класс построения структуры данных.
|
||||
/// </summary>
|
||||
public readonly StructureBuilder Builder = builder;
|
||||
|
||||
/// <summary>
|
||||
/// Флаг, указывающий, отображается ли поле.
|
||||
/// </summary>
|
||||
public virtual bool Visible { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Глубина поля структуры.
|
||||
/// </summary>
|
||||
public readonly int Depth = depth;
|
||||
|
||||
/// <summary>
|
||||
/// Получить строку, представляющую тип поля.
|
||||
/// </summary>
|
||||
/// <returns>Строка, представляющая тип поля.</returns>
|
||||
public abstract string GetTypeString();
|
||||
|
||||
/// <summary>
|
||||
/// Получить строку, представляющую значение поля.
|
||||
/// </summary>
|
||||
/// <returns>Строка, представляющая значение поля.</returns>
|
||||
public abstract string GetValueString();
|
||||
|
||||
/// <summary>
|
||||
/// Получить набор строк, используя результат функция.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Тип результирующей строки.</typeparam>
|
||||
/// <param name="func">Функция, оборажения поля в результирующую строку.</param>
|
||||
/// <returns>Коллекция, содержащая набор строк.</returns>
|
||||
public virtual ICollection<T> GetRows<T>(Func<StructureField, T> func)
|
||||
{
|
||||
StructureField[] q = [this];
|
||||
return q.Select(func).ToList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using LinqToDB.Extensions;
|
||||
using Pf2eModel.Model.Common.I18n;
|
||||
|
||||
namespace Pf2eDataScrapper.WPF.ViewModels.EntityViewStructure;
|
||||
|
||||
/// <summary>
|
||||
/// Поле стркутуры с объектом.
|
||||
/// </summary>
|
||||
internal class StructureFieldComplex : StructureField
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override StructureFieldType FieldType => StructureFieldType.Complex;
|
||||
|
||||
/// <summary>
|
||||
/// Набор полей объекта.
|
||||
/// </summary>
|
||||
public readonly List<StructureField> Fields = [];
|
||||
|
||||
/// <summary>
|
||||
/// Тип значения поля.
|
||||
/// </summary>
|
||||
public readonly Type Type;
|
||||
|
||||
/// <summary>
|
||||
/// Флаг, указывающий, отображается ли поля объекта.
|
||||
/// </summary>
|
||||
public bool Collapsed
|
||||
{
|
||||
get => _collapsed;
|
||||
set
|
||||
{
|
||||
_collapsed = value;
|
||||
UpdateFieldVisibility();
|
||||
}
|
||||
}
|
||||
private bool _collapsed;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Visible
|
||||
{
|
||||
get => _visible;
|
||||
set
|
||||
{
|
||||
_visible = value;
|
||||
UpdateFieldVisibility();
|
||||
}
|
||||
}
|
||||
private bool _visible = true;
|
||||
|
||||
/// <summary>
|
||||
/// Конструктор класса поля структуры с объектом.
|
||||
/// </summary>
|
||||
/// <param name="builder">Класс построения структуры данных.</param>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="depth">Глубина поля.</param>
|
||||
public StructureFieldComplex(StructureBuilder builder, object value, string name, int depth)
|
||||
: base(builder, name, depth)
|
||||
{
|
||||
Type = value.GetType();
|
||||
|
||||
SetFields((dynamic)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обновить видимость полей структуры.
|
||||
/// </summary>
|
||||
private void UpdateFieldVisibility()
|
||||
{
|
||||
foreach (StructureField field in Fields)
|
||||
{
|
||||
field.Visible = Visible && !Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Установить поля структуры `LocaleString`.
|
||||
/// </summary>
|
||||
/// <param name="localeString">Объект строки локализации.</param>
|
||||
private void SetFields(LocaleString localeString)
|
||||
{
|
||||
SetFieldsFromRecords(
|
||||
localeString
|
||||
.Select(field => new FieldRecord(field.Locale.ToString(), field.Value))
|
||||
.ToList()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Установить поля структуры `IEnumerable`.
|
||||
/// </summary>
|
||||
/// <param name="enumerable">Объект-перечисление.</param>
|
||||
private void SetFields(IEnumerable enumerable)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
List<FieldRecord> records = [];
|
||||
|
||||
foreach (object? item in enumerable)
|
||||
{
|
||||
records.Add(new($"[{i++}]", item));
|
||||
}
|
||||
|
||||
SetFieldsFromRecords(records);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Установить поля структуры общего объекта.
|
||||
/// </summary>
|
||||
/// <param name="value">Объект.</param>
|
||||
private void SetFields(object value)
|
||||
{
|
||||
Type type = value.GetType();
|
||||
PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
SetFieldsFromRecords(
|
||||
properties
|
||||
.Where(property =>
|
||||
property.GetAttribute<Newtonsoft.Json.JsonIgnoreAttribute>() is null
|
||||
)
|
||||
.Select(property => new FieldRecord(property.Name, property.GetValue(value, null)))
|
||||
.ToList()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Установить поля структуры из списка записей.
|
||||
/// </summary>
|
||||
/// <param name="records"></param>
|
||||
private void SetFieldsFromRecords(List<FieldRecord> records)
|
||||
{
|
||||
foreach (
|
||||
FieldRecord record in records.Where(record =>
|
||||
record.FieldType == StructureFieldType.Complex
|
||||
)
|
||||
)
|
||||
{
|
||||
record.Allowed = Builder.RegisterProcessed(record.Value!);
|
||||
}
|
||||
|
||||
foreach (FieldRecord record in records)
|
||||
{
|
||||
Fields.Add(
|
||||
record is { FieldType: StructureFieldType.Complex, Allowed: true }
|
||||
? Builder.Create(record.Name, record.Value!, Depth + 1)
|
||||
: Builder.Resolve(record.Name, record.Value!, Depth + 1)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Запись поля структуры.
|
||||
/// </summary>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
private class FieldRecord(string name, object? value)
|
||||
{
|
||||
/// <summary>
|
||||
/// Название поля.
|
||||
/// </summary>
|
||||
public readonly string Name = name;
|
||||
|
||||
/// <summary>
|
||||
/// Значение поля.
|
||||
/// </summary>
|
||||
public readonly object? Value = value;
|
||||
|
||||
/// <summary>
|
||||
/// Тип поля структуры.
|
||||
/// </summary>
|
||||
public StructureFieldType FieldType => StructureBuilder.GetFieldType(Value);
|
||||
|
||||
/// <summary>
|
||||
/// Флаг, указывающий, использовать ли поле.
|
||||
/// </summary>
|
||||
public bool Allowed = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetTypeString()
|
||||
{
|
||||
if (Type.IsGenericEnumerableType())
|
||||
{
|
||||
Type elementType = Type.GetGenericArguments().Single();
|
||||
|
||||
return $"{elementType.Name}[]";
|
||||
}
|
||||
else
|
||||
{
|
||||
return Type.Name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetValueString()
|
||||
{
|
||||
if (Type.IsGenericEnumerableType())
|
||||
{
|
||||
return Fields.Count.ToString() + (Fields.Count == 1 ? " item" : " items");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override ICollection<T> GetRows<T>(Func<StructureField, T> func)
|
||||
{
|
||||
return [.. base.GetRows(func), .. Fields.SelectMany(f => f.GetRows(func)).ToList()];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
namespace Pf2eDataScrapper.WPF.ViewModels.EntityViewStructure;
|
||||
|
||||
/// <summary>
|
||||
/// Поле структуры с перечислением.
|
||||
/// </summary>
|
||||
/// <param name="builder">Класс построения структуры данных.</param>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="depth">Глубина поля.</param>
|
||||
internal class StructureFieldEnum(StructureBuilder builder, object value, string name, int depth)
|
||||
: StructureField(builder, name, depth)
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override StructureFieldType FieldType => StructureFieldType.Enum;
|
||||
|
||||
/// <summary>
|
||||
/// Значение поля.
|
||||
/// </summary>
|
||||
public readonly object Value = value;
|
||||
|
||||
/// <summary>
|
||||
/// Тип значения поля.
|
||||
/// </summary>
|
||||
public readonly Type Type = value.GetType();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetTypeString() => "enum";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetValueString() => $"{Type.Name}.{Value}";
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace Pf2eDataScrapper.WPF.ViewModels.EntityViewStructure;
|
||||
|
||||
/// <summary>
|
||||
/// Пустое поле структуры.
|
||||
/// </summary>
|
||||
/// <param name="builder">Класс построения структуры данных.</param>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="depth">Глубина поля.</param>
|
||||
internal class StructureFieldNull(StructureBuilder builder, string name, int depth)
|
||||
: StructureField(builder, name, depth)
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override StructureFieldType FieldType => StructureFieldType.Null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetTypeString() => "null";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetValueString() => "";
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
namespace Pf2eDataScrapper.WPF.ViewModels.EntityViewStructure;
|
||||
|
||||
/// <summary>
|
||||
/// Простое поле структуры.
|
||||
/// </summary>
|
||||
/// <param name="builder">Класс построения структуры данных.</param>
|
||||
/// <param name="value">Значение поля.</param>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="depth">Глубина поля.</param>
|
||||
internal class StructureFieldPrimitive(
|
||||
StructureBuilder builder,
|
||||
object value,
|
||||
string name,
|
||||
int depth
|
||||
) : StructureField(builder, name, depth)
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override StructureFieldType FieldType => StructureFieldType.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// Значение поля.
|
||||
/// </summary>
|
||||
public readonly object Value = value;
|
||||
|
||||
/// <summary>
|
||||
/// Тип значения поля.
|
||||
/// </summary>
|
||||
public readonly Type Type = value.GetType();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetTypeString()
|
||||
{
|
||||
return Type.GetTypeCode(Type) switch
|
||||
{
|
||||
TypeCode.Boolean => "bool",
|
||||
TypeCode.Int32 => "int",
|
||||
TypeCode.String => "string",
|
||||
TypeCode.Double => "double",
|
||||
_ => Type.Name,
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetValueString()
|
||||
{
|
||||
if (Type == typeof(string))
|
||||
{
|
||||
return "\"" + ((string)Value).Replace("\"", "\\\"") + "\"";
|
||||
}
|
||||
|
||||
return Value.ToString() ?? string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace Pf2eDataScrapper.WPF.ViewModels.EntityViewStructure;
|
||||
|
||||
/// <summary>
|
||||
/// Поле структуры с ссылкой на объект.
|
||||
/// </summary>
|
||||
/// <param name="builder">Класс построения структуры данных.</param>
|
||||
/// <param name="name">Название поля.</param>
|
||||
/// <param name="reference">Ссылка на объект.</param>
|
||||
/// <param name="depth">Глубина поля.</param>
|
||||
internal class StructureFieldReference(
|
||||
StructureBuilder builder,
|
||||
string name,
|
||||
StructureFieldComplex? reference,
|
||||
int depth
|
||||
) : StructureField(builder, name, depth)
|
||||
{
|
||||
/// <summary>
|
||||
/// Ссылка на объект.
|
||||
/// </summary>
|
||||
public StructureFieldComplex? Reference = reference;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override StructureFieldType FieldType => StructureFieldType.Reference;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetTypeString() => Reference?.GetTypeString() ?? "???";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetValueString() => "<Reference>";
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace Pf2eDataScrapper.WPF.ViewModels.EntityViewStructure;
|
||||
|
||||
/// <summary>
|
||||
/// Тип поля структуры.
|
||||
/// </summary>
|
||||
internal enum StructureFieldType
|
||||
{
|
||||
/// <summary>
|
||||
/// Пустое поле.
|
||||
/// </summary>
|
||||
Null,
|
||||
|
||||
/// <summary>
|
||||
/// Простое поле.
|
||||
/// </summary>
|
||||
Primitive,
|
||||
|
||||
/// <summary>
|
||||
/// Поле с перечислением.
|
||||
/// </summary>
|
||||
Enum,
|
||||
|
||||
/// <summary>
|
||||
/// Поле с объектом.
|
||||
/// </summary>
|
||||
Complex,
|
||||
|
||||
/// <summary>
|
||||
/// Поле с ссылкой на объект.
|
||||
/// </summary>
|
||||
Reference,
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace Pf2eDataScrapper.WPF.ViewModels.EntityViewStructure;
|
||||
|
||||
/// <summary>
|
||||
/// Класс для хранения обработанного объекта.
|
||||
/// </summary>
|
||||
/// <param name="value">Объект.</param>
|
||||
internal class StructureProcessedObject(object value)
|
||||
{
|
||||
public readonly object Value = value;
|
||||
|
||||
private StructureFieldComplex? _field;
|
||||
|
||||
public StructureFieldComplex? Field
|
||||
{
|
||||
get => _field;
|
||||
set
|
||||
{
|
||||
_field = value;
|
||||
foreach (StructureFieldReference reference in _references)
|
||||
{
|
||||
reference.Reference = Field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<StructureFieldReference> _references = [];
|
||||
|
||||
public void AddReference(StructureFieldReference reference)
|
||||
{
|
||||
_references.Add(reference);
|
||||
|
||||
if (_field is not null)
|
||||
{
|
||||
reference.Reference = Field;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
using System.CodeDom;
|
||||
using System.Globalization;
|
||||
using System.Security.Cryptography;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Header;
|
||||
using Brush = System.Windows.Media.Brush;
|
||||
using Brushes = System.Windows.Media.Brushes;
|
||||
using Color = System.Windows.Media.Color;
|
||||
|
||||
namespace Pf2eDataScrapper.WPF.ViewModels.EntityViewStructure;
|
||||
|
||||
internal class StructureRow(StructureField item)
|
||||
{
|
||||
/// <summary>
|
||||
/// Отображаемая строка поля сущности.
|
||||
/// </summary>
|
||||
public StructureField Item { get; } = item;
|
||||
|
||||
/// <summary>
|
||||
/// Список типов лестницы.
|
||||
/// </summary>
|
||||
public List<LadderType> Ladder { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Флаг наличия кнопки.
|
||||
/// </summary>
|
||||
public bool HasButton => Item.FieldType == StructureFieldType.Complex;
|
||||
|
||||
/// <summary>
|
||||
/// Флаг, указывающий, отображается ли строка.
|
||||
/// </summary>
|
||||
public bool Visible => Item.Visible;
|
||||
|
||||
/// <summary>
|
||||
/// Глубина отображения поля.
|
||||
/// </summary>
|
||||
public int Depth => Item.Depth;
|
||||
|
||||
/// <summary>
|
||||
/// Строковое представление названия поля.
|
||||
/// </summary>
|
||||
public string Name => Item.Name;
|
||||
|
||||
/// <summary>
|
||||
/// Строковое представление типа поля.
|
||||
/// </summary>
|
||||
public string Type
|
||||
{
|
||||
get
|
||||
{
|
||||
string result = Item.GetTypeString();
|
||||
if (result != "")
|
||||
{
|
||||
result = $@"({result})";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Строковое представление значения поля.
|
||||
/// </summary>
|
||||
public string Value => Item.GetValueString();
|
||||
}
|
||||
|
||||
// [ValueConversion(typeof(IEnumerable<LadderType>), typeof(Image), ParameterType = typeof(int))]
|
||||
public class LadderToImageConverter : IMultiValueConverter
|
||||
{
|
||||
#region IValueConverter Members
|
||||
|
||||
|
||||
public object? Convert(
|
||||
object[] value,
|
||||
Type targetType,
|
||||
object? parameter,
|
||||
System.Globalization.CultureInfo culture
|
||||
)
|
||||
{
|
||||
if (value.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
IEnumerable<LadderType> ladder = (IEnumerable<LadderType>)value[0];
|
||||
|
||||
if (!ladder.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int n = ladder.Count();
|
||||
if (!int.TryParse((string)value[1], out int itemHeight))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
DrawingGroup result = new();
|
||||
|
||||
// Rectangle
|
||||
|
||||
result.Children.Add(
|
||||
new GeometryDrawing()
|
||||
{
|
||||
Geometry = new RectangleGeometry(new(0, 0, n * 100, 100)),
|
||||
Pen = new(Brushes.Transparent, 0),
|
||||
}
|
||||
);
|
||||
|
||||
result.Children.Add(
|
||||
new GeometryDrawing()
|
||||
{
|
||||
Geometry = GetLadderGeometryGroup(ladder, itemHeight),
|
||||
Pen = new(Brushes.Black, 1),
|
||||
}
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private GeometryGroup GetLadderGeometryGroup(IEnumerable<LadderType> ladder, int h)
|
||||
{
|
||||
float h2 = h * 0.5f;
|
||||
GeometryGroup geometryGroup = new();
|
||||
|
||||
//geometryGroup.Children.Add(new RectangleGeometry(new(0, 0, ladder.Count() * 100, 100)));
|
||||
|
||||
int i = 0;
|
||||
|
||||
foreach (LadderType ladderType in ladder)
|
||||
{
|
||||
switch (ladderType)
|
||||
{
|
||||
case LadderType.Empty:
|
||||
break;
|
||||
case LadderType.Last:
|
||||
geometryGroup.Children.Add(
|
||||
new LineGeometry(new(i * h + h2, 0), new(i * h + h2, h2))
|
||||
);
|
||||
geometryGroup.Children.Add(
|
||||
new LineGeometry(new(i * h + h2, 50), new(i * h + h, h2))
|
||||
);
|
||||
break;
|
||||
case LadderType.Through:
|
||||
geometryGroup.Children.Add(
|
||||
new LineGeometry(new(i * h + h2, 0), new(i * h + h2, h))
|
||||
);
|
||||
if (i == ladder.Count() - 1)
|
||||
{
|
||||
geometryGroup.Children.Add(
|
||||
new LineGeometry(new(i * h + h2, h2), new(i * h + h2, h2))
|
||||
);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(ladder));
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return geometryGroup;
|
||||
}
|
||||
|
||||
public object[] ConvertBack(
|
||||
object value,
|
||||
Type[] targetTypes,
|
||||
object parameter,
|
||||
CultureInfo culture
|
||||
)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Data;
|
||||
using Pf2eDataScrapper.Forms.Controls.EntityTreeViewer;
|
||||
using Pf2eDataScrapper.Helpers;
|
||||
using Pf2eDataScrapper.WPF.Stores;
|
||||
using Pf2eDataScrapper.WPF.ViewModels.EntityViewStructure;
|
||||
using Pf2eModel;
|
||||
|
||||
namespace Pf2eDataScrapper.WPF.ViewModels;
|
||||
|
||||
internal class EntityViewViewModel : ViewModelBase
|
||||
{
|
||||
public Pf2eDbContext Context { get; }
|
||||
public SelectedEntityStore SelectedEntityStore { get; }
|
||||
|
||||
public EntityView? CurrentEntityView
|
||||
{
|
||||
get => _currentEntityView;
|
||||
set
|
||||
{
|
||||
_currentEntityView = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
private EntityView? _currentEntityView;
|
||||
|
||||
public ObservableCollection<StructureRow> Rows
|
||||
{
|
||||
get => _rows;
|
||||
set
|
||||
{
|
||||
_rows = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
private ObservableCollection<StructureRow> _rows = [];
|
||||
|
||||
private readonly List<StructureField> _fields = [];
|
||||
|
||||
private readonly List<StructureRow> _allRows = [];
|
||||
|
||||
public EntityViewViewModel(Pf2eDbContext context, SelectedEntityStore selectedEntityStore)
|
||||
{
|
||||
Context = context;
|
||||
SelectedEntityStore = selectedEntityStore;
|
||||
|
||||
SelectedEntityStore.SelectedEntityTypeChanged +=
|
||||
SelectedEntityStore_SelectedEntityTypeChanged;
|
||||
}
|
||||
|
||||
~EntityViewViewModel()
|
||||
{
|
||||
SelectedEntityStore.SelectedEntityTypeChanged -=
|
||||
SelectedEntityStore_SelectedEntityTypeChanged;
|
||||
}
|
||||
|
||||
private void SelectedEntityStore_SelectedEntityTypeChanged(Pf2eModel.Model.Base.Entity? entity)
|
||||
{
|
||||
if (entity is null)
|
||||
{
|
||||
CurrentEntityView = null;
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentEntityView = new(Context, entity.Type, entity.Id);
|
||||
|
||||
if (CurrentEntityView.Entity is null)
|
||||
{
|
||||
CurrentEntityView = null;
|
||||
return;
|
||||
}
|
||||
|
||||
StructureBuilder builder = new();
|
||||
|
||||
_fields.Clear();
|
||||
_fields.Add(builder.Create("Entity", CurrentEntityView.Entity));
|
||||
|
||||
if (CurrentEntityView.SubEntity is not null)
|
||||
{
|
||||
string typeName = CurrentEntityView.SubEntity.GetType().Name;
|
||||
|
||||
_fields.Add(builder.Create(typeName, CurrentEntityView.SubEntity));
|
||||
}
|
||||
|
||||
_allRows.Clear();
|
||||
_allRows.AddRange(
|
||||
_fields.SelectMany(field =>
|
||||
field.GetRows(structureField => new StructureRow(structureField))
|
||||
)
|
||||
);
|
||||
|
||||
Rows.Clear();
|
||||
foreach (StructureRow row in _allRows)
|
||||
{
|
||||
Rows.Add(row);
|
||||
}
|
||||
|
||||
BuildLadder();
|
||||
|
||||
OnPropertyChanged(nameof(Rows));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Построение лестницы для строк полей структуры.
|
||||
/// </summary>
|
||||
/// <param name="rows">Набор строк полей структуры.</param>
|
||||
private void BuildLadder()
|
||||
{
|
||||
List<LadderType> lastLadder = [];
|
||||
|
||||
for (int i = Rows.Count - 1; i >= 0; i--)
|
||||
{
|
||||
StructureRow row = Rows[i];
|
||||
List<LadderType> ladder;
|
||||
|
||||
if (row.Depth == 0)
|
||||
{
|
||||
ladder = [];
|
||||
}
|
||||
else if (row.Depth < lastLadder.Count)
|
||||
{
|
||||
ladder =
|
||||
[
|
||||
.. lastLadder[..(row.Depth - 1)],
|
||||
lastLadder[row.Depth - 1] == LadderType.Empty
|
||||
? LadderType.Last
|
||||
: LadderType.Through,
|
||||
];
|
||||
}
|
||||
else if (row.Depth == lastLadder.Count)
|
||||
{
|
||||
ladder = lastLadder;
|
||||
}
|
||||
else
|
||||
{
|
||||
ladder =
|
||||
[
|
||||
.. lastLadder,
|
||||
.. Enumerable.Repeat(LadderType.Empty, row.Depth - lastLadder.Count - 1),
|
||||
LadderType.Last,
|
||||
];
|
||||
}
|
||||
|
||||
row.Ladder = ladder;
|
||||
|
||||
lastLadder = ladder
|
||||
.Select(l => (l == LadderType.Last) ? LadderType.Through : l)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,25 +8,27 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="clr-namespace:Pf2eDataScrapper.WPF.ViewModels"
|
||||
Title="EntityForm"
|
||||
Width="1000"
|
||||
Height="600"
|
||||
Width="1400"
|
||||
Height="800"
|
||||
d:DataContext="{d:DesignInstance Type=viewModels:EntityFormViewModel}"
|
||||
x:ClassModifier="internal"
|
||||
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
|
||||
Loaded="Window_Loaded"
|
||||
mc:Ignorable="d">
|
||||
<Grid>
|
||||
<Grid ClipToBounds="True">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="20" />
|
||||
<RowDefinition Height="150" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="215*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="2*" />
|
||||
<ColumnDefinition Width="537*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="2*" />
|
||||
<ColumnDefinition Width="322*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<GridSplitter
|
||||
Grid.Row="0"
|
||||
@@ -52,11 +54,24 @@
|
||||
x:Name="EntityTypeListBox"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="3,3,3,0"
|
||||
d:ItemsSource="{d:SampleData ItemCount=10}"
|
||||
ItemsSource="{Binding EntityTypes}"
|
||||
SelectedItem="{Binding SelectedEntityType}" />
|
||||
|
||||
<components:DbEntityListControl x:Name="DbEntityListControl" Grid.Row="2" Grid.Column="0" />
|
||||
<components:DbEntityListControl
|
||||
x:Name="DbEntityListControl"
|
||||
Grid.Row="2"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="0"
|
||||
ClipToBounds="True" />
|
||||
|
||||
<components:EntityViewControl
|
||||
x:Name="EntityViewControl"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="4"
|
||||
Grid.Column="2"
|
||||
FontFamily="Consolas" />
|
||||
|
||||
<TextBox
|
||||
Grid.Row="0"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows;
|
||||
using Pf2eDataScrapper.WPF.Components;
|
||||
using Pf2eDataScrapper.WPF.ViewModels;
|
||||
using Pf2eModel;
|
||||
using Pf2eModel.Model.Enum;
|
||||
@@ -17,13 +18,21 @@ internal partial class EntityForm : Window
|
||||
public EntityForm(
|
||||
Pf2eDbContext context,
|
||||
EntityFormViewModel viewModel,
|
||||
DbEntityListViewModel dbEntityListViewModel
|
||||
DbEntityListViewModel dbEntityListViewModel,
|
||||
EntityViewViewModel entityViewViewModel
|
||||
)
|
||||
{
|
||||
Context = context;
|
||||
ViewModel = viewModel;
|
||||
DataContext = ViewModel;
|
||||
InitializeComponent();
|
||||
|
||||
DbEntityListControl.ViewModel = dbEntityListViewModel;
|
||||
EntityViewControl.ViewModel = entityViewViewModel;
|
||||
|
||||
DataContext = ViewModel;
|
||||
|
||||
dbEntityListViewModel.Reload();
|
||||
}
|
||||
|
||||
private void Window_Loaded(object sender, RoutedEventArgs e) { }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Pf2eDataScrapper.WPF.Components;
|
||||
using Pf2eDataScrapper.WPF.Stores;
|
||||
using Pf2eDataScrapper.WPF.ViewModels;
|
||||
|
||||
@@ -12,6 +13,7 @@ public static class WpfServiceProviderExtension
|
||||
// ViewModels
|
||||
.AddTransient<EntityFormViewModel>()
|
||||
.AddTransient<DbEntityListViewModel>()
|
||||
.AddTransient<EntityViewViewModel>()
|
||||
// Stores
|
||||
.AddSingleton<SelectedEntityTypeStore>()
|
||||
.AddSingleton<SelectedEntityStore>()
|
||||
|
||||
Reference in New Issue
Block a user