From d6fef4c2c886064a4d056e5bee351aeed08b63f2 Mon Sep 17 00:00:00 2001 From: Ilya Kozhemyakin Date: Mon, 28 Apr 2025 00:26:50 +0400 Subject: [PATCH] =?UTF-8?q?=D0=9D=D0=B0=D1=87=D0=B0=D0=BB=D0=BE=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D0=BD=D0=B0=D0=B4=20=D0=BA?= =?UTF-8?q?=D0=B0=D1=81=D1=82=D0=BE=D0=BC=D0=BD=D1=8B=D0=BC=20=D0=B4=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=B2=D0=BE=D0=BC=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../EntityTreeViewerControl.Designer.cs | 66 +++++ Pf2eViewer/EntityTreeViewerControl.cs | 229 ++++++++++++++++++ Pf2eViewer/EntityTreeViewerControl.resx | 120 +++++++++ Pf2eViewer/Program.cs | 3 +- Pf2eViewer/TestForm.Designer.cs | 57 +++++ Pf2eViewer/TestForm.cs | 32 +++ Pf2eViewer/TestForm.resx | 120 +++++++++ 7 files changed, 626 insertions(+), 1 deletion(-) create mode 100644 Pf2eViewer/EntityTreeViewerControl.Designer.cs create mode 100644 Pf2eViewer/EntityTreeViewerControl.cs create mode 100644 Pf2eViewer/EntityTreeViewerControl.resx create mode 100644 Pf2eViewer/TestForm.Designer.cs create mode 100644 Pf2eViewer/TestForm.cs create mode 100644 Pf2eViewer/TestForm.resx diff --git a/Pf2eViewer/EntityTreeViewerControl.Designer.cs b/Pf2eViewer/EntityTreeViewerControl.Designer.cs new file mode 100644 index 0000000..8c79c98 --- /dev/null +++ b/Pf2eViewer/EntityTreeViewerControl.Designer.cs @@ -0,0 +1,66 @@ +namespace Pf2eViewer; + +partial class EntityTreeViewerControl +{ + /// + /// Обязательная переменная конструктора. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Освободить все используемые ресурсы. + /// + /// истинно, если управляемый ресурс должен быть удален; иначе ложно. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Код, автоматически созданный конструктором компонентов + + /// + /// Требуемый метод для поддержки конструктора — не изменяйте + /// содержимое этого метода с помощью редактора кода. + /// + private void InitializeComponent() + { + VerticalScrollBar = new VScrollBar(); + ContentPanel = new Panel(); + SuspendLayout(); + // + // VerticalScrollBar + // + VerticalScrollBar.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right; + VerticalScrollBar.Location = new Point(400, 0); + VerticalScrollBar.Name = "VerticalScrollBar"; + VerticalScrollBar.Size = new Size(20, 520); + VerticalScrollBar.TabIndex = 0; + // + // ContentPanel + // + ContentPanel.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; + ContentPanel.Location = new Point(0, 0); + ContentPanel.Name = "ContentPanel"; + ContentPanel.Size = new Size(400, 520); + ContentPanel.TabIndex = 2; + // + // EntityTreeViewerControl + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + Controls.Add(ContentPanel); + Controls.Add(VerticalScrollBar); + Name = "EntityTreeViewerControl"; + Size = new Size(420, 520); + ResumeLayout(false); + } + + #endregion + + private VScrollBar VerticalScrollBar; + private Panel ContentPanel; +} diff --git a/Pf2eViewer/EntityTreeViewerControl.cs b/Pf2eViewer/EntityTreeViewerControl.cs new file mode 100644 index 0000000..db1f4d5 --- /dev/null +++ b/Pf2eViewer/EntityTreeViewerControl.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Windows.Forms.VisualStyles; +using Microsoft.EntityFrameworkCore.Metadata.Internal; + +namespace Pf2eViewer; + +public partial class EntityTreeViewerControl : UserControl +{ + private int _itemHeight = 20; + + private EntityView? _entityView = null; + + public int ItemHeight + { + get => _itemHeight; + set + { + _itemHeight = value; + UpdateView(); + } + } + + public EntityView? EntityView + { + get => _entityView; + set + { + _entityView = value; + UpdateView(); + } + } + + public EntityTreeViewerControl() + { + InitializeComponent(); + } + + public void UpdateView() + { + ContentPanel.Controls.Clear(); + + if (EntityView?.Entity is null) + { + return; + } + + Rows.Clear(); + + Rows.Add(new HeaderRow("Entity")); + Rows.Add(new ObjectComplex(null, EntityView.Entity)); + Rows.Add(new HeaderRow(EntityView.SubEntity?.GetType().ToString() ?? "SubEntity")); + Rows.Add(new ObjectComplex(null, EntityView.SubEntity)); + + List displayRows = Rows.SelectMany(r => r.GetTextRows()).ToList(); + + for (int i = 0; i < displayRows.Count; i++) + { + DisplayRow dr = displayRows[i]; + + TextBox tb = new() + { + Top = i * ItemHeight, + Left = 0, + Width = ContentPanel.Width, + Height = ItemHeight, + Font = Font, + Text = string.Join( + "", + [new(' ', dr.Depth * 2), dr.Name, dr.Value != "" ? " : " : "", dr.Value] + ), + Margin = new(0), + }; + + ContentPanel.Controls.Add(tb); + } + } + + private Panel GetObjectPanel(object obj) + { + Panel result = new(); + + Type type = obj.GetType(); + + PropertyInfo[] properties = type.GetProperties(); + /* + FieldInfo[] fields = type.GetFields( + BindingFlags.Instance + | BindingFlags.Static + | BindingFlags.NonPublic + | BindingFlags.Public + ); + */ + for (int i = 0; i < properties.Length; i++) + { + PropertyInfo property = properties[i]; + + TextBox tb = new() + { + Top = i * ItemHeight, + Left = 0, + Width = ContentPanel.Width, + Height = ItemHeight, + Font = Font, + Text = property.Name, + Margin = new(0), + }; + + result.Controls.Add(tb); + } + + result.Width = ContentPanel.Width; + result.Height = properties.Length * ItemHeight * 2; + + return result; + } + + private List Rows = []; + + private class DisplayRow + { + public string Name = ""; + + public string Value = ""; + + public int Depth = 0; + } + + private abstract class Row + { + public abstract ICollection GetTextRows(); + + public int Depth = 0; + } + + private class HeaderRow(string name) : Row + { + public readonly string Name = name; + + public override ICollection GetTextRows() + { + return [new() { Name = Name, Depth = Depth }]; + } + } + + private abstract class ObjectField(PropertyInfo property, object? parent) : Row + { + public readonly PropertyInfo? Property = property; + + public readonly string Name = property.Name; + + public readonly Type Type = property.PropertyType; + + public abstract int Rows { get; } + + public static ObjectField Resolve(PropertyInfo property, object? parent) + { + return property.PropertyType.IsPrimitive + ? new ObjectPrimitive(property, parent) + : new ObjectComplex(property, parent); + } + } + + private class ObjectPrimitive : ObjectField + { + public readonly object? Value; + + public override int Rows => 1; + + public ObjectPrimitive(PropertyInfo? property, object? parent) + : base(property, parent) + { + Value = Property.GetValue(parent, null); + } + + public override ICollection GetTextRows() + { + return + [ + new() + { + Name = Name, + Value = Value?.ToString() ?? "null", + Depth = Depth, + }, + ]; + } + } + + private class ObjectComplex : ObjectField + { + public readonly List Fields; + + public bool Collapsed = false; + + public override int Rows => 1 + Fields.Select(f => f.Rows).Sum(); + + public ObjectComplex(PropertyInfo? property, object? parent) + : base(property, parent) + { + PropertyInfo[] properties = Type.GetProperties(); + + object? value = Property?.GetValue(parent, null) ?? parent; + + Fields = properties.Select(f => Resolve(f, value)).ToList(); + } + + public override ICollection GetTextRows() + { + List rows = [new() { Name = Name, Depth = Depth }]; + + if (!Collapsed) + { + rows.AddRange(Fields.SelectMany(f => f.GetTextRows()).ToList()); + } + + return rows; + } + } +} diff --git a/Pf2eViewer/EntityTreeViewerControl.resx b/Pf2eViewer/EntityTreeViewerControl.resx new file mode 100644 index 0000000..8b2ff64 --- /dev/null +++ b/Pf2eViewer/EntityTreeViewerControl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Pf2eViewer/Program.cs b/Pf2eViewer/Program.cs index cef9888..2e0c427 100644 --- a/Pf2eViewer/Program.cs +++ b/Pf2eViewer/Program.cs @@ -11,6 +11,7 @@ internal static class Program // To customize application configuration such as set high DPI settings or default font, // see https://aka.ms/applicationconfiguration. ApplicationConfiguration.Initialize(); - Application.Run(new MainViewForm()); + // Application.Run(new MainViewForm()); + Application.Run(new TestForm()); } } diff --git a/Pf2eViewer/TestForm.Designer.cs b/Pf2eViewer/TestForm.Designer.cs new file mode 100644 index 0000000..519a836 --- /dev/null +++ b/Pf2eViewer/TestForm.Designer.cs @@ -0,0 +1,57 @@ +namespace Pf2eViewer; + +partial class TestForm +{ + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + entityTreeViewerControl1 = new EntityTreeViewerControl(); + SuspendLayout(); + // + // entityTreeViewerControl1 + // + entityTreeViewerControl1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; + entityTreeViewerControl1.EntityView = null; + entityTreeViewerControl1.Location = new Point(12, 12); + entityTreeViewerControl1.Name = "entityTreeViewerControl1"; + entityTreeViewerControl1.Size = new Size(426, 632); + entityTreeViewerControl1.TabIndex = 0; + // + // TestForm + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(450, 656); + Controls.Add(entityTreeViewerControl1); + Name = "TestForm"; + Text = "TestForm"; + ResumeLayout(false); + } + + #endregion + + private EntityTreeViewerControl entityTreeViewerControl1; +} \ No newline at end of file diff --git a/Pf2eViewer/TestForm.cs b/Pf2eViewer/TestForm.cs new file mode 100644 index 0000000..c4ae87b --- /dev/null +++ b/Pf2eViewer/TestForm.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using Pf2eModel; +using Pf2eModel.Model.Base; + +namespace Pf2eViewer; + +public partial class TestForm : Form +{ + public TestForm() + { + InitializeComponent(); + + _ = new DarkModeCS(this) { ColorMode = DarkModeCS.DisplayMode.SystemDefault }; + + Pf2eDbContext context = new(); + int count = context.Entities.Count(); + int id = Random.Shared.Next(1, count + 1); + + Entity entity = context.Entities.FirstOrDefault(e => e.Id == id)!; + + EntityView view = new(context, entity.Type, id); + entityTreeViewerControl1.EntityView = view; + } +} diff --git a/Pf2eViewer/TestForm.resx b/Pf2eViewer/TestForm.resx new file mode 100644 index 0000000..8b2ff64 --- /dev/null +++ b/Pf2eViewer/TestForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file