Глобальное обновление #1
@@ -1,5 +1,6 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using Pf2eDataScrapper.Helpers;
|
using Pf2eDataScrapper.Helpers;
|
||||||
|
using Pf2eDataScrapper.WPF.ViewModels.EntityViewStructure;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Forms.Controls.EntityTreeViewer;
|
namespace Pf2eDataScrapper.Forms.Controls.EntityTreeViewer;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using FontAwesome.Sharp;
|
using FontAwesome.Sharp;
|
||||||
|
using Pf2eDataScrapper.WPF.ViewModels.EntityViewStructure;
|
||||||
using Font = System.Drawing.Font;
|
using Font = System.Drawing.Font;
|
||||||
using FontStyle = System.Drawing.FontStyle;
|
using FontStyle = System.Drawing.FontStyle;
|
||||||
using Image = System.Drawing.Image;
|
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.ComponentModel;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Forms.Integration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Pf2eDataScrapper.App;
|
using Pf2eDataScrapper.App;
|
||||||
using Pf2eDataScrapper.Helpers;
|
using Pf2eDataScrapper.Helpers;
|
||||||
@@ -76,11 +77,12 @@ internal sealed class ImportApplication : ApplicationContext
|
|||||||
|
|
||||||
case ApplicationStage.InitialSettings:
|
case ApplicationStage.InitialSettings:
|
||||||
_startFormWpf = ActivatorUtilities.CreateInstance<StartForm>(ServiceProvider);
|
_startFormWpf = ActivatorUtilities.CreateInstance<StartForm>(ServiceProvider);
|
||||||
_startFormWpf.WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
_startFormWpf.WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||||
_startFormWpf.Closing += Wpf_HandleFormClosing;
|
_startFormWpf.Closing += Wpf_HandleFormClosing;
|
||||||
_startFormWpf.ApplicationStateChange += ApplicationStateChange;
|
_startFormWpf.ApplicationStateChange += ApplicationStateChange;
|
||||||
_startFormWpf.Show();
|
_startFormWpf.Show();
|
||||||
|
|
||||||
|
ElementHost.EnableModelessKeyboardInterop(_startFormWpf);
|
||||||
CurrentWpfForm = _startFormWpf;
|
CurrentWpfForm = _startFormWpf;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -92,22 +94,25 @@ internal sealed class ImportApplication : ApplicationContext
|
|||||||
_progressFormWpf = ActivatorUtilities.CreateInstance<ProgressFormWpf>(
|
_progressFormWpf = ActivatorUtilities.CreateInstance<ProgressFormWpf>(
|
||||||
ServiceProvider
|
ServiceProvider
|
||||||
);
|
);
|
||||||
_progressFormWpf.WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
_progressFormWpf.WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||||
_progressFormWpf.Closing += Wpf_HandleFormClosing;
|
_progressFormWpf.Closing += Wpf_HandleFormClosing;
|
||||||
_progressFormWpf.ApplicationStateChange += ApplicationStateChange;
|
_progressFormWpf.ApplicationStateChange += ApplicationStateChange;
|
||||||
|
|
||||||
CurrentWpfForm = _progressFormWpf;
|
|
||||||
_progressFormWpf.Show();
|
_progressFormWpf.Show();
|
||||||
|
|
||||||
|
ElementHost.EnableModelessKeyboardInterop(_progressFormWpf);
|
||||||
|
CurrentWpfForm = _progressFormWpf;
|
||||||
|
|
||||||
executor.Start();
|
executor.Start();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ApplicationStage.Viewer:
|
case ApplicationStage.Viewer:
|
||||||
_entityForm = ActivatorUtilities.CreateInstance<EntityForm>(ServiceProvider);
|
_entityForm = ActivatorUtilities.CreateInstance<EntityForm>(ServiceProvider);
|
||||||
_entityForm.WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
_entityForm.WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||||
_entityForm.Closing += Wpf_HandleFormClosing;
|
_entityForm.Closing += Wpf_HandleFormClosing;
|
||||||
//_entityForm.ApplicationStateChange += ApplicationStateChange;
|
//_entityForm.ApplicationStateChange += ApplicationStateChange;
|
||||||
_entityForm.Show();
|
_entityForm.Show();
|
||||||
|
|
||||||
|
ElementHost.EnableModelessKeyboardInterop(_entityForm);
|
||||||
CurrentWpfForm = _entityForm;
|
CurrentWpfForm = _entityForm;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -30,8 +30,6 @@ internal static class Program
|
|||||||
ImportApplication app = ActivatorUtilities.CreateInstance<ImportApplication>(
|
ImportApplication app = ActivatorUtilities.CreateInstance<ImportApplication>(
|
||||||
serviceProvider
|
serviceProvider
|
||||||
);
|
);
|
||||||
|
|
||||||
Application.Run(app);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IServiceCollection RegisterServices()
|
private static IServiceCollection RegisterServices()
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
xmlns:local="clr-namespace:Pf2eDataScrapper.WPF.Components"
|
xmlns:local="clr-namespace:Pf2eDataScrapper.WPF.Components"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:viewModels="clr-namespace:Pf2eDataScrapper.WPF.ViewModels"
|
xmlns:viewModels="clr-namespace:Pf2eDataScrapper.WPF.ViewModels"
|
||||||
|
Padding="3,3,3,3"
|
||||||
d:DataContext="{d:DesignInstance Type=viewModels:DbEntityListViewModel}"
|
d:DataContext="{d:DesignInstance Type=viewModels:DbEntityListViewModel}"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
d:DesignWidth="400"
|
d:DesignWidth="400"
|
||||||
@@ -14,10 +15,11 @@
|
|||||||
<UserControl.Background>
|
<UserControl.Background>
|
||||||
<SolidColorBrush Color="{DynamicResource {x:Static SystemColors.ControlColorKey}}" />
|
<SolidColorBrush Color="{DynamicResource {x:Static SystemColors.ControlColorKey}}" />
|
||||||
</UserControl.Background>
|
</UserControl.Background>
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="25" />
|
<RowDefinition Height="25" />
|
||||||
<RowDefinition Height="30" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
@@ -26,33 +28,48 @@
|
|||||||
<Button
|
<Button
|
||||||
Width="25"
|
Width="25"
|
||||||
Margin="0,0,3,0"
|
Margin="0,0,3,0"
|
||||||
|
Click="FirstButton_OnClick"
|
||||||
Content="<<"
|
Content="<<"
|
||||||
DockPanel.Dock="Left" />
|
DockPanel.Dock="Left"
|
||||||
|
IsEnabled="{Binding HasPrevious}" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
Width="25"
|
Width="25"
|
||||||
Margin="0,0,3,0"
|
Margin="0,0,3,0"
|
||||||
|
Click="PreviousButton_OnClick"
|
||||||
Content="<"
|
Content="<"
|
||||||
DockPanel.Dock="Left" />
|
DockPanel.Dock="Left"
|
||||||
|
IsEnabled="{Binding HasPrevious}" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
Width="25"
|
Width="25"
|
||||||
Margin="3,0,0,0"
|
Margin="3,0,0,0"
|
||||||
|
Click="LastButton_OnClick"
|
||||||
Content=">>"
|
Content=">>"
|
||||||
DockPanel.Dock="Right" />
|
DockPanel.Dock="Right"
|
||||||
|
IsEnabled="{Binding HasNext}" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
Width="25"
|
Width="25"
|
||||||
Margin="3,0,0,0"
|
Margin="3,0,0,0"
|
||||||
|
Click="NextButton_OnClick"
|
||||||
Content=">"
|
Content=">"
|
||||||
DockPanel.Dock="Right" />
|
DockPanel.Dock="Right"
|
||||||
|
IsEnabled="{Binding HasNext}" />
|
||||||
|
|
||||||
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
|
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
|
||||||
<Label VerticalContentAlignment="Center" Content="Страница" />
|
<Label VerticalContentAlignment="Center" Content="Страница" />
|
||||||
<TextBox
|
<TextBox
|
||||||
Width="30"
|
Width="30"
|
||||||
VerticalContentAlignment="Center"
|
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="из" />
|
||||||
<Label VerticalContentAlignment="Center" Content="{Binding PageCount, Mode=OneWay}" />
|
<Label VerticalContentAlignment="Center" Content="{Binding PageCount, Mode=OneWay}" />
|
||||||
|
|
||||||
@@ -62,6 +79,7 @@
|
|||||||
|
|
||||||
<TextBox
|
<TextBox
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
|
Margin="0,3,0,3"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="TextBox"
|
Text="TextBox"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
@@ -69,7 +87,8 @@
|
|||||||
<ListView
|
<ListView
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
d:ItemsSource="{d:SampleData ItemCount=50}"
|
d:ItemsSource="{d:SampleData ItemCount=50}"
|
||||||
ItemsSource="{Binding Entities}">
|
ItemsSource="{Binding Entities}"
|
||||||
|
SelectedItem="{Binding SelectedEntity}">
|
||||||
<ListView.View>
|
<ListView.View>
|
||||||
<GridView>
|
<GridView>
|
||||||
<GridViewColumn
|
<GridViewColumn
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Pf2eDataScrapper.WPF.ViewModels;
|
using System.Windows;
|
||||||
|
using Pf2eDataScrapper.WPF.ViewModels;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.WPF.Components;
|
namespace Pf2eDataScrapper.WPF.Components;
|
||||||
|
|
||||||
@@ -25,4 +26,32 @@ internal partial class DbEntityListControl : System.Windows.Controls.UserControl
|
|||||||
}
|
}
|
||||||
|
|
||||||
//https://www.youtube.com/watch?v=54ZmhbpjBmg
|
//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
|
public class SelectedEntityStore
|
||||||
{
|
{
|
||||||
private Entity? _selectedEntityType;
|
private Entity? _selectedEntity;
|
||||||
|
|
||||||
public Entity? SelectedEntityType
|
public Entity? SelectedEntity
|
||||||
{
|
{
|
||||||
get => _selectedEntityType;
|
get => _selectedEntity;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_selectedEntityType = value;
|
_selectedEntity = value;
|
||||||
SelectedEntityTypeChanged?.Invoke(SelectedEntityType);
|
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 Pf2eDataScrapper.WPF.Stores;
|
||||||
using Pf2eModel;
|
using Pf2eModel;
|
||||||
using Pf2eModel.Model.Base;
|
using Pf2eModel.Model.Base;
|
||||||
@@ -11,9 +12,16 @@ internal class DbEntityListViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
public Pf2eDbContext Context { get; }
|
public Pf2eDbContext Context { get; }
|
||||||
public SelectedEntityTypeStore SelectedEntityTypeStore { get; }
|
public SelectedEntityTypeStore SelectedEntityTypeStore { get; }
|
||||||
|
public SelectedEntityStore SelectedEntityStore { get; }
|
||||||
|
|
||||||
private EntityType? SelectedEntityType => SelectedEntityTypeStore.SelectedEntityType;
|
private EntityType? SelectedEntityType => SelectedEntityTypeStore.SelectedEntityType;
|
||||||
|
|
||||||
|
public Entity? SelectedEntity
|
||||||
|
{
|
||||||
|
get => SelectedEntityStore.SelectedEntity;
|
||||||
|
set => SelectedEntityStore.SelectedEntity = value;
|
||||||
|
}
|
||||||
|
|
||||||
public int Page
|
public int Page
|
||||||
{
|
{
|
||||||
get => _page;
|
get => _page;
|
||||||
@@ -21,6 +29,9 @@ internal class DbEntityListViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
_page = Math.Clamp(value, 1, PageCount);
|
_page = Math.Clamp(value, 1, PageCount);
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
|
OnPropertyChanged(nameof(HasPrevious));
|
||||||
|
OnPropertyChanged(nameof(HasNext));
|
||||||
|
Reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private int _page = 1;
|
private int _page = 1;
|
||||||
@@ -33,6 +44,8 @@ internal class DbEntityListViewModel : ViewModelBase
|
|||||||
_entitiesCount = value;
|
_entitiesCount = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
OnPropertyChanged(nameof(PageCount));
|
OnPropertyChanged(nameof(PageCount));
|
||||||
|
OnPropertyChanged(nameof(HasPrevious));
|
||||||
|
OnPropertyChanged(nameof(HasNext));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private int _entitiesCount = 1;
|
private int _entitiesCount = 1;
|
||||||
@@ -45,6 +58,7 @@ internal class DbEntityListViewModel : ViewModelBase
|
|||||||
_perPage = value;
|
_perPage = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
OnPropertyChanged(nameof(PageCount));
|
OnPropertyChanged(nameof(PageCount));
|
||||||
|
Reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private int _perPage = 30;
|
private int _perPage = 30;
|
||||||
@@ -56,6 +70,7 @@ internal class DbEntityListViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
_search = value;
|
_search = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
|
Reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private string _search = "";
|
private string _search = "";
|
||||||
@@ -75,11 +90,13 @@ internal class DbEntityListViewModel : ViewModelBase
|
|||||||
|
|
||||||
public DbEntityListViewModel(
|
public DbEntityListViewModel(
|
||||||
Pf2eDbContext context,
|
Pf2eDbContext context,
|
||||||
SelectedEntityTypeStore selectedEntityTypeStore
|
SelectedEntityTypeStore selectedEntityTypeStore,
|
||||||
|
SelectedEntityStore selectedEntityStore
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
Context = context;
|
Context = context;
|
||||||
SelectedEntityTypeStore = selectedEntityTypeStore;
|
SelectedEntityTypeStore = selectedEntityTypeStore;
|
||||||
|
SelectedEntityStore = selectedEntityStore;
|
||||||
|
|
||||||
SelectedEntityTypeStore.SelectedEntityTypeChanged +=
|
SelectedEntityTypeStore.SelectedEntityTypeChanged +=
|
||||||
SelectedEntityTypeStore_SelectedEntityTypeChanged;
|
SelectedEntityTypeStore_SelectedEntityTypeChanged;
|
||||||
@@ -101,15 +118,34 @@ internal class DbEntityListViewModel : ViewModelBase
|
|||||||
return;
|
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>
|
||||||
/// Поток, в котором будет выполняться запрос к базе данных.
|
/// Поток, в котором будет выполняться запрос к базе данных.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Thread? _queryThread;
|
private Thread? _queryThread;
|
||||||
|
|
||||||
private void Query()
|
public void Reload()
|
||||||
{
|
{
|
||||||
Abort();
|
Abort();
|
||||||
_queryThread = new(PerformQuery);
|
_queryThread = new(PerformQuery);
|
||||||
@@ -143,13 +179,17 @@ internal class DbEntityListViewModel : ViewModelBase
|
|||||||
e.Type == SelectedEntityType && Pf2eDbContext.LocaleSearch(e.Name, Search)
|
e.Type == SelectedEntityType && Pf2eDbContext.LocaleSearch(e.Name, Search)
|
||||||
);
|
);
|
||||||
|
|
||||||
Page = 1;
|
|
||||||
EntitiesCount = query.Count();
|
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)
|
catch (ThreadInterruptedException)
|
||||||
{
|
{
|
||||||
// Ignore
|
// Ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,5 +45,7 @@ internal class EntityFormViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
EntityTypes = Context.Entities.Select(e => e.Type).Distinct().ToList();
|
EntityTypes = Context.Entities.Select(e => e.Type).Distinct().ToList();
|
||||||
OnPropertyChanged(nameof(EntityTypes));
|
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:viewModels="clr-namespace:Pf2eDataScrapper.WPF.ViewModels"
|
xmlns:viewModels="clr-namespace:Pf2eDataScrapper.WPF.ViewModels"
|
||||||
Title="EntityForm"
|
Title="EntityForm"
|
||||||
Width="1000"
|
Width="1400"
|
||||||
Height="600"
|
Height="800"
|
||||||
d:DataContext="{d:DesignInstance Type=viewModels:EntityFormViewModel}"
|
d:DataContext="{d:DesignInstance Type=viewModels:EntityFormViewModel}"
|
||||||
x:ClassModifier="internal"
|
x:ClassModifier="internal"
|
||||||
|
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
|
||||||
|
Loaded="Window_Loaded"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<Grid>
|
<Grid ClipToBounds="True">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="20" />
|
<RowDefinition Height="20" />
|
||||||
<RowDefinition Height="150" />
|
<RowDefinition Height="150" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition />
|
<RowDefinition Height="*" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="1*" />
|
<ColumnDefinition Width="215*" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="2*" />
|
<ColumnDefinition Width="537*" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="2*" />
|
<ColumnDefinition Width="322*" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<GridSplitter
|
<GridSplitter
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
@@ -52,11 +54,24 @@
|
|||||||
x:Name="EntityTypeListBox"
|
x:Name="EntityTypeListBox"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
|
Margin="3,3,3,0"
|
||||||
d:ItemsSource="{d:SampleData ItemCount=10}"
|
d:ItemsSource="{d:SampleData ItemCount=10}"
|
||||||
ItemsSource="{Binding EntityTypes}"
|
ItemsSource="{Binding EntityTypes}"
|
||||||
SelectedItem="{Binding SelectedEntityType}" />
|
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
|
<TextBox
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using Pf2eDataScrapper.WPF.Components;
|
||||||
using Pf2eDataScrapper.WPF.ViewModels;
|
using Pf2eDataScrapper.WPF.ViewModels;
|
||||||
using Pf2eModel;
|
using Pf2eModel;
|
||||||
using Pf2eModel.Model.Enum;
|
using Pf2eModel.Model.Enum;
|
||||||
@@ -17,13 +18,21 @@ internal partial class EntityForm : Window
|
|||||||
public EntityForm(
|
public EntityForm(
|
||||||
Pf2eDbContext context,
|
Pf2eDbContext context,
|
||||||
EntityFormViewModel viewModel,
|
EntityFormViewModel viewModel,
|
||||||
DbEntityListViewModel dbEntityListViewModel
|
DbEntityListViewModel dbEntityListViewModel,
|
||||||
|
EntityViewViewModel entityViewViewModel
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
Context = context;
|
Context = context;
|
||||||
ViewModel = viewModel;
|
ViewModel = viewModel;
|
||||||
DataContext = ViewModel;
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
DbEntityListControl.ViewModel = dbEntityListViewModel;
|
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 Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Pf2eDataScrapper.WPF.Components;
|
||||||
using Pf2eDataScrapper.WPF.Stores;
|
using Pf2eDataScrapper.WPF.Stores;
|
||||||
using Pf2eDataScrapper.WPF.ViewModels;
|
using Pf2eDataScrapper.WPF.ViewModels;
|
||||||
|
|
||||||
@@ -12,6 +13,7 @@ public static class WpfServiceProviderExtension
|
|||||||
// ViewModels
|
// ViewModels
|
||||||
.AddTransient<EntityFormViewModel>()
|
.AddTransient<EntityFormViewModel>()
|
||||||
.AddTransient<DbEntityListViewModel>()
|
.AddTransient<DbEntityListViewModel>()
|
||||||
|
.AddTransient<EntityViewViewModel>()
|
||||||
// Stores
|
// Stores
|
||||||
.AddSingleton<SelectedEntityTypeStore>()
|
.AddSingleton<SelectedEntityTypeStore>()
|
||||||
.AddSingleton<SelectedEntityStore>()
|
.AddSingleton<SelectedEntityStore>()
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ using Pf2eModel.Model.Enum;
|
|||||||
namespace Pf2eModel.Migrations
|
namespace Pf2eModel.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(Pf2eDbContext))]
|
[DbContext(typeof(Pf2eDbContext))]
|
||||||
[Migration("20250515080518_1")]
|
[Migration("20250516162158_1")]
|
||||||
partial class _1
|
partial class _1
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
Reference in New Issue
Block a user