change UI to UI.WinForms

This commit is contained in:
2025-06-16 11:50:17 +02:00
parent fa3a9da07e
commit 299867a910
116 changed files with 318 additions and 319 deletions

View File

@@ -0,0 +1,10 @@
namespace Pilz.UI.WinForms.Controls.ConfigurationManager;
public class ConfigurationEntry(string name, string title, ConfigurationValueListener Listener)
{
public event EventHandler? OnValueChanged;
public string Name { get; } = name;
public string Title { get; } = title;
public ConfigurationValueListener Listener { get; } = Listener;
}

View File

@@ -0,0 +1,20 @@
namespace Pilz.UI.WinForms.Controls.ConfigurationManager;
public class ConfigurationManager
{
private readonly List<ConfigurationPanel> panels = [];
public void Register(ConfigurationPanel panel)
{
panels.Add(panel);
}
public IEnumerable<Control> Build()
{
foreach (var panel in panels)
{
panel.Build(this);
yield return panel;
}
}
}

View File

@@ -0,0 +1,33 @@
namespace Pilz.UI.WinForms.Controls.ConfigurationManager;
public class ConfigurationPanel : TableLayoutPanel
{
private readonly List<ConfigurationEntry> entries = [];
public ConfigurationEntry CreateEntry(string name, string title)
{
return CreateEntry(name, title, null);
}
public ConfigurationEntry CreateEntry(string name, string title, Action? create)
{
throw new NotImplementedException();
//return CreateEntry(new(name, title, ));
}
public ConfigurationEntry CreateEntry(ConfigurationEntry entry)
{
entries.Add(entry);
return entry;
}
internal protected void Build(ConfigurationManager manager)
{
foreach (var entry in entries)
{
//var control = ;
//entry.Listener.Initialize();
// ...
}
}
}

View File

@@ -0,0 +1,15 @@
namespace Pilz.UI.WinForms.Controls.ConfigurationManager;
public abstract class ConfigurationValueListener : IDisposable
{
public event EventHandler? ValueChanged;
protected virtual void OnValueChanged(object sender, EventArgs e)
{
ValueChanged?.Invoke(sender, e);
}
internal protected abstract void Initialize(Control control);
public abstract void Dispose();
}

View File

@@ -0,0 +1,78 @@
using Pilz.UI.WinForms.Dialogs;
namespace Pilz.UI.Dialogs;
partial class DialogBase
{
public delegate void DialogLoadingEventHandler(DialogLoadingEventArgs e);
public delegate void DialogClosedEventHandler(DialogClosedEventArgs e);
public static event DialogLoadingEventHandler? DialogLoading;
public static event DialogClosedEventHandler? DialogClosed;
public static T Show<T>(string title, Icon icon, object? tag = null) where T : FlyoutBase
{
return Show<T>(null, title, icon, tag);
}
public static T ShowDialog<T>(string title, Icon icon, object? tag = null) where T : FlyoutBase
{
return ShowDialog<T>(null, title, icon, tag);
}
public static T Show<T>(IWin32Window? parent, string title, Icon icon, object? tag = null) where T : FlyoutBase
{
return Show(CreatePanelInstance<T>(tag), parent, title, icon);
}
public static T ShowDialog<T>(IWin32Window? parent, string title, Icon icon, object? tag = null) where T : FlyoutBase
{
return ShowDialog(CreatePanelInstance<T>(tag), parent, title, icon);
}
public static T Show<T>(T dialogPanel, string title, Icon icon) where T : FlyoutBase
{
return Show(dialogPanel, null, title, icon);
}
public static T ShowDialog<T>(T dialogPanel, string title, Icon icon) where T : FlyoutBase
{
return ShowDialog(dialogPanel, null, title, icon);
}
public static T Show<T>(T dialogPanel, IWin32Window? parent, string title, Icon icon) where T : FlyoutBase
{
CreateForm(dialogPanel, parent, title, icon).Show();
return dialogPanel;
}
public static T ShowDialog<T>(T dialogPanel, IWin32Window? parent, string title, Icon icon) where T : FlyoutBase
{
CreateForm(dialogPanel, parent, title, icon).ShowDialog();
return dialogPanel;
}
private static T CreatePanelInstance<T>(object? tag) where T : FlyoutBase
{
T dialogPanel = Activator.CreateInstance<T>();
dialogPanel.Tag = tag;
return dialogPanel;
}
private static DialogBase CreateForm<T>(T dialogPanel, IWin32Window? parent, string title, Icon icon) where T : FlyoutBase
{
dialogPanel.Dock = DockStyle.Fill;
var dialog = new DialogBase(dialogPanel)
{
Text = title,
Icon = icon,
StartPosition = parent == null ? FormStartPosition.CenterScreen : FormStartPosition.CenterParent,
ClientSize = dialogPanel.Size
};
dialog.Controls.Add(dialogPanel);
return dialog;
}
}

View File

@@ -0,0 +1,35 @@
using Pilz.UI.WinForms;
using Pilz.UI.WinForms.Dialogs;
namespace Pilz.UI.Dialogs;
public partial class DialogBase : Form
{
public FlyoutBase? DialogPanel { get; private set; }
private DialogBase()
{
Load += DialogBaseForm_Load;
FormClosed += DialogBaseForm_FormClosed;
}
public DialogBase(FlyoutBase? dialogPanel) : this()
{
DialogPanel = dialogPanel;
}
private void DialogBaseForm_Load(object? sender, EventArgs e)
{
if (DialogPanel is ILoadContent iLoadContent)
iLoadContent.LoadContent();
else if (DialogPanel is ILoadContentAsync iLoadContentAsync)
Task.Run(iLoadContentAsync.LoadContentAsync).Wait();
DialogLoading?.Invoke(new DialogLoadingEventArgs(this));
}
private void DialogBaseForm_FormClosed(object? sender, FormClosedEventArgs e)
{
DialogClosed?.Invoke(new DialogClosedEventArgs(this));
}
}

View File

@@ -0,0 +1,9 @@
using Pilz.UI.Dialogs;
namespace Pilz.UI.WinForms.Dialogs;
public class DialogClosedEventArgs(DialogBase dialog) : EventArgs
{
public DialogBase Parent { get; } = dialog;
public FlyoutBase? Content => Parent?.DialogPanel;
}

View File

@@ -0,0 +1,9 @@
using Pilz.UI.Dialogs;
namespace Pilz.UI.WinForms.Dialogs;
public class DialogLoadingEventArgs(DialogBase dialog) : EventArgs
{
public DialogBase Parent { get; } = dialog;
public FlyoutBase? Content => Parent?.DialogPanel;
}

View File

@@ -0,0 +1,223 @@
using Pilz.UI.Dialogs;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
namespace Pilz.UI.WinForms.Dialogs;
public partial class FlyoutBase : UserControl
{
private bool addedControlsToUi;
protected TableLayoutPanel tableLayoutPanel_TitlePanel;
protected Label label_Title;
protected TableLayoutPanel tableLayoutPanel_ActionPanel;
protected Button button_Cancel;
protected Button button_Accept;
[ReadOnly(true)]
public DialogResult Result { get; protected set; }
[DefaultValue(true)]
public bool RegisterDialogAccept { get; set; } = true;
[DefaultValue(true)]
public bool RegisterDialogCancel { get; set; } = true;
[DefaultValue(true)]
public bool ActionPanelVisible
{
get => tableLayoutPanel_ActionPanel.Visible;
set => tableLayoutPanel_ActionPanel.Visible = value;
}
[DefaultValue(true)]
public bool CancelButtonVisible
{
get => button_Cancel.Visible;
set => button_Cancel.Visible = value;
}
[DefaultValue(true)]
public bool CancelButtonEnable
{
get => button_Cancel.Enabled;
set => button_Cancel.Enabled = value;
}
[DefaultValue(true)]
public bool ConfirmButtonEnable
{
get => button_Accept.Enabled;
set => button_Accept.Enabled = value;
}
[Localizable(true)]
[DefaultValue("Okay")]
public string ConfirmButtonText
{
get => button_Accept.Text;
set => button_Accept.Text = value;
}
[Localizable(true)]
[DefaultValue("Cancel")]
public string CancelButtonText
{
get => button_Cancel.Text;
set => button_Cancel.Text = value;
}
[Localizable(true)]
[DefaultValue("")]
public string Title
{
get => label_Title.Text;
set
{
label_Title.Text = value;
SetShowTitlePanel();
}
}
public FlyoutBase()
{
InitializeComponent();
ParentChanged += FlyoutBase_ParentChanged;
}
private void InitializeComponent()
{
// button_Accept
button_Accept = new Button
{
Name = "button_Accept",
Size = new Size(75, 23),
TabIndex = int.MaxValue - 1,
Text = "Accept",
UseVisualStyleBackColor = true
};
button_Accept.Click += Button_Accept_Click;
// button_Cancel
button_Cancel = new Button
{
Name = "button_Cancel",
Size = new Size(75, 23),
TabIndex = int.MaxValue,
Text = "Cancel",
UseVisualStyleBackColor = true
};
button_Cancel.Click += Button_Cancel_Click;
// label_Title
label_Title = new Label
{
Name = "label_Title",
Dock = DockStyle.Fill,
TextAlign = ContentAlignment.MiddleLeft
};
// tableLayoutPanel_TitlePanel
tableLayoutPanel_TitlePanel = new TableLayoutPanel
{
Name = "tableLayoutPanel_TitlePanel",
Dock = DockStyle.Top,
Size = new Size(ClientSize.Width, 29),
Visible = false,
ColumnCount = 1,
RowCount = 1
};
tableLayoutPanel_TitlePanel.SuspendLayout();
tableLayoutPanel_TitlePanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
tableLayoutPanel_TitlePanel.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));
tableLayoutPanel_TitlePanel.Controls.Add(label_Title, 0, 0);
// tableLayoutPanel_ActionPanel
tableLayoutPanel_ActionPanel = new TableLayoutPanel
{
Name = "tableLayoutPanel_ActionPanel",
AutoSize = true,
AutoSizeMode = AutoSizeMode.GrowAndShrink,
Dock = DockStyle.Bottom,
Size = new Size(ClientSize.Width, 29),
ColumnCount = 3,
RowCount = 1
};
tableLayoutPanel_ActionPanel.SuspendLayout();
tableLayoutPanel_ActionPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
tableLayoutPanel_ActionPanel.ColumnStyles.Add(new ColumnStyle());
tableLayoutPanel_ActionPanel.ColumnStyles.Add(new ColumnStyle());
tableLayoutPanel_ActionPanel.RowStyles.Add(new RowStyle());
tableLayoutPanel_ActionPanel.Controls.Add(button_Cancel, 2, 0);
tableLayoutPanel_ActionPanel.Controls.Add(button_Accept, 1, 0);
// FlyoutBase
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
Size = new Size(300, 150);
tableLayoutPanel_TitlePanel.ResumeLayout(false);
tableLayoutPanel_ActionPanel.ResumeLayout(false);
}
protected override void OnLoad(EventArgs e)
{
if (!addedControlsToUi && !DesignMode)
{
SuspendLayout();
Controls.Add(tableLayoutPanel_ActionPanel);
tableLayoutPanel_ActionPanel.SendToBack();
Controls.Add(tableLayoutPanel_TitlePanel);
tableLayoutPanel_TitlePanel.SendToBack();
ResumeLayout(false);
PerformLayout();
addedControlsToUi = true;
}
base.OnLoad(e);
}
private void FlyoutBase_ParentChanged(object sender, EventArgs e)
{
var frm = FindForm();
if (frm != null)
{
if (RegisterDialogAccept)
frm.AcceptButton = button_Accept;
if (RegisterDialogCancel)
{
frm.CancelButton = button_Cancel;
button_Cancel.DialogResult = DialogResult.None;
}
}
}
protected void Close(DialogResult result)
{
Result = result;
if (FindForm() is DialogBase dialogForm)
dialogForm.Close();
}
protected virtual bool ValidateOK()
{
return true;
}
protected virtual void SetShowTitlePanel()
{
tableLayoutPanel_TitlePanel.Visible = !string.IsNullOrWhiteSpace(label_Title.Text);
}
protected virtual void Button_Accept_Click(object sender, EventArgs e)
{
if (ValidateOK())
Close(DialogResult.OK);
}
protected virtual void Button_Cancel_Click(object sender, EventArgs e)
{
Close(DialogResult.Cancel);
}
}

View File

@@ -0,0 +1,252 @@
using System.Drawing;
using System.Drawing.Drawing2D;
namespace Pilz.UI.WinForms;
public class DisplayHelp
{
public static void FillRectangle(Graphics g, Rectangle bounds, Color color1)
{
FillRectangle(g, bounds, color1, Color.Empty, 90);
}
public static void FillRectangle(Graphics g, Rectangle bounds, Color color1, Color color2)
{
FillRectangle(g, bounds, color1, color2, 90);
}
public static void FillRectangle(Graphics g, Rectangle r, Color color1, Color color2, int gradientAngle)
{
if (r.Width == 0 || r.Height == 0)
return;
if (color2.IsEmpty)
{
if (!color1.IsEmpty)
{
var sm = g.SmoothingMode;
g.SmoothingMode = SmoothingMode.None;
using var brush = new SolidBrush(color1);
g.FillRectangle(brush, r);
g.SmoothingMode = sm;
}
}
else
{
using var brush = CreateLinearGradientBrush(r, color1, color2, gradientAngle);
g.FillRectangle(brush, r);
}
}
public static void FillRectangle(Graphics g, Rectangle r, Color color1, Color color2, int gradientAngle, float[] factors, float[] positions)
{
if (r.Width == 0 || r.Height == 0)
return;
if (color2.IsEmpty)
{
if (!color1.IsEmpty)
{
var sm = g.SmoothingMode;
g.SmoothingMode = SmoothingMode.None;
using var brush = new SolidBrush(color1);
g.FillRectangle(brush, r);
g.SmoothingMode = sm;
}
}
else
{
using var brush = CreateLinearGradientBrush(r, color1, color2, gradientAngle);
var blend = new Blend(factors.Length);
blend.Factors = factors;
blend.Positions = positions;
brush.Blend = blend;
g.FillRectangle(brush, r);
}
}
public static void FillRoundedRectangle(Graphics g, Rectangle bounds, int cornerSize, Color color1, Color color2, int gradientAngle)
{
if (color2.IsEmpty)
{
if (!color1.IsEmpty)
{
using var brush = new SolidBrush(color1);
FillRoundedRectangle(g, brush, bounds, cornerSize);
}
}
else
{
using var brush = CreateLinearGradientBrush(bounds, color1, color2, gradientAngle);
FillRoundedRectangle(g, brush, bounds, cornerSize);
}
}
public static void FillRoundedRectangle(Graphics g, Rectangle bounds, int cornerSize, Color color1, Color color2)
{
FillRoundedRectangle(g, bounds, cornerSize, color1, color2, 90);
}
public static void FillRoundedRectangle(Graphics g, Rectangle bounds, int cornerSize, Color color1)
{
using var brush = new SolidBrush(color1);
FillRoundedRectangle(g, brush, bounds, cornerSize);
}
public static void FillRoundedRectangle(Graphics g, Brush brush, Rectangle bounds, int cornerSize)
{
if (cornerSize <= 0)
{
var sm = g.SmoothingMode;
g.SmoothingMode = SmoothingMode.None;
g.FillRectangle(brush, bounds);
g.SmoothingMode = sm;
}
else
{
bounds.Width -= 1;
bounds.Height -= 1;
using var path = GetRoundedRectanglePath(bounds, cornerSize);
g.FillPath(brush, path);
}
}
public static void DrawRectangle(Graphics g, Color color, int x, int y, int width, int height)
{
using var pen = new Pen(color, 1f);
DrawRectangle(g, pen, x, y, width, height);
}
public static void DrawRectangle(Graphics g, Color color, Rectangle r)
{
DrawRectangle(g, color, r.X, r.Y, r.Width, r.Height);
}
public static void DrawRectangle(Graphics g, Pen pen, int x, int y, int width, int height)
{
width -= 1;
height -= 1;
g.DrawRectangle(pen, x, y, width, height);
}
public static void DrawRoundedRectangle(Graphics g, Color color, Rectangle bounds, int cornerSize)
{
if (color.IsEmpty)
return;
using var pen = new Pen(color);
DrawRoundedRectangle(g, pen, bounds.X, bounds.Y, bounds.Width, bounds.Height, cornerSize);
}
public static void DrawRoundedRectangle(Graphics g, Pen pen, int x, int y, int width, int height, int cornerSize)
{
DrawRoundedRectangle(g, pen, null, x, y, width, height, cornerSize);
}
public static void DrawRoundedRectangle(Graphics g, Pen pen, Brush fill, int x, int y, int width, int height, int cornerSize)
{
width -= 1;
height -= 1;
var r = new Rectangle(x, y, width, height);
using var path = GetRoundedRectanglePath(r, cornerSize);
if (fill is not null)
g.FillPath(fill, path);
g.DrawPath(pen, path);
}
public static GraphicsPath GetRoundedRectanglePath(Rectangle r, int cornerSize)
{
var path = new GraphicsPath();
if (cornerSize == 0)
path.AddRectangle(r);
else
{
AddCornerArc(path, r, cornerSize, eCornerArc.TopLeft);
AddCornerArc(path, r, cornerSize, eCornerArc.TopRight);
AddCornerArc(path, r, cornerSize, eCornerArc.BottomRight);
AddCornerArc(path, r, cornerSize, eCornerArc.BottomLeft);
path.CloseAllFigures();
}
return path;
}
public static LinearGradientBrush CreateLinearGradientBrush(Rectangle r, Color color1, Color color2, float gradientAngle)
{
if (r.Width <= 0)
r.Width = 1;
if (r.Height <= 0)
r.Height = 1;
return new LinearGradientBrush(new Rectangle(r.X, r.Y - 1, r.Width, r.Height + 1), color1, color2, gradientAngle);
}
public static void AddCornerArc(GraphicsPath path, Rectangle bounds, int cornerDiameter, eCornerArc corner)
{
if (cornerDiameter > 0)
{
var a = GetCornerArc(bounds, cornerDiameter, corner);
path.AddArc(a.X, a.Y, a.Width, a.Height, a.StartAngle, a.SweepAngle);
}
else if (corner == eCornerArc.TopLeft)
path.AddLine(bounds.X, bounds.Y + 2, bounds.X, bounds.Y);
else if (corner == eCornerArc.BottomLeft)
path.AddLine(bounds.X + 2, bounds.Bottom, bounds.X, bounds.Bottom);
else if (corner == eCornerArc.TopRight)
path.AddLine(bounds.Right - 2, bounds.Y, bounds.Right, bounds.Y);
else if (corner == eCornerArc.BottomRight)
path.AddLine(bounds.Right, bounds.Bottom - 2, bounds.Right, bounds.Bottom);
}
internal static ArcData GetCornerArc(Rectangle bounds, int cornerDiameter, eCornerArc corner)
{
if (cornerDiameter == 0)
cornerDiameter = 1;
var diameter = cornerDiameter * 2;
return corner switch
{
eCornerArc.TopLeft => new ArcData(bounds.X, bounds.Y, diameter, diameter, 180f, 90f),
eCornerArc.TopRight => new ArcData(bounds.Right - diameter, bounds.Y, diameter, diameter, 270f, 90f),
eCornerArc.BottomLeft => new ArcData(bounds.X, bounds.Bottom - diameter, diameter, diameter, 90f, 90f),
_ => new ArcData(bounds.Right - diameter, bounds.Bottom - diameter, diameter, diameter, 0f, 90f),
};
}
public enum eCornerArc
{
TopLeft,
TopRight,
BottomLeft,
BottomRight
}
internal struct ArcData
{
public int X;
public int Y;
public int Width;
public int Height;
public float StartAngle;
public float SweepAngle;
public ArcData(int x, int y, int width, int height, float startAngle, float sweepAngle)
{
X = x;
Y = y;
Width = width;
Height = height;
StartAngle = startAngle;
SweepAngle = sweepAngle;
}
}
}

View File

@@ -0,0 +1,232 @@
Imports System.Drawing
Imports System.Drawing.Drawing2D
Public Class DisplayHelp
Public Shared Sub FillRectangle(ByVal g As Graphics, ByVal bounds As Rectangle, ByVal color1 As Color)
FillRectangle(g, bounds, color1, Color.Empty, 90)
End Sub
Public Shared Sub FillRectangle(ByVal g As Graphics, ByVal bounds As Rectangle, ByVal color1 As Color, ByVal color2 As Color)
FillRectangle(g, bounds, color1, color2, 90)
End Sub
Public Shared Sub FillRectangle(ByVal g As Graphics, ByVal r As Rectangle, ByVal color1 As Color, ByVal color2 As Color, ByVal gradientAngle As Integer)
If r.Width = 0 OrElse r.Height = 0 Then Return
If color2.IsEmpty Then
If Not color1.IsEmpty Then
Dim sm As SmoothingMode = g.SmoothingMode
g.SmoothingMode = SmoothingMode.None
Using brush As SolidBrush = New SolidBrush(color1)
g.FillRectangle(brush, r)
End Using
g.SmoothingMode = sm
End If
Else
Using brush As LinearGradientBrush = CreateLinearGradientBrush(r, color1, color2, gradientAngle)
g.FillRectangle(brush, r)
End Using
End If
End Sub
Public Shared Sub FillRectangle(ByVal g As Graphics, ByVal r As Rectangle, ByVal color1 As Color, ByVal color2 As Color, ByVal gradientAngle As Integer, ByVal factors As Single(), ByVal positions As Single())
If r.Width = 0 OrElse r.Height = 0 Then Return
If color2.IsEmpty Then
If Not color1.IsEmpty Then
Dim sm As SmoothingMode = g.SmoothingMode
g.SmoothingMode = SmoothingMode.None
Using brush As SolidBrush = New SolidBrush(color1)
g.FillRectangle(brush, r)
End Using
g.SmoothingMode = sm
End If
Else
Using brush As LinearGradientBrush = CreateLinearGradientBrush(r, color1, color2, gradientAngle)
Dim blend As Blend = New Blend(factors.Length)
blend.Factors = factors
blend.Positions = positions
brush.Blend = blend
g.FillRectangle(brush, r)
End Using
End If
End Sub
Public Shared Sub FillRoundedRectangle(ByVal g As Graphics, ByVal bounds As Rectangle, ByVal cornerSize As Integer, ByVal color1 As Color, ByVal color2 As Color, ByVal gradientAngle As Integer)
If color2.IsEmpty Then
If Not color1.IsEmpty Then
Using brush As SolidBrush = New SolidBrush(color1)
FillRoundedRectangle(g, brush, bounds, cornerSize)
End Using
End If
Else
Using brush As LinearGradientBrush = CreateLinearGradientBrush(bounds, color1, color2, gradientAngle)
FillRoundedRectangle(g, brush, bounds, cornerSize)
End Using
End If
End Sub
Public Shared Sub FillRoundedRectangle(ByVal g As Graphics, ByVal bounds As Rectangle, ByVal cornerSize As Integer, ByVal color1 As Color, ByVal color2 As Color)
FillRoundedRectangle(g, bounds, cornerSize, color1, color2, 90)
End Sub
Public Shared Sub FillRoundedRectangle(ByVal g As Graphics, ByVal bounds As Rectangle, ByVal cornerSize As Integer, ByVal color1 As Color)
Using brush As SolidBrush = New SolidBrush(color1)
FillRoundedRectangle(g, brush, bounds, cornerSize)
End Using
End Sub
Public Shared Sub FillRoundedRectangle(ByVal g As Graphics, ByVal brush As Brush, ByVal bounds As Rectangle, ByVal cornerSize As Integer)
If cornerSize <= 0 Then
Dim sm As SmoothingMode = g.SmoothingMode
g.SmoothingMode = SmoothingMode.None
g.FillRectangle(brush, bounds)
g.SmoothingMode = sm
Else
bounds.Width -= 1
bounds.Height -= 1
Using path As GraphicsPath = GetRoundedRectanglePath(bounds, cornerSize)
g.FillPath(brush, path)
End Using
End If
End Sub
Public Shared Sub DrawRectangle(ByVal g As System.Drawing.Graphics, ByVal color As Color, ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer)
Using pen As Pen = New Pen(color, 1)
DrawRectangle(g, pen, x, y, width, height)
End Using
End Sub
Public Shared Sub DrawRectangle(ByVal g As System.Drawing.Graphics, ByVal color As Color, ByVal r As System.Drawing.Rectangle)
DrawRectangle(g, color, r.X, r.Y, r.Width, r.Height)
End Sub
Public Shared Sub DrawRectangle(ByVal g As System.Drawing.Graphics, ByVal pen As System.Drawing.Pen, ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer)
width -= 1
height -= 1
g.DrawRectangle(pen, x, y, width, height)
End Sub
Public Shared Sub DrawRoundedRectangle(ByVal g As System.Drawing.Graphics, ByVal color As Color, ByVal bounds As Rectangle, ByVal cornerSize As Integer)
If Not color.IsEmpty Then
Using pen As Pen = New Pen(color)
DrawRoundedRectangle(g, pen, bounds.X, bounds.Y, bounds.Width, bounds.Height, cornerSize)
End Using
End If
End Sub
Public Shared Sub DrawRoundedRectangle(ByVal g As System.Drawing.Graphics, ByVal pen As System.Drawing.Pen, ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer, ByVal cornerSize As Integer)
DrawRoundedRectangle(g, pen, Nothing, x, y, width, height, cornerSize)
End Sub
Public Shared Sub DrawRoundedRectangle(ByVal g As System.Drawing.Graphics, ByVal pen As System.Drawing.Pen, ByVal fill As Brush, ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer, ByVal cornerSize As Integer)
width -= 1
height -= 1
Dim r As Rectangle = New Rectangle(x, y, width, height)
Using path As GraphicsPath = GetRoundedRectanglePath(r, cornerSize)
If fill IsNot Nothing Then g.FillPath(fill, path)
g.DrawPath(pen, path)
End Using
End Sub
Public Shared Function GetRoundedRectanglePath(ByVal r As Rectangle, ByVal cornerSize As Integer) As GraphicsPath
Dim path As GraphicsPath = New GraphicsPath()
If cornerSize = 0 Then
path.AddRectangle(r)
Else
AddCornerArc(path, r, cornerSize, eCornerArc.TopLeft)
AddCornerArc(path, r, cornerSize, eCornerArc.TopRight)
AddCornerArc(path, r, cornerSize, eCornerArc.BottomRight)
AddCornerArc(path, r, cornerSize, eCornerArc.BottomLeft)
path.CloseAllFigures()
End If
Return path
End Function
Public Shared Function CreateLinearGradientBrush(ByVal r As Rectangle, ByVal color1 As Color, ByVal color2 As Color, ByVal gradientAngle As Single) As LinearGradientBrush
If r.Width <= 0 Then r.Width = 1
If r.Height <= 0 Then r.Height = 1
Return New LinearGradientBrush(New Rectangle(r.X, r.Y - 1, r.Width, r.Height + 1), color1, color2, gradientAngle)
End Function
Public Shared Sub AddCornerArc(ByVal path As GraphicsPath, ByVal bounds As Rectangle, ByVal cornerDiameter As Integer, ByVal corner As eCornerArc)
If cornerDiameter > 0 Then
Dim a As ArcData = GetCornerArc(bounds, cornerDiameter, corner)
path.AddArc(a.X, a.Y, a.Width, a.Height, a.StartAngle, a.SweepAngle)
Else
If corner = eCornerArc.TopLeft Then
path.AddLine(bounds.X, bounds.Y + 2, bounds.X, bounds.Y)
ElseIf corner = eCornerArc.BottomLeft Then
path.AddLine(bounds.X + 2, bounds.Bottom, bounds.X, bounds.Bottom)
ElseIf corner = eCornerArc.TopRight Then
path.AddLine(bounds.Right - 2, bounds.Y, bounds.Right, bounds.Y)
ElseIf corner = eCornerArc.BottomRight Then
path.AddLine(bounds.Right, bounds.Bottom - 2, bounds.Right, bounds.Bottom)
End If
End If
End Sub
Friend Shared Function GetCornerArc(ByVal bounds As Rectangle, ByVal cornerDiameter As Integer, ByVal corner As eCornerArc) As ArcData
Dim a As ArcData
If cornerDiameter = 0 Then cornerDiameter = 1
Dim diameter As Integer = cornerDiameter * 2
Select Case corner
Case eCornerArc.TopLeft
a = New ArcData(bounds.X, bounds.Y, diameter, diameter, 180, 90)
Case eCornerArc.TopRight
a = New ArcData(bounds.Right - diameter, bounds.Y, diameter, diameter, 270, 90)
Case eCornerArc.BottomLeft
a = New ArcData(bounds.X, bounds.Bottom - diameter, diameter, diameter, 90, 90)
Case Else
a = New ArcData(bounds.Right - diameter, bounds.Bottom - diameter, diameter, diameter, 0, 90)
End Select
Return a
End Function
Public Enum eCornerArc
TopLeft
TopRight
BottomLeft
BottomRight
End Enum
Friend Structure ArcData
Public X As Integer
Public Y As Integer
Public Width As Integer
Public Height As Integer
Public StartAngle As Single
Public SweepAngle As Single
Public Sub New(ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer, ByVal startAngle As Single, ByVal sweepAngle As Single)
Me.X = x
Me.Y = y
Me.Width = width
Me.Height = height
Me.StartAngle = startAngle
Me.SweepAngle = sweepAngle
End Sub
End Structure
End Class

View File

@@ -0,0 +1,14 @@
Imports System.Drawing
Imports System.Runtime.CompilerServices
Public Module Extensions
<Extension>
Public Function ToIcon(image As Image)
If TypeOf image Is Bitmap Then
Return Icon.FromHandle(CType(image, Bitmap).GetHicon)
End If
Return Nothing
End Function
End Module

View File

@@ -0,0 +1,54 @@
namespace Pilz.UI.WinForms.Extensions;
public static class DialogResultExtensions
{
public static bool Is(this DialogResult @this, DialogResult result)
{
return @this == result;
}
public static bool IsNot(this DialogResult @this, DialogResult result)
{
return @this != result;
}
public static bool Is(this DialogResult @this, params DialogResult[] results)
{
return results.Any(result => @this == result);
}
public static bool IsNot(this DialogResult @this, params DialogResult[] results)
{
return results.All(result => @this != result);
}
public static bool IsOk(this DialogResult @this)
{
return @this == DialogResult.OK;
}
public static bool IsNotOk(this DialogResult @this)
{
return @this != DialogResult.OK;
}
public static bool IsYes(this DialogResult @this)
{
return @this == DialogResult.Yes;
}
public static bool IsNotYes(this DialogResult @this)
{
return @this != DialogResult.Yes;
}
public static bool IsCancel(this DialogResult @this)
{
return @this == DialogResult.Cancel;
}
public static bool IsNotCancel(this DialogResult @this)
{
return @this != DialogResult.Cancel;
}
}

View File

@@ -0,0 +1,12 @@
using Pilz.UI.WinForms.Dialogs;
using System.Diagnostics.CodeAnalysis;
namespace Pilz.UI.WinForms.Extensions;
public static class FlyoutBaseExtensions
{
public static bool IsValid(this FlyoutBase? @this)
{
return @this != null && @this.Result == DialogResult.OK;
}
}

View File

@@ -0,0 +1,11 @@
namespace Pilz.UI.WinForms.Extensions;
public static class ImageExtensions
{
public static Icon? ToIcon(this Image image)
{
if (image is Bitmap bmp)
return Icon.FromHandle(bmp.GetHicon());
return null;
}
}

View File

@@ -0,0 +1,19 @@
using System.Diagnostics;
using System.Windows.Forms;
namespace Pilz.UI.WinForms;
public static class HelpfulFunctions
{
public static void Sleep(int milliseconds)
{
var stopw = new Stopwatch();
stopw.Start();
while (stopw.ElapsedMilliseconds < milliseconds)
Application.DoEvents();
stopw.Stop();
}
}

View File

@@ -0,0 +1,17 @@
Imports System.Windows.Forms
Public Module HelpfulFunctions
Public Sub Sleep(milliseconds As Integer)
Dim stopw As New Stopwatch
stopw.Start()
Do While stopw.ElapsedMilliseconds < milliseconds
Application.DoEvents()
Loop
stopw.Stop()
End Sub
End Module

View File

@@ -0,0 +1,323 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace Pilz.UI.WinForms;
internal class HighlightPanel : Control
{
private Dictionary<Control, eHighlightColor> _Highlights = null;
private List<HighlightRegion> _HighlightRegions = new List<HighlightRegion>();
public HighlightPanel(Dictionary<Control, eHighlightColor> highlights)
{
_Highlights = highlights;
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.Opaque, true);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.Selectable, false);
}
protected override void OnPaint(PaintEventArgs e)
{
var g = e.Graphics;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
foreach (var highlightRegion in _HighlightRegions)
{
var colors = GetHighlightColors(highlightRegion.HighlightColor);
var r = highlightRegion.Bounds;
var back = highlightRegion.BackColor;
r.Inflate(1, 1);
DisplayHelp.FillRectangle(g, r, back);
r.Inflate(-1, -1);
DisplayHelp.FillRoundedRectangle(g, r, 2, colors[0]);
r.Inflate(-2, -2);
DisplayHelp.DrawRectangle(g, colors[2], r);
r.Inflate(1, 1);
DisplayHelp.DrawRoundedRectangle(g, colors[1], r, 2);
}
base.OnPaint(e);
}
private Color[] GetHighlightColors(eHighlightColor color)
{
var colors = new Color[3];
if (color == eHighlightColor.Blue)
{
colors[0] = GetColor(172, 0x6A9CD4);
colors[1] = GetColor(0x6A9CD4);
colors[2] = GetColor(0x5D7EA4);
}
else if (color == eHighlightColor.Orange)
{
colors[0] = GetColor(172, 0xFF9C00);
colors[1] = GetColor(0xFF9C00);
colors[2] = GetColor(0xCC6600);
}
else if (color == eHighlightColor.Green)
{
colors[0] = GetColor(172, 0x71B171);
colors[1] = GetColor(0x71B171);
colors[2] = GetColor(0x339933);
}
else if (color == eHighlightColor.Custom)
{
if (_CustomHighlightColors is null || _CustomHighlightColors.Length < 3)
{
colors[0] = Color.Red;
colors[1] = Color.Red;
colors[2] = Color.Red;
}
else
{
colors[0] = _CustomHighlightColors[0];
colors[1] = _CustomHighlightColors[1];
colors[2] = _CustomHighlightColors[2];
}
}
else
{
colors[0] = GetColor(172, 0xC63030);
colors[1] = GetColor(0xC63030);
colors[2] = GetColor(0x990000);
}
return colors;
}
protected override void OnVisibleChanged(EventArgs e)
{
if (Visible && !_UpdatingRegion)
UpdateRegion();
base.OnVisibleChanged(e);
}
protected override void OnHandleCreated(EventArgs e)
{
if (!_RegionInitialized)
UpdateRegion();
base.OnHandleCreated(e);
}
private bool _RegionInitialized = false;
private bool _UpdatingRegion = false;
internal void UpdateRegion()
{
if (_UpdatingRegion || !IsHandleCreated)
return;
try
{
_UpdatingRegion = true;
Region = null;
_HighlightRegions.Clear();
if (_Highlights is null)
return;
if (_Highlights.Count == 0 && _FocusHighlightControl is null)
{
Visible = false;
return;
}
var processFocusControl = true;
Region region = null;
foreach (var item in _Highlights)
{
if (item.Value == eHighlightColor.None || !GetIsVisible(item.Key))
continue;
if (ReferenceEquals(item.Key, _FocusHighlightControl))
processFocusControl = false;
var r = GetControlRect(item.Key);
if (r.IsEmpty)
continue;
r.Inflate(2, 2);
_HighlightRegions.Add(new HighlightRegion(r, GetBackColor(item.Key.Parent), item.Value));
if (region is null)
region = new Region(r);
else
{
region.Union(r);
}
r.Inflate(-3, -3);
region.Exclude(r);
}
if (processFocusControl && _FocusHighlightControl is not null && _FocusHighlightControl.Visible)
{
var r = GetControlRect(_FocusHighlightControl);
if (!r.IsEmpty)
{
r.Inflate(2, 2);
_HighlightRegions.Add(new HighlightRegion(r, GetBackColor(_FocusHighlightControl.Parent), _FocusHighlightColor));
if (region is null)
region = new Region(r);
else
{
region.Union(r);
}
r.Inflate(-3, -3);
region.Exclude(r);
}
}
Region = region;
if (region is null)
Visible = false;
else if (!Visible)
{
Visible = true;
BringToFront();
}
Invalidate();
}
finally
{
_UpdatingRegion = false;
_RegionInitialized = true;
}
}
private static Color GetColor(int rgb)
{
if (rgb == -1)
return Color.Empty;
else
{
return Color.FromArgb((rgb & 0xFF0000) >> 16, (rgb & 0xFF00) >> 8, rgb & 0xFF);
}
}
private static Color GetColor(int alpha, int rgb)
{
if (rgb == -1)
return Color.Empty;
else
{
return Color.FromArgb(alpha, (rgb & 0xFF0000) >> 16, (rgb & 0xFF00) >> 8, rgb & 0xFF);
}
}
private bool GetIsVisible(Control control)
{
if (!control.Visible)
return false;
if (control.Parent is null || !control.IsHandleCreated)
return control.Visible;
var rect = new Win32.Native.RECT();
Win32.Native.User32.GetWindowRect(control.Handle, ref rect);
var pp = control.Parent.PointToClient(new Point(rect.Left + 3, rect.Top + 3));
var handle = Win32.Native.User32.ChildWindowFromPointEx(control.Parent.Handle, new Win32.Native.POINT(pp.X, pp.Y), (uint)Win32.Native.WindowFromPointFlags.CWP_SKIPINVISIBLE);
if (handle == nint.Zero)
return control.Visible;
var c = FromHandle(handle);
if (c is not null && !ReferenceEquals(c, control) && !ReferenceEquals(c, this) && !ReferenceEquals(c, control.Parent))
return false;
return control.Visible;
}
private Color GetBackColor(Control control)
{
var backColor = control.BackColor;
if (backColor.IsEmpty || backColor == Color.Transparent)
backColor = SystemColors.Control;
else if (backColor.A < 255)
{
backColor = Color.FromArgb(255, backColor);
}
return backColor;
}
protected override void OnResize(EventArgs e)
{
UpdateRegion();
base.OnResize(e);
}
private Rectangle GetControlRect(Control c)
{
if (!c.IsHandleCreated)
return Rectangle.Empty;
var rect = default(Win32.Native.RECT);
Win32.Native.User32.GetWindowRect(c.Handle, ref rect);
var p = PointToClient(rect.Location);
return new Rectangle(p, rect.Size);
}
private struct HighlightRegion
{
public Rectangle Bounds;
public Color BackColor;
public eHighlightColor HighlightColor;
public HighlightRegion(Rectangle bounds, Color backColor, eHighlightColor highlightColor)
{
Bounds = bounds;
BackColor = backColor;
HighlightColor = highlightColor;
}
}
private Control _FocusHighlightControl;
public Control FocusHighlightControl
{
get
{
return _FocusHighlightControl;
}
set
{
_FocusHighlightControl = value;
}
}
private eHighlightColor _FocusHighlightColor = eHighlightColor.Blue;
public eHighlightColor FocusHighlightColor
{
get
{
return _FocusHighlightColor;
}
set
{
_FocusHighlightColor = value;
}
}
private Color[] _CustomHighlightColors = null;
public Color[] CustomHighlightColors
{
get
{
return _CustomHighlightColors;
}
set
{
_CustomHighlightColors = value;
}
}
}

View File

@@ -0,0 +1,261 @@
Imports System.Drawing
Imports System.Windows.Forms
Imports Pilz.Win32
Friend Class HighlightPanel
Inherits Control
Private _Highlights As Dictionary(Of Control, eHighlightColor) = Nothing
Private _HighlightRegions As List(Of HighlightRegion) = New List(Of HighlightRegion)()
Public Sub New(ByVal highlights As Dictionary(Of Control, eHighlightColor))
_Highlights = highlights
Me.SetStyle(ControlStyles.UserPaint, True)
Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
Me.SetStyle(ControlStyles.Opaque, True)
Me.SetStyle(ControlStyles.ResizeRedraw, True)
Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
Me.SetStyle(ControlStyles.Selectable, False)
End Sub
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
Dim g As Graphics = e.Graphics
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias
For Each highlightRegion As HighlightRegion In _HighlightRegions
Dim colors As Color() = GetHighlightColors(highlightRegion.HighlightColor)
Dim r As Rectangle = highlightRegion.Bounds
Dim back As Color = highlightRegion.BackColor
r.Inflate(1, 1)
DisplayHelp.FillRectangle(g, r, back)
r.Inflate(-1, -1)
DisplayHelp.FillRoundedRectangle(g, r, 2, colors(0))
r.Inflate(-2, -2)
DisplayHelp.DrawRectangle(g, colors(2), r)
r.Inflate(1, 1)
DisplayHelp.DrawRoundedRectangle(g, colors(1), r, 2)
Next
MyBase.OnPaint(e)
End Sub
Private Function GetHighlightColors(ByVal color As eHighlightColor) As Color()
Dim colors As Color() = New Color(2) {}
If color = eHighlightColor.Blue Then
colors(0) = GetColor(172, &H6A9CD4)
colors(1) = GetColor(&H6A9CD4)
colors(2) = GetColor(&H5D7EA4)
ElseIf color = eHighlightColor.Orange Then
colors(0) = GetColor(172, &HFF9C00)
colors(1) = GetColor(&HFF9C00)
colors(2) = GetColor(&HCC6600)
ElseIf color = eHighlightColor.Green Then
colors(0) = GetColor(172, &H71B171)
colors(1) = GetColor(&H71B171)
colors(2) = GetColor(&H339933)
ElseIf color = eHighlightColor.Custom Then
If _CustomHighlightColors Is Nothing OrElse _CustomHighlightColors.Length < 3 Then
colors(0) = System.Drawing.Color.Red
colors(1) = System.Drawing.Color.Red
colors(2) = System.Drawing.Color.Red
Else
colors(0) = _CustomHighlightColors(0)
colors(1) = _CustomHighlightColors(1)
colors(2) = _CustomHighlightColors(2)
End If
Else
colors(0) = GetColor(172, &HC63030)
colors(1) = GetColor(&HC63030)
colors(2) = GetColor(&H990000)
End If
Return colors
End Function
Protected Overrides Sub OnVisibleChanged(ByVal e As EventArgs)
If Me.Visible AndAlso Not _UpdatingRegion Then UpdateRegion()
MyBase.OnVisibleChanged(e)
End Sub
Protected Overrides Sub OnHandleCreated(ByVal e As EventArgs)
If Not _RegionInitialized Then UpdateRegion()
MyBase.OnHandleCreated(e)
End Sub
Private _RegionInitialized As Boolean = False
Private _UpdatingRegion As Boolean = False
Friend Sub UpdateRegion()
If _UpdatingRegion OrElse Not Me.IsHandleCreated Then Return
Try
_UpdatingRegion = True
Me.Region = Nothing
_HighlightRegions.Clear()
If _Highlights Is Nothing Then Return
If _Highlights.Count = 0 AndAlso _FocusHighlightControl Is Nothing Then
Me.Visible = False
Return
End If
Dim processFocusControl As Boolean = True
Dim region As Region = Nothing
For Each item As KeyValuePair(Of Control, eHighlightColor) In _Highlights
If item.Value = eHighlightColor.None OrElse Not GetIsVisible(item.Key) Then Continue For
If item.Key Is _FocusHighlightControl Then processFocusControl = False
Dim r As Rectangle = GetControlRect(item.Key)
If r.IsEmpty Then Continue For
r.Inflate(2, 2)
_HighlightRegions.Add(New HighlightRegion(r, GetBackColor(item.Key.Parent), item.Value))
If region Is Nothing Then
region = New Region(r)
Else
region.Union(r)
End If
r.Inflate(-3, -3)
region.Exclude(r)
Next
If processFocusControl AndAlso _FocusHighlightControl IsNot Nothing AndAlso _FocusHighlightControl.Visible Then
Dim r As Rectangle = GetControlRect(_FocusHighlightControl)
If Not r.IsEmpty Then
r.Inflate(2, 2)
_HighlightRegions.Add(New HighlightRegion(r, GetBackColor(_FocusHighlightControl.Parent), _FocusHighlightColor))
If region Is Nothing Then
region = New Region(r)
Else
region.Union(r)
End If
r.Inflate(-3, -3)
region.Exclude(r)
End If
End If
Me.Region = region
If region Is Nothing Then
Me.Visible = False
ElseIf Not Me.Visible Then
Me.Visible = True
Me.BringToFront()
End If
Me.Invalidate()
Finally
_UpdatingRegion = False
_RegionInitialized = True
End Try
End Sub
Private Shared Function GetColor(rgb As Integer) As Color
If rgb = -1 Then
Return Color.Empty
Else
Return Color.FromArgb((rgb And &HFF0000) >> 16, (rgb And &HFF00) >> 8, rgb And &HFF)
End If
End Function
Private Shared Function GetColor(alpha As Integer, rgb As Integer) As Color
If rgb = -1 Then
Return Color.Empty
Else
Return Color.FromArgb(alpha, (rgb And &HFF0000) >> 16, (rgb And &HFF00) >> 8, rgb And &HFF)
End If
End Function
Private Function GetIsVisible(ByVal control As Control) As Boolean
If Not control.Visible Then Return False
If control.Parent Is Nothing OrElse Not control.IsHandleCreated Then Return control.Visible
Dim rect As New Native.RECT
Native.User32.GetWindowRect(control.Handle, rect)
Dim pp As Point = control.Parent.PointToClient(New Point(rect.Left + 3, rect.Top + 3))
Dim handle As IntPtr = Native.User32.ChildWindowFromPointEx(control.Parent.Handle, New Native.POINT(pp.X, pp.Y), CUInt(Native.WindowFromPointFlags.CWP_SKIPINVISIBLE))
If handle = IntPtr.Zero Then Return control.Visible
Dim c As Control = Control.FromHandle(handle)
If c IsNot Nothing AndAlso c IsNot control AndAlso c IsNot Me AndAlso c IsNot control.Parent Then
Return False
End If
Return control.Visible
End Function
Private Function GetBackColor(ByVal control As Control) As Color
Dim backColor As Color = control.BackColor
If backColor.IsEmpty OrElse backColor = Color.Transparent Then
backColor = SystemColors.Control
ElseIf backColor.A < 255 Then
backColor = Color.FromArgb(255, backColor)
End If
Return backColor
End Function
Protected Overrides Sub OnResize(ByVal e As EventArgs)
UpdateRegion()
MyBase.OnResize(e)
End Sub
Private Function GetControlRect(ByVal c As Control) As Rectangle
If Not c.IsHandleCreated Then Return Rectangle.Empty
Dim rect As Native.RECT
Native.User32.GetWindowRect(c.Handle, rect)
Dim p As Point = Me.PointToClient(rect.Location)
Return New Rectangle(p, rect.Size)
End Function
Private Structure HighlightRegion
Public Bounds As Rectangle
Public BackColor As Color
Public HighlightColor As eHighlightColor
Public Sub New(ByVal bounds As Rectangle, ByVal backColor As Color, ByVal highlightColor As eHighlightColor)
Me.Bounds = bounds
Me.BackColor = backColor
Me.HighlightColor = highlightColor
End Sub
End Structure
Private _FocusHighlightControl As Control
Public Property FocusHighlightControl As Control
Get
Return _FocusHighlightControl
End Get
Set(ByVal value As Control)
_FocusHighlightControl = value
End Set
End Property
Private _FocusHighlightColor As eHighlightColor = eHighlightColor.Blue
Public Property FocusHighlightColor As eHighlightColor
Get
Return _FocusHighlightColor
End Get
Set(ByVal value As eHighlightColor)
_FocusHighlightColor = value
End Set
End Property
Private _CustomHighlightColors As Color() = Nothing
Public Property CustomHighlightColors As Color()
Get
Return _CustomHighlightColors
End Get
Set(ByVal value As Color())
_CustomHighlightColors = value
End Set
End Property
End Class

View File

@@ -0,0 +1,509 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
// Nicht gemergte Änderung aus Projekt "Pilz.UI (net8.0-windows)"
// Vor:
// Imports System.Windows.Forms
// Imports System.Drawing
// Nach:
// Imports System.Drawing
// Imports System.Windows.Forms
using System.Windows.Forms;
namespace Pilz.UI.WinForms;
public class Highlighter : Component
{
private Dictionary<Control, eHighlightColor> _Highlights = new Dictionary<Control, eHighlightColor>();
private Dictionary<Control, bool> _HighlightOnFocus = new Dictionary<Control, bool>();
protected override void Dispose(bool disposing)
{
if (_ContainerControl is not null)
{
_ContainerControl.SizeChanged -= ContainerControlSizeChanged;
_ContainerControl.HandleCreated -= ContainerControlHandleCreated;
}
if (_HighlightPanel is not null && _HighlightPanel.Parent is null && !_HighlightPanel.IsDisposed)
{
_HighlightPanel.Dispose();
_HighlightPanel = null;
}
else
{
_HighlightPanel = null;
}
base.Dispose(disposing);
}
[DefaultValue(false)]
[Localizable(true)]
[Description("Indicates whether control is highlighted when it receives input focus.")]
public bool GetHighlightOnFocus(Control c)
{
if (_HighlightOnFocus.ContainsKey(c))
return _HighlightOnFocus[c];
return false;
}
public void SetHighlightOnFocus(Control c, bool highlight)
{
if (c is null)
throw new NullReferenceException();
if (_HighlightOnFocus.ContainsKey(c))
{
if (!highlight)
RemoveHighlightOnFocus(_HighlightOnFocus, c);
return;
}
if (highlight)
AddHighlightOnFocus(_HighlightOnFocus, c);
}
private void AddHighlightOnFocus(Dictionary<Control, bool> highlightOnFocus, Control c)
{
c.Enter += ControlHighlightEnter;
c.Leave += ControlHighlightLeave;
c.VisibleChanged += ControlHighlightVisibleChanged;
highlightOnFocus.Add(c, true);
}
private void ControlHighlightVisibleChanged(object sender, EventArgs e)
{
if (_HighlightPanel is not null && sender.Equals(_HighlightPanel.FocusHighlightControl))
UpdateHighlighterRegion();
}
private void ControlHighlightLeave(object sender, EventArgs e)
{
if (_HighlightPanel is not null)
_HighlightPanel.FocusHighlightControl = null;
UpdateHighlighterRegion();
}
private void ControlHighlightEnter(object sender, EventArgs e)
{
if (_HighlightPanel is not null)
{
if (!_HighlightPanel.Visible)
_HighlightPanel.Visible = true;
_HighlightPanel.BringToFront();
_HighlightPanel.FocusHighlightControl = (Control)sender;
}
UpdateHighlighterRegion();
}
private void RemoveHighlightOnFocus(Dictionary<Control, bool> highlightOnFocus, Control c)
{
c.Enter -= ControlHighlightEnter;
c.Leave -= ControlHighlightLeave;
c.VisibleChanged -= ControlHighlightVisibleChanged;
highlightOnFocus.Remove(c);
}
[DefaultValue(eHighlightColor.None)]
[Localizable(true)]
[Description("Indicates the highlight color that is applied to the control.")]
public eHighlightColor GetHighlightColor(Control c)
{
if (_Highlights.ContainsKey(c))
return _Highlights[c];
return eHighlightColor.None;
}
public void SetHighlightColor(Control c, eHighlightColor highlightColor)
{
if (_Highlights.ContainsKey(c))
{
if (highlightColor == eHighlightColor.None)
RemoveHighlight(_Highlights, c);
else
{
var color = _Highlights[c];
RemoveHighlight(_Highlights, c);
AddHighlight(_Highlights, c, highlightColor);
}
}
else if (highlightColor != eHighlightColor.None)
{
AddHighlight(_Highlights, c, highlightColor);
}
}
private Dictionary<TabControl, int> _TabControl2 = new Dictionary<TabControl, int>();
private Dictionary<Panel, int> _ParentPanel = new Dictionary<Panel, int>();
private void AddHighlight(Dictionary<Control, eHighlightColor> highlights, Control c, eHighlightColor highlightColor)
{
highlights.Add(c, highlightColor);
c.LocationChanged += new EventHandler(ControlLocationChanged);
c.SizeChanged += new EventHandler(ControlSizeChanged);
c.VisibleChanged += new EventHandler(ControlVisibleChanged);
if (_HighlightPanel is not null)
{
if (!_HighlightPanel.Visible)
_HighlightPanel.Visible = true;
_HighlightPanel.BringToFront();
}
if (c.Parent is null)
c.ParentChanged += ControlParentChanged;
else
{
AddTabControlHandlers(c);
}
UpdateHighlighterRegion();
}
private void ControlParentChanged(object sender, EventArgs e)
{
var c = (Control)sender;
c.ParentChanged -= ControlParentChanged;
AddTabControlHandlers(c);
}
private void AddTabControlHandlers(Control c)
{
var tab2 = GetParentControl(c, typeof(TabControl)) as TabControl;
if (tab2 is not null)
{
if (_TabControl2.ContainsKey(tab2))
_TabControl2[tab2] = _TabControl2[tab2] + 1;
else
{
_TabControl2.Add(tab2, 1);
tab2.SelectedIndexChanged += WinFormsTabSelectedIndexChanged;
}
}
else
{
var parentPanel = GetParentControl(c, typeof(Panel)) as Panel;
if (parentPanel is not null)
{
if (_ParentPanel.ContainsKey(parentPanel))
_ParentPanel[parentPanel] = _ParentPanel[parentPanel] + 1;
else
{
_ParentPanel.Add(parentPanel, 1);
parentPanel.Resize += ParentPanelResized;
parentPanel.LocationChanged += ParentPanelLocationChanged;
}
}
}
}
private void ParentPanelLocationChanged(object sender, EventArgs e)
{
UpdateHighlights();
}
private void ParentPanelResized(object sender, EventArgs e)
{
UpdateHighlights();
}
private void WinFormsTabSelectedIndexChanged(object sender, EventArgs e)
{
UpdateHighlighterRegion();
}
private Control GetParentControl(Control c, Type parentType)
{
var parent = c.Parent;
while (parent is not null)
{
if (parentType.IsAssignableFrom(parent.GetType()))
return parent;
parent = parent.Parent;
}
return null;
}
private void ControlVisibleChanged(object sender, EventArgs e)
{
UpdateHighlighterRegion();
}
private void ControlSizeChanged(object sender, EventArgs e)
{
UpdateHighlighterRegion();
}
private void ControlLocationChanged(object sender, EventArgs e)
{
UpdateHighlighterRegion();
}
private void UpdateHighlighterRegion()
{
if (_HighlightPanel is not null)
_HighlightPanel.UpdateRegion();
}
public void UpdateHighlights()
{
UpdateHighlighterRegion();
}
private void RemoveHighlight(Dictionary<Control, eHighlightColor> highlights, Control c)
{
highlights.Remove(c);
c.LocationChanged -= new EventHandler(ControlLocationChanged);
c.SizeChanged -= new EventHandler(ControlSizeChanged);
c.VisibleChanged -= new EventHandler(ControlVisibleChanged);
var tab2 = GetParentControl(c, typeof(TabControl)) as TabControl;
if (tab2 is not null)
{
if (_TabControl2.ContainsKey(tab2))
{
if (_TabControl2[tab2] == 1)
{
_TabControl2.Remove(tab2);
tab2.SelectedIndexChanged -= WinFormsTabSelectedIndexChanged;
}
else
{
_TabControl2[tab2] = _TabControl2[tab2] - 1;
}
}
}
else
{
var parentPanel = GetParentControl(c, typeof(Panel)) as Panel;
if (parentPanel is not null)
{
if (_ParentPanel.ContainsKey(parentPanel))
{
if (_ParentPanel[parentPanel] == 1)
{
_ParentPanel.Remove(parentPanel);
parentPanel.LocationChanged -= ParentPanelLocationChanged;
parentPanel.SizeChanged -= ParentPanelResized;
}
else
{
_ParentPanel[parentPanel] = _ParentPanel[parentPanel] - 1;
}
}
}
}
UpdateHighlighterRegion();
}
internal Dictionary<Control, eHighlightColor> Highlights
{
get
{
return _Highlights;
}
}
private eHighlightColor _FocusHighlightColor = eHighlightColor.Blue;
[DefaultValue(eHighlightColor.Blue)]
[Category("Appearance")]
[Description("Indicates the highlight focus color.")]
[Localizable(true)]
public eHighlightColor FocusHighlightColor
{
get
{
return _FocusHighlightColor;
}
set
{
_FocusHighlightColor = value;
if (_HighlightPanel is not null)
{
_HighlightPanel.FocusHighlightColor = value;
UpdateHighlighterRegion();
}
}
}
private HighlightPanel _HighlightPanel = null;
private Control _ContainerControl = null;
[Description("Indicates container control highlighter is bound to. Should be set to parent form.")]
[Category("Behavior")]
public Control ContainerControl
{
get
{
return _ContainerControl;
}
set
{
if (DesignMode)
{
_ContainerControl = value;
return;
}
if (!ReferenceEquals(_ContainerControl, value))
{
if (_ContainerControl is not null)
{
_ContainerControl.SizeChanged -= ContainerControlSizeChanged;
_ContainerControl.HandleCreated -= ContainerControlHandleCreated;
if (_HighlightPanel is not null && ReferenceEquals(_HighlightPanel.Parent, _ContainerControl))
_ContainerControl.Controls.Remove(_HighlightPanel);
}
_ContainerControl = value;
if (_ContainerControl is not null)
{
if (_HighlightPanel is null)
{
_HighlightPanel = new HighlightPanel(_Highlights);
_HighlightPanel.FocusHighlightColor = _FocusHighlightColor;
_HighlightPanel.Margin = new Padding(0);
_HighlightPanel.Padding = new Padding(0);
_HighlightPanel.CustomHighlightColors = _CustomHighlightColors;
_HighlightPanel.Visible = false;
}
_ContainerControl.SizeChanged += ContainerControlSizeChanged;
_ContainerControl.HandleCreated += ContainerControlHandleCreated;
_ContainerControl.Controls.Add(_HighlightPanel);
UpdateHighlightPanelBounds();
}
}
}
}
private void ContainerControlHandleCreated(object sender, EventArgs e)
{
if (_Highlights.Count > 0 && _HighlightPanel is not null && !_HighlightPanel.Visible)
_HighlightPanel.Visible = true;
}
private void UpdateHighlightPanelBounds()
{
var bounds = new Rectangle(0, 0, _ContainerControl.ClientRectangle.Width, _ContainerControl.ClientRectangle.Height);
if (_HighlightPanel is not null)
{
if (_HighlightPanel.Parent is Form)
{
var form = _HighlightPanel.Parent as Form;
if (form.AutoSize)
{
bounds.X += form.Padding.Left;
bounds.Y += form.Padding.Top;
bounds.Width -= form.Padding.Horizontal;
bounds.Height -= form.Padding.Vertical;
}
}
if (_HighlightPanel.Bounds.Equals(bounds))
_HighlightPanel.UpdateRegion();
else
{
_HighlightPanel.Bounds = bounds;
}
_HighlightPanel.BringToFront();
}
}
private System.Windows.Forms.Timer _DelayTimer = null;
private void ContainerControlSizeChanged(object sender, EventArgs e)
{
var form = sender as Form;
if (form is not null)
{
if (_DelayTimer is null)
{
_DelayTimer = new();
_DelayTimer.Interval = 100;
_DelayTimer.Tick += new EventHandler(DelayTimerTick);
_DelayTimer.Start();
}
return;
}
UpdateHighlightPanelBounds();
}
private void DelayTimerTick(object sender, EventArgs e)
{
var timer = _DelayTimer;
_DelayTimer = null;
timer.Tick -= new EventHandler(DelayTimerTick);
timer.Stop();
timer.Dispose();
UpdateHighlightPanelBounds();
}
private Color[] _CustomHighlightColors = null;
[Category("Appearance")]
[Description("Array of colors used to render custom highlight color. Control expects 3 colors in array to be specified which define the highlight border.")]
public Color[] CustomHighlightColors
{
get
{
return _CustomHighlightColors;
}
set
{
_CustomHighlightColors = value;
if (_HighlightPanel is not null)
{
_HighlightPanel.CustomHighlightColors = _CustomHighlightColors;
_HighlightPanel.Invalidate();
}
}
}
}
public enum eHighlightColor
{
None,
Red,
Blue,
Green,
Orange,
Custom
}

View File

@@ -0,0 +1,406 @@
Imports System.ComponentModel
Imports System.Drawing
' Nicht gemergte Änderung aus Projekt "Pilz.UI (net8.0-windows)"
' Vor:
' Imports System.Windows.Forms
' Imports System.Drawing
' Nach:
' Imports System.Drawing
' Imports System.Windows.Forms
Imports System.Windows.Forms
Public Class Highlighter
Inherits Component
Private _Highlights As Dictionary(Of Control, eHighlightColor) = New Dictionary(Of Control, eHighlightColor)()
Private _HighlightOnFocus As Dictionary(Of Control, Boolean) = New Dictionary(Of Control, Boolean)()
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If _ContainerControl IsNot Nothing Then
RemoveHandler _ContainerControl.SizeChanged, AddressOf ContainerControlSizeChanged
RemoveHandler _ContainerControl.HandleCreated, AddressOf ContainerControlHandleCreated
End If
If _HighlightPanel IsNot Nothing AndAlso _HighlightPanel.Parent Is Nothing AndAlso Not _HighlightPanel.IsDisposed Then
_HighlightPanel.Dispose()
_HighlightPanel = Nothing
Else
_HighlightPanel = Nothing
End If
MyBase.Dispose(disposing)
End Sub
<DefaultValue(False), Localizable(True), Description("Indicates whether control is highlighted when it receives input focus.")>
Public Function GetHighlightOnFocus(ByVal c As Control) As Boolean
If _HighlightOnFocus.ContainsKey(c) Then
Return _HighlightOnFocus(c)
End If
Return False
End Function
Public Sub SetHighlightOnFocus(ByVal c As Control, ByVal highlight As Boolean)
If c Is Nothing Then Throw New NullReferenceException()
If _HighlightOnFocus.ContainsKey(c) Then
If Not highlight Then
RemoveHighlightOnFocus(_HighlightOnFocus, c)
End If
Return
End If
If highlight Then AddHighlightOnFocus(_HighlightOnFocus, c)
End Sub
Private Sub AddHighlightOnFocus(ByVal highlightOnFocus As Dictionary(Of Control, Boolean), ByVal c As Control)
AddHandler c.Enter, AddressOf ControlHighlightEnter
AddHandler c.Leave, AddressOf ControlHighlightLeave
AddHandler c.VisibleChanged, AddressOf ControlHighlightVisibleChanged
highlightOnFocus.Add(c, True)
End Sub
Private Sub ControlHighlightVisibleChanged(ByVal sender As Object, ByVal e As EventArgs)
If _HighlightPanel IsNot Nothing AndAlso _HighlightPanel.FocusHighlightControl = sender Then UpdateHighlighterRegion()
End Sub
Private Sub ControlHighlightLeave(ByVal sender As Object, ByVal e As EventArgs)
If _HighlightPanel IsNot Nothing Then _HighlightPanel.FocusHighlightControl = Nothing
UpdateHighlighterRegion()
End Sub
Private Sub ControlHighlightEnter(ByVal sender As Object, ByVal e As EventArgs)
If _HighlightPanel IsNot Nothing Then
If Not _HighlightPanel.Visible Then _HighlightPanel.Visible = True
_HighlightPanel.BringToFront()
_HighlightPanel.FocusHighlightControl = CType(sender, Control)
End If
UpdateHighlighterRegion()
End Sub
Private Sub RemoveHighlightOnFocus(ByVal highlightOnFocus As Dictionary(Of Control, Boolean), ByVal c As Control)
RemoveHandler c.Enter, AddressOf ControlHighlightEnter
RemoveHandler c.Leave, AddressOf ControlHighlightLeave
RemoveHandler c.VisibleChanged, AddressOf ControlHighlightVisibleChanged
highlightOnFocus.Remove(c)
End Sub
<DefaultValue(eHighlightColor.None), Localizable(True), Description("Indicates the highlight color that is applied to the control.")>
Public Function GetHighlightColor(ByVal c As Control) As eHighlightColor
If _Highlights.ContainsKey(c) Then
Return _Highlights(c)
End If
Return eHighlightColor.None
End Function
Public Sub SetHighlightColor(ByVal c As Control, ByVal highlightColor As eHighlightColor)
If _Highlights.ContainsKey(c) Then
If highlightColor = eHighlightColor.None Then
RemoveHighlight(_Highlights, c)
Else
Dim color As eHighlightColor = _Highlights(c)
RemoveHighlight(_Highlights, c)
AddHighlight(_Highlights, c, highlightColor)
End If
ElseIf highlightColor <> eHighlightColor.None Then
AddHighlight(_Highlights, c, highlightColor)
End If
End Sub
Private _TabControl2 As Dictionary(Of System.Windows.Forms.TabControl, Integer) = New Dictionary(Of System.Windows.Forms.TabControl, Integer)()
Private _ParentPanel As Dictionary(Of Panel, Integer) = New Dictionary(Of Panel, Integer)()
Private Sub AddHighlight(ByVal highlights As Dictionary(Of Control, eHighlightColor), ByVal c As Control, ByVal highlightColor As eHighlightColor)
highlights.Add(c, highlightColor)
AddHandler c.LocationChanged, New EventHandler(AddressOf ControlLocationChanged)
AddHandler c.SizeChanged, New EventHandler(AddressOf ControlSizeChanged)
AddHandler c.VisibleChanged, New EventHandler(AddressOf ControlVisibleChanged)
If _HighlightPanel IsNot Nothing Then
If Not _HighlightPanel.Visible Then _HighlightPanel.Visible = True
_HighlightPanel.BringToFront()
End If
If c.Parent Is Nothing Then
AddHandler c.ParentChanged, AddressOf ControlParentChanged
Else
AddTabControlHandlers(c)
End If
UpdateHighlighterRegion()
End Sub
Private Sub ControlParentChanged(ByVal sender As Object, ByVal e As EventArgs)
Dim c As Control = CType(sender, Control)
RemoveHandler c.ParentChanged, AddressOf ControlParentChanged
AddTabControlHandlers(c)
End Sub
Private Sub AddTabControlHandlers(ByVal c As Control)
Dim tab2 As System.Windows.Forms.TabControl = TryCast(GetParentControl(c, GetType(System.Windows.Forms.TabControl)), System.Windows.Forms.TabControl)
If tab2 IsNot Nothing Then
If _TabControl2.ContainsKey(tab2) Then
_TabControl2(tab2) = _TabControl2(tab2) + 1
Else
_TabControl2.Add(tab2, 1)
AddHandler tab2.SelectedIndexChanged, AddressOf WinFormsTabSelectedIndexChanged
End If
Else
Dim parentPanel As Panel = TryCast(GetParentControl(c, GetType(Panel)), Panel)
If parentPanel IsNot Nothing Then
If _ParentPanel.ContainsKey(parentPanel) Then
_ParentPanel(parentPanel) = _ParentPanel(parentPanel) + 1
Else
_ParentPanel.Add(parentPanel, 1)
AddHandler parentPanel.Resize, AddressOf ParentPanelResized
AddHandler parentPanel.LocationChanged, AddressOf ParentPanelLocationChanged
End If
End If
End If
End Sub
Private Sub ParentPanelLocationChanged(ByVal sender As Object, ByVal e As EventArgs)
UpdateHighlights()
End Sub
Private Sub ParentPanelResized(ByVal sender As Object, ByVal e As EventArgs)
UpdateHighlights()
End Sub
Private Sub WinFormsTabSelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)
UpdateHighlighterRegion()
End Sub
Private Function GetParentControl(ByVal c As Control, ByVal parentType As Type) As Control
Dim parent As Control = c.Parent
While parent IsNot Nothing
If parentType.IsAssignableFrom(parent.[GetType]()) Then Return parent
parent = parent.Parent
End While
Return Nothing
End Function
Private Sub ControlVisibleChanged(ByVal sender As Object, ByVal e As EventArgs)
UpdateHighlighterRegion()
End Sub
Private Sub ControlSizeChanged(ByVal sender As Object, ByVal e As EventArgs)
UpdateHighlighterRegion()
End Sub
Private Sub ControlLocationChanged(ByVal sender As Object, ByVal e As EventArgs)
UpdateHighlighterRegion()
End Sub
Private Sub UpdateHighlighterRegion()
If _HighlightPanel IsNot Nothing Then _HighlightPanel.UpdateRegion()
End Sub
Public Sub UpdateHighlights()
UpdateHighlighterRegion()
End Sub
Private Sub RemoveHighlight(ByVal highlights As Dictionary(Of Control, eHighlightColor), ByVal c As Control)
highlights.Remove(c)
RemoveHandler c.LocationChanged, New EventHandler(AddressOf ControlLocationChanged)
RemoveHandler c.SizeChanged, New EventHandler(AddressOf ControlSizeChanged)
RemoveHandler c.VisibleChanged, New EventHandler(AddressOf ControlVisibleChanged)
Dim tab2 As System.Windows.Forms.TabControl = TryCast(GetParentControl(c, GetType(System.Windows.Forms.TabControl)), System.Windows.Forms.TabControl)
If tab2 IsNot Nothing Then
If _TabControl2.ContainsKey(tab2) Then
If _TabControl2(tab2) = 1 Then
_TabControl2.Remove(tab2)
RemoveHandler tab2.SelectedIndexChanged, AddressOf WinFormsTabSelectedIndexChanged
Else
_TabControl2(tab2) = _TabControl2(tab2) - 1
End If
End If
Else
Dim parentPanel As Panel = TryCast(GetParentControl(c, GetType(Panel)), Panel)
If parentPanel IsNot Nothing Then
If _ParentPanel.ContainsKey(parentPanel) Then
If _ParentPanel(parentPanel) = 1 Then
_ParentPanel.Remove(parentPanel)
RemoveHandler parentPanel.LocationChanged, AddressOf ParentPanelLocationChanged
RemoveHandler parentPanel.SizeChanged, AddressOf ParentPanelResized
Else
_ParentPanel(parentPanel) = _ParentPanel(parentPanel) - 1
End If
End If
End If
End If
UpdateHighlighterRegion()
End Sub
Friend ReadOnly Property Highlights As Dictionary(Of Control, eHighlightColor)
Get
Return _Highlights
End Get
End Property
Private _FocusHighlightColor As eHighlightColor = eHighlightColor.Blue
<DefaultValue(eHighlightColor.Blue), Category("Appearance"), Description("Indicates the highlight focus color."), Localizable(True)>
Public Property FocusHighlightColor As eHighlightColor
Get
Return _FocusHighlightColor
End Get
Set(ByVal value As eHighlightColor)
_FocusHighlightColor = value
If _HighlightPanel IsNot Nothing Then
_HighlightPanel.FocusHighlightColor = value
UpdateHighlighterRegion()
End If
End Set
End Property
Private _HighlightPanel As HighlightPanel = Nothing
Private _ContainerControl As Control = Nothing
<Description("Indicates container control highlighter is bound to. Should be set to parent form."), Category("Behavior")>
Public Property ContainerControl As Control
Get
Return _ContainerControl
End Get
Set(ByVal value As Control)
If Me.DesignMode Then
_ContainerControl = value
Return
End If
If _ContainerControl IsNot value Then
If _ContainerControl IsNot Nothing Then
RemoveHandler _ContainerControl.SizeChanged, AddressOf ContainerControlSizeChanged
RemoveHandler _ContainerControl.HandleCreated, AddressOf ContainerControlHandleCreated
If _HighlightPanel IsNot Nothing AndAlso _HighlightPanel.Parent Is _ContainerControl Then _ContainerControl.Controls.Remove(_HighlightPanel)
End If
_ContainerControl = value
If _ContainerControl IsNot Nothing Then
If _HighlightPanel Is Nothing Then
_HighlightPanel = New HighlightPanel(_Highlights)
_HighlightPanel.FocusHighlightColor = _FocusHighlightColor
_HighlightPanel.Margin = New System.Windows.Forms.Padding(0)
_HighlightPanel.Padding = New System.Windows.Forms.Padding(0)
_HighlightPanel.CustomHighlightColors = _CustomHighlightColors
_HighlightPanel.Visible = False
End If
AddHandler _ContainerControl.SizeChanged, AddressOf ContainerControlSizeChanged
AddHandler _ContainerControl.HandleCreated, AddressOf ContainerControlHandleCreated
_ContainerControl.Controls.Add(_HighlightPanel)
UpdateHighlightPanelBounds()
End If
End If
End Set
End Property
Private Sub ContainerControlHandleCreated(ByVal sender As Object, ByVal e As EventArgs)
If _Highlights.Count > 0 AndAlso _HighlightPanel IsNot Nothing AndAlso Not _HighlightPanel.Visible Then _HighlightPanel.Visible = True
End Sub
Private Sub UpdateHighlightPanelBounds()
Dim bounds As Rectangle = New Rectangle(0, 0, _ContainerControl.ClientRectangle.Width, _ContainerControl.ClientRectangle.Height)
If _HighlightPanel IsNot Nothing Then
If TypeOf _HighlightPanel.Parent Is Form Then
Dim form As Form = TryCast(_HighlightPanel.Parent, Form)
If form.AutoSize Then
bounds.X += form.Padding.Left
bounds.Y += form.Padding.Top
bounds.Width -= form.Padding.Horizontal
bounds.Height -= form.Padding.Vertical
End If
End If
If _HighlightPanel.Bounds.Equals(bounds) Then
_HighlightPanel.UpdateRegion()
Else
_HighlightPanel.Bounds = bounds
End If
_HighlightPanel.BringToFront()
End If
End Sub
Private _DelayTimer As Timer = Nothing
Private Sub ContainerControlSizeChanged(ByVal sender As Object, ByVal e As EventArgs)
Dim form As Form = TryCast(sender, Form)
If form IsNot Nothing Then
If _DelayTimer Is Nothing Then
_DelayTimer = New Timer()
_DelayTimer.Interval = 100
AddHandler _DelayTimer.Tick, New EventHandler(AddressOf DelayTimerTick)
_DelayTimer.Start()
End If
Return
End If
UpdateHighlightPanelBounds()
End Sub
Private Sub DelayTimerTick(ByVal sender As Object, ByVal e As EventArgs)
Dim timer As Timer = _DelayTimer
_DelayTimer = Nothing
RemoveHandler timer.Tick, New EventHandler(AddressOf DelayTimerTick)
timer.[Stop]()
timer.Dispose()
UpdateHighlightPanelBounds()
End Sub
Private _CustomHighlightColors As Color() = Nothing
<Category("Appearance"), Description("Array of colors used to render custom highlight color. Control expects 3 colors in array to be specified which define the highlight border.")>
Public Property CustomHighlightColors As Color()
Get
Return _CustomHighlightColors
End Get
Set(ByVal value As Color())
_CustomHighlightColors = value
If _HighlightPanel IsNot Nothing Then
_HighlightPanel.CustomHighlightColors = _CustomHighlightColors
_HighlightPanel.Invalidate()
End If
End Set
End Property
End Class
Public Enum eHighlightColor
None
Red
Blue
Green
Orange
Custom
End Enum

View File

@@ -0,0 +1,6 @@
namespace Pilz.UI.WinForms;
public interface ILoadContent
{
void LoadContent();
}

View File

@@ -0,0 +1,6 @@
namespace Pilz.UI.WinForms;
public interface ILoadContentAsync
{
Task LoadContentAsync();
}

View File

@@ -0,0 +1,18 @@
using System.Drawing;
using System.Drawing.Drawing2D;
namespace Pilz.UI.WinForms.PaintingControl;
public class ArrowLineCapProps : LineCapProps
{
public Size Size { get; set; } = new Size(10, 10);
public bool IsFilles { get; set; } = true;
internal override LineCapConfigurationArgs Configure()
{
var cap = new AdjustableArrowCap(Size.Width, Size.Height, IsFilles);
return new LineCapConfigurationArgs(cap);
}
}

View File

@@ -0,0 +1,15 @@
Imports System.Drawing
Imports System.Drawing.Drawing2D
Public Class ArrowLineCapProps
Inherits LineCapProps
Public Property Size As New Size(10, 10)
Public Property IsFilles As Boolean = True
Friend Overrides Function Configure() As LineCapConfigurationArgs
Dim cap As New AdjustableArrowCap(Size.Width, Size.Height, IsFilles)
Return New LineCapConfigurationArgs(cap)
End Function
End Class

View File

@@ -0,0 +1,285 @@
using Pilz.Drawing;
using Pilz.UI.WinForms.PaintingControl.EventArgs;
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace Pilz.UI.WinForms.PaintingControl;
/// <summary>
/// Contains static methods that are used for the standart PaintingObject Types.
/// </summary>
public class DefaultDrawMethodes
{
public static void DrawText(PaintingObjectPaintEventArgs e)
{
var obj = e.PaintingObject;
var b = new SolidBrush(obj.TextColor);
var p = new PointF();
var rect = new Rectangle((int)Math.Round(e.X), (int)Math.Round(e.Y), (int)Math.Round(obj.Width), (int)Math.Round(obj.Height));
var f = StringFormat.GenericDefault;
f.Alignment = obj.HorizontalTextAlignment;
f.LineAlignment = obj.VerticalTextAlignment;
float zoomFactor;
if (obj.Parent is null)
zoomFactor = 1.0f;
else
{
zoomFactor = obj.Parent.ZoomFactor.Width;
}
e.Graphics.DrawString(obj.Text, new Font(obj.TextFont.FontFamily, obj.TextFont.Size * zoomFactor, obj.TextFont.Style), b, rect, f);
}
private static object _DrawPicture_newSyncObj = new object();
public static void DrawPicture(PaintingObjectPaintEventArgs e)
{
var obj = e.PaintingObject;
Image objImg;
Size objImgSize;
RectangleF result;
Bitmap image;
SizeF zoomf;
var hasNoParent = e.PaintingObject.Parent is null;
object syncObj;
if (hasNoParent)
{
zoomf = new SizeF(1f, 1f);
syncObj = _DrawPicture_newSyncObj;
}
else
{
zoomf = e.PaintingObject.Parent.ZoomFactor;
syncObj = e.PaintingObject.Parent;
}
lock (syncObj)
{
if (obj?.Image is null)
return;
objImg = obj.Image;
objImgSize = objImg.Size;
}
image = (Bitmap)obj.BufferedImage;
result = CalculateImageResolution(obj, objImgSize, zoomf);
if (obj.ImageProperties.Rotate == 90 || obj.ImageProperties.Rotate == 270)
result = CalculateImageResolution(obj, new SizeF(objImgSize.Height, objImgSize.Width), zoomf);
if (image is null)
{
var needRescaleImageBecauseRot = false;
image = DrawToNewImage((Bitmap)objImg, result.Size);
switch (obj.ImageProperties.Rotate)
{
case 90:
{
image.RotateFlip(RotateFlipType.Rotate90FlipNone);
needRescaleImageBecauseRot = true;
break;
}
case 180:
{
image.RotateFlip(RotateFlipType.Rotate180FlipNone);
break;
}
case 270:
{
image.RotateFlip(RotateFlipType.Rotate270FlipNone);
needRescaleImageBecauseRot = true;
break;
}
}
if (obj.ImageProperties.FlipX)
image.RotateFlip(RotateFlipType.RotateNoneFlipX);
if (obj.ImageProperties.FlipY)
image.RotateFlip(RotateFlipType.RotateNoneFlipY);
if (needRescaleImageBecauseRot)
{
result = CalculateImageResolution(obj, new SizeF(objImgSize.Height, objImgSize.Width), zoomf);
image = DrawToNewImage(image, result.Size);
}
obj.BufferedImage = image;
}
if (image is not null)
{
lock (syncObj)
e.Graphics.DrawImageUnscaled(image, new Rectangle(new Point((int)Math.Round(obj.Location.X + result.Location.X - e.Offset.X), (int)Math.Round(obj.Location.Y + result.Location.Y - e.Offset.Y)), result.Size.ToSize()));
}
}
private static Bitmap DrawToNewImage(Bitmap image, SizeF newSize)
{
var bmp = new Bitmap((int)Math.Round(newSize.Width < 0f ? newSize.Width * -1 : newSize.Width), (int)Math.Round(newSize.Height < 0f ? newSize.Height * -1 : newSize.Height));
var gimage = Graphics.FromImage(bmp);
gimage.SmoothingMode = SmoothingMode.HighQuality;
gimage.PixelOffsetMode = PixelOffsetMode.HighQuality;
gimage.PageUnit = GraphicsUnit.Pixel;
gimage.InterpolationMode = InterpolationMode.HighQualityBicubic;
gimage.DrawImage(image, new RectangleF(PointF.Empty, newSize));
gimage.Dispose();
return bmp;
}
public static void DrawLine(PaintingObjectPaintEventArgs e)
{
var obj = e.PaintingObject;
var p2 = new Pen(obj.OutlineColor, obj.OutlineThicknes) { DashStyle = obj.OutlineDashStyle };
if (obj.LineEndCap is not null)
{
var args = obj.LineEndCap.Configure();
p2.StartCap = args.LineCap;
p2.CustomStartCap = args.CustomLineCap;
}
if (obj.LineStartCap is not null)
{
var args = obj.LineStartCap.Configure();
p2.EndCap = args.LineCap;
p2.CustomEndCap = args.CustomLineCap;
}
p2.Alignment = PenAlignment.Center;
var no = new PointF(e.X, e.Y);
e.Graphics.DrawLine(p2, no, no + obj.Size);
}
private static RectangleF CalculateImageResolution(PaintingObject obj, SizeF imageSize, SizeF zoom)
{
var result = new RectangleF();
var objrect = new RectangleF(obj.Location, obj.Size);
var size = new SizeF(imageSize.Width * zoom.Width, imageSize.Height * zoom.Height);
var clientRectangle = objrect;
var val = clientRectangle.Width / size.Width;
clientRectangle = objrect;
var num = Math.Min(val, clientRectangle.Height / size.Height);
result.Width = (int)Math.Round(Math.Truncate((double)(size.Width * num)));
result.Height = (int)Math.Round(Math.Truncate((double)(size.Height * num)));
clientRectangle = objrect;
result.X = (long)Math.Round(clientRectangle.Width - result.Width) / 2L;
clientRectangle = objrect;
result.Y = (long)Math.Round(clientRectangle.Height - result.Height) / 2L;
return result;
}
public static void DrawTriangle(PaintingObjectPaintEventArgs e)
{
var obj = e.PaintingObject;
var p1 = new Point((int)Math.Round(obj.Size.Width / 2f + e.X), (int)Math.Round(e.Y));
var p2 = new Point((int)Math.Round(e.X), (int)Math.Round(e.Y + obj.Size.Height));
var p3 = new Point((int)Math.Round(e.X + obj.Size.Width), (int)Math.Round(e.Y + obj.Size.Height));
if (obj.EnableFill)
{
var b = new SolidBrush(obj.FillColor);
e.Graphics.FillPolygon(b, new[] { p1, p2, p3 });
}
if (obj.EnableOutline)
{
var lw = obj.OutlineThicknes;
var p = new Pen(obj.OutlineColor, obj.OutlineThicknes) { DashStyle = obj.OutlineDashStyle, Alignment = PenAlignment.Inset };
e.Graphics.DrawPolygon(p, new[] { p1, p2, p3 });
}
}
public static void DrawRectangle(PaintingObjectPaintEventArgs e)
{
var obj = e.PaintingObject;
var hol = obj.OutlineThicknes / 2f;
if (obj.EnableFill)
{
var b = new SolidBrush(obj.FillColor);
var rect = obj.EnableOutline ? new Rectangle((int)Math.Round(e.X + hol), (int)Math.Round(e.Y + hol), (int)Math.Round(obj.Size.Width - hol * 2f), (int)Math.Round(obj.Size.Height - hol * 2f)) : new Rectangle((int)Math.Round(e.X), (int)Math.Round(e.Y), (int)Math.Round(obj.Size.Width), (int)Math.Round(obj.Size.Height));
e.Graphics.FillRectangle(b, rect);
}
if (obj.EnableOutline)
{
var p = new Pen(obj.OutlineColor, obj.OutlineThicknes) { DashStyle = obj.OutlineDashStyle, Alignment = PenAlignment.Inset };
var rect = new Rectangle((int)Math.Round(e.X), (int)Math.Round(e.Y), (int)Math.Round(obj.Size.Width), (int)Math.Round(obj.Size.Height));
e.Graphics.DrawRectangle(p, rect);
}
}
public static void DrawEllipse(PaintingObjectPaintEventArgs e)
{
var obj = e.PaintingObject;
if (obj.EnableFill)
{
var b = new SolidBrush(obj.FillColor);
var rect = new Rectangle((int)Math.Round(e.X), (int)Math.Round(e.Y), (int)Math.Round(obj.Size.Width), (int)Math.Round(obj.Size.Height));
e.Graphics.FillEllipse(b, rect);
}
if (obj.EnableOutline)
{
var p = new Pen(obj.OutlineColor, obj.OutlineThicknes) { DashStyle = obj.OutlineDashStyle, Alignment = PenAlignment.Inset };
var rect = new Rectangle((int)Math.Round(e.X), (int)Math.Round(e.Y), (int)Math.Round(obj.Size.Width), (int)Math.Round(obj.Size.Height));
e.Graphics.DrawEllipse(p, rect);
}
}
public static void DrawSelection(PaintingObjectPaintEventArgs e)
{
var obj = e.PaintingObject;
var lw = 2.5f;
var hlw = lw / 2f;
var hlwphol = hlw; // + hol
var hlwpholm2 = hlwphol * 2f;
var p = new Pen(Color.CornflowerBlue, lw) { DashStyle = obj.SelectionDashStyle, Alignment = PenAlignment.Outset };
var rect = new Rectangle((int)Math.Round(e.X - hlwphol), (int)Math.Round(e.Y - hlwphol), (int)Math.Round(obj.Size.Width + hlwpholm2), (int)Math.Round(obj.Size.Height + hlwpholm2));
e.Graphics.DrawRectangle(p, rect);
}
public static void DrawGrid(PaintEventArgs e, PaintingControl pc, PointF offset)
{
var p = new Pen(pc.GridColor, 0.5f);
var curX = (int)Math.Round(pc.GridChunkSize.Width * pc.ZoomFactor.Width - offset.X);
while (curX < pc.Width)
{
e.Graphics.DrawLine(p, curX, -offset.Y, curX, pc.Height);
curX = (int)Math.Round(curX + pc.GridChunkSize.Width * pc.ZoomFactor.Width);
}
var curY = (int)Math.Round(pc.GridChunkSize.Height * pc.ZoomFactor.Height - offset.Y);
while (curY < pc.Height)
{
e.Graphics.DrawLine(p, -offset.X, curY, pc.Width, curY);
curY = (int)Math.Round(curY + pc.GridChunkSize.Height * pc.ZoomFactor.Height);
}
}
public static void DrawAreaSelection(PaintEventArgs e, PaintingControl pc, PointF startMousePos, PointF lastMousePos)
{
var rectToDraw = HelpfulDrawingFunctions.GetRectangle(startMousePos, lastMousePos);
var p = new Pen(pc.AreaSelectionColor);
p.DashStyle = startMousePos.X >= lastMousePos.X ? DashStyle.DashDot : DashStyle.Solid;
p.Width = 3f;
e.Graphics.DrawRectangle(p, rectToDraw.X, rectToDraw.Y, rectToDraw.Width, rectToDraw.Height);
}
}

View File

@@ -0,0 +1,249 @@
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Windows.Forms
Imports Pilz.Drawing
''' <summary>
''' Contains static methods that are used for the standart PaintingObject Types.
''' </summary>
Public Class DefaultDrawMethodes
Public Shared Sub DrawText(e As PaintingObjectPaintEventArgs)
Dim obj As PaintingObject = e.PaintingObject
Dim b As New SolidBrush(obj.TextColor)
Dim p As New PointF
Dim rect As New Rectangle(e.X, e.Y, obj.Width, obj.Height)
Dim f As StringFormat = StringFormat.GenericDefault
f.Alignment = obj.HorizontalTextAlignment
f.LineAlignment = obj.VerticalTextAlignment
Dim zoomFactor As Single
If obj.Parent Is Nothing Then
zoomFactor = 1.0!
Else
zoomFactor = obj.Parent.ZoomFactor.Width
End If
e.Graphics.DrawString(obj.Text, New Font(obj.TextFont.FontFamily, obj.TextFont.Size * zoomFactor, obj.TextFont.Style), b, rect, f)
End Sub
Public Shared Sub DrawPicture(e As PaintingObjectPaintEventArgs)
Dim obj As PaintingObject = e.PaintingObject
Dim objImg As Image
Dim objImgSize As Size
Dim result As RectangleF
Dim image As Bitmap
Dim zoomf As SizeF
Dim hasNoParent As Boolean = e.PaintingObject.Parent Is Nothing
Dim syncObj As Object
If hasNoParent Then
zoomf = New SizeF(1, 1)
Static newSyncObj As New Object
syncObj = newSyncObj
Else
zoomf = e.PaintingObject.Parent.ZoomFactor
syncObj = e.PaintingObject.Parent
End If
SyncLock syncObj
If obj?.Image Is Nothing Then Return
objImg = obj.Image
objImgSize = objImg.Size
End SyncLock
image = obj.BufferedImage
result = CalculateImageResolution(obj, objImgSize, zoomf)
If obj.ImageProperties.Rotate = 90 OrElse obj.ImageProperties.Rotate = 270 Then
result = CalculateImageResolution(obj, New SizeF(objImgSize.Height, objImgSize.Width), zoomf)
End If
If image Is Nothing Then
Dim needRescaleImageBecauseRot As Boolean = False
image = DrawToNewImage(objImg, result.Size)
Select Case obj.ImageProperties.Rotate
Case 90
image.RotateFlip(RotateFlipType.Rotate90FlipNone)
needRescaleImageBecauseRot = True
Case 180
image.RotateFlip(RotateFlipType.Rotate180FlipNone)
Case 270
image.RotateFlip(RotateFlipType.Rotate270FlipNone)
needRescaleImageBecauseRot = True
End Select
If obj.ImageProperties.FlipX Then
image.RotateFlip(RotateFlipType.RotateNoneFlipX)
End If
If obj.ImageProperties.FlipY Then
image.RotateFlip(RotateFlipType.RotateNoneFlipY)
End If
If needRescaleImageBecauseRot Then
result = CalculateImageResolution(obj, New SizeF(objImgSize.Height, objImgSize.Width), zoomf)
image = DrawToNewImage(image, result.Size)
End If
obj.BufferedImage = image
End If
If image IsNot Nothing Then
SyncLock syncObj
e.Graphics.DrawImageUnscaled(image, New Rectangle(New Point(obj.Location.X + result.Location.X - e.Offset.X, obj.Location.Y + result.Location.Y - e.Offset.Y), result.Size.ToSize))
End SyncLock
End If
End Sub
Private Shared Function DrawToNewImage(image As Bitmap, newSize As SizeF) As Bitmap
Dim bmp As New Bitmap(CInt(If(newSize.Width < 0, newSize.Width * -1, newSize.Width)),
CInt(If(newSize.Height < 0, newSize.Height * -1, newSize.Height)))
Dim gimage As Graphics = Graphics.FromImage(bmp)
gimage.SmoothingMode = SmoothingMode.HighQuality
gimage.PixelOffsetMode = PixelOffsetMode.HighQuality
gimage.PageUnit = GraphicsUnit.Pixel
gimage.InterpolationMode = InterpolationMode.HighQualityBicubic
gimage.DrawImage(image, New RectangleF(PointF.Empty, newSize))
gimage.Dispose()
Return bmp
End Function
Public Shared Sub DrawLine(e As PaintingObjectPaintEventArgs)
Dim obj As PaintingObject = e.PaintingObject
Dim p2 As New Pen(obj.OutlineColor, obj.OutlineThicknes) With {
.DashStyle = obj.OutlineDashStyle
}
If obj.LineEndCap IsNot Nothing Then
Dim args As LineCapConfigurationArgs = obj.LineEndCap.Configure
p2.StartCap = args.LineCap
p2.CustomStartCap = args.CustomLineCap
End If
If obj.LineStartCap IsNot Nothing Then
Dim args As LineCapConfigurationArgs = obj.LineStartCap.Configure
p2.EndCap = args.LineCap
p2.CustomEndCap = args.CustomLineCap
End If
p2.Alignment = PenAlignment.Center
Dim no As PointF = New PointF(e.X, e.Y)
e.Graphics.DrawLine(p2, no, no + obj.Size)
End Sub
Private Shared Function CalculateImageResolution(obj As PaintingObject, imageSize As SizeF, zoom As SizeF) As RectangleF
Dim result As New RectangleF
Dim objrect As New RectangleF(obj.Location, obj.Size)
Dim size As SizeF = New SizeF(imageSize.Width * zoom.Width, imageSize.Height * zoom.Height)
Dim clientRectangle As RectangleF = objrect
Dim val As Single = clientRectangle.Width / size.Width
clientRectangle = objrect
Dim num As Single = Math.Min(val, clientRectangle.Height / size.Height)
result.Width = CInt(Math.Truncate(size.Width * num))
result.Height = CInt(Math.Truncate(size.Height * num))
clientRectangle = objrect
result.X = (clientRectangle.Width - result.Width) \ 2
clientRectangle = objrect
result.Y = (clientRectangle.Height - result.Height) \ 2
Return result
End Function
Public Shared Sub DrawTriangle(e As PaintingObjectPaintEventArgs)
Dim obj As PaintingObject = e.PaintingObject
Dim p1 As New Point(obj.Size.Width / 2 + e.X, e.Y)
Dim p2 As New Point(e.X, e.Y + obj.Size.Height)
Dim p3 As New Point(e.X + obj.Size.Width, e.Y + obj.Size.Height)
If obj.EnableFill Then
Dim b As New SolidBrush(obj.FillColor)
e.Graphics.FillPolygon(b, {p1, p2, p3})
End If
If obj.EnableOutline Then
Dim lw As Single = obj.OutlineThicknes
Dim p As New Pen(obj.OutlineColor, obj.OutlineThicknes) With {.DashStyle = obj.OutlineDashStyle, .Alignment = PenAlignment.Inset}
e.Graphics.DrawPolygon(p, {p1, p2, p3})
End If
End Sub
Public Shared Sub DrawRectangle(e As PaintingObjectPaintEventArgs)
Dim obj As PaintingObject = e.PaintingObject
Dim hol As Single = obj.OutlineThicknes / 2
If obj.EnableFill Then
Dim b As New SolidBrush(obj.FillColor)
Dim rect As Rectangle = If(obj.EnableOutline,
New Rectangle(e.X + hol, e.Y + hol, obj.Size.Width - hol * 2, obj.Size.Height - hol * 2),
New Rectangle(e.X, e.Y, obj.Size.Width, obj.Size.Height))
e.Graphics.FillRectangle(b, rect)
End If
If obj.EnableOutline Then
Dim p As New Pen(obj.OutlineColor, obj.OutlineThicknes) With {.DashStyle = obj.OutlineDashStyle, .Alignment = PenAlignment.Inset}
Dim rect As New Rectangle(e.X, e.Y, obj.Size.Width, obj.Size.Height)
e.Graphics.DrawRectangle(p, rect)
End If
End Sub
Public Shared Sub DrawEllipse(e As PaintingObjectPaintEventArgs)
Dim obj As PaintingObject = e.PaintingObject
If obj.EnableFill Then
Dim b As New SolidBrush(obj.FillColor)
Dim rect As Rectangle = New Rectangle(e.X, e.Y, obj.Size.Width, obj.Size.Height)
e.Graphics.FillEllipse(b, rect)
End If
If obj.EnableOutline Then
Dim p As New Pen(obj.OutlineColor, obj.OutlineThicknes) With {.DashStyle = obj.OutlineDashStyle, .Alignment = PenAlignment.Inset}
Dim rect As New Rectangle(e.X, e.Y, obj.Size.Width, obj.Size.Height)
e.Graphics.DrawEllipse(p, rect)
End If
End Sub
Public Shared Sub DrawSelection(e As PaintingObjectPaintEventArgs)
Dim obj As PaintingObject = e.PaintingObject
Dim lw As Single = 2.5!
Dim hlw As Single = lw / 2
Dim hlwphol As Single = hlw '+ hol
Dim hlwpholm2 As Single = hlwphol * 2
Dim p As New Pen(Color.CornflowerBlue, lw) With {.DashStyle = obj.SelectionDashStyle, .Alignment = PenAlignment.Outset}
Dim rect As New Rectangle(e.X - hlwphol, e.Y - hlwphol, obj.Size.Width + hlwpholm2, obj.Size.Height + hlwpholm2)
e.Graphics.DrawRectangle(p, rect)
End Sub
Public Shared Sub DrawGrid(e As PaintEventArgs, pc As PaintingControl, offset As PointF)
Dim p As New Pen(pc.GridColor, 0.5)
Dim curX As Integer = pc.GridChunkSize.Width * pc.ZoomFactor.Width - offset.X
Do While curX < pc.Width
e.Graphics.DrawLine(p, curX, -offset.Y, curX, pc.Height)
curX += (pc.GridChunkSize.Width * pc.ZoomFactor.Width)
Loop
Dim curY As Integer = pc.GridChunkSize.Height * pc.ZoomFactor.Height - offset.Y
Do While curY < pc.Height
e.Graphics.DrawLine(p, -offset.X, curY, pc.Width, curY)
curY += (pc.GridChunkSize.Height * pc.ZoomFactor.Height)
Loop
End Sub
Public Shared Sub DrawAreaSelection(e As PaintEventArgs, pc As PaintingControl, startMousePos As PointF, lastMousePos As PointF)
Dim rectToDraw As RectangleF = HelpfulDrawingFunctions.GetRectangle(startMousePos, lastMousePos)
Dim p As New Pen(pc.AreaSelectionColor)
p.DashStyle = If(startMousePos.X >= lastMousePos.X, DashStyle.DashDot, DashStyle.Solid)
p.Width = 3
e.Graphics.DrawRectangle(p, rectToDraw.X, rectToDraw.Y, rectToDraw.Width, rectToDraw.Height)
End Sub
End Class

View File

@@ -0,0 +1,17 @@
using System.Drawing.Drawing2D;
namespace Pilz.UI.WinForms.PaintingControl;
public class DefaultLineCapProps : LineCapProps
{
public LineCap LineCap { get; set; } = LineCap.Flat;
public CustomLineCap CustomLineCap { get; set; } = null;
internal override LineCapConfigurationArgs Configure()
{
return new LineCapConfigurationArgs(LineCap, CustomLineCap);
}
}

View File

@@ -0,0 +1,13 @@
Imports System.Drawing.Drawing2D
Public Class DefaultLineCapProps
Inherits LineCapProps
Public Property LineCap As LineCap = LineCap.Flat
Public Property CustomLineCap As CustomLineCap = Nothing
Friend Overrides Function Configure() As LineCapConfigurationArgs
Return New LineCapConfigurationArgs(LineCap, CustomLineCap)
End Function
End Class

View File

@@ -0,0 +1,8 @@
using System;
namespace Pilz.UI.WinForms.PaintingControl.EventArgs;
public class PaintingObjectEventArgs(PaintingObject[] paintingObjects) : EventArgs
{
public PaintingObject[] PaintingObjects { get; private set; } = paintingObjects;
}

View File

@@ -0,0 +1,19 @@
' Nicht gemergte Änderung aus Projekt "Pilz.UI (net8.0-windows)"
' Vor:
' Imports System.Windows.Forms
' Imports Newtonsoft.Json
' Nach:
' Imports System.Windows.Forms
'
' Imports Newtonsoft.Json
Public Class PaintingObjectEventArgs
Inherits EventArgs
Public ReadOnly Property PaintingObjects As PaintingObject() = Nothing
Friend Sub New(paintingObjects As PaintingObject())
_PaintingObjects = paintingObjects
End Sub
End Class

View File

@@ -0,0 +1,53 @@
using System;
using System.Drawing;
namespace Pilz.UI.WinForms.PaintingControl.EventArgs;
public class PaintingObjectPaintEventArgs(PaintingObject obj, Graphics g, PointF offset) : EventArgs
{
/// <summary>
/// The Painting Object to draw.
/// </summary>
/// <returns></returns>
public PaintingObject PaintingObject { get; } = obj;
/// <summary>
/// The current offset of the page on the screen.
/// </summary>
/// <returns></returns>
public PointF Offset { get; } = offset;
/// <summary>
/// The Grpahics from the parent PaintingControl.
/// </summary>
/// <returns></returns>
public Graphics Graphics { get; } = g;
/// <summary>
/// The position of the PaintingObject on Screen.
/// </summary>
/// <returns></returns>
public PointF Location => new(X, Y);
/// <summary>
/// The X position of the PaintingObject on Screen.
/// </summary>
/// <returns></returns>
public float X => PaintingObject.X - Offset.X;
/// <summary>
/// The Y position of the PaintingObject on Screen.
/// </summary>
/// <returns></returns>
public float Y => PaintingObject.Y - Offset.Y;
/// <summary>
/// The rectangle of the PaintingObject on Screen.
/// </summary>
/// <returns></returns>
public RectangleF Rectangle => new RectangleF(X, Y, PaintingObject.Width, PaintingObject.Height);
public PaintingObjectPaintEventArgs(PaintingObject obj, Graphics g) : this(obj, g, obj.Parent.Offset)
{
}
}

View File

@@ -0,0 +1,83 @@
Imports System.Drawing
' Nicht gemergte Änderung aus Projekt "Pilz.UI (net8.0-windows)"
' Vor:
' Imports System.Windows.Forms
' Imports Newtonsoft.Json
' Nach:
' Imports System.Windows.Forms
'
' Imports Newtonsoft.Json
Public Class PaintingObjectPaintEventArgs
Inherits EventArgs
''' <summary>
''' The Painting Object to draw.
''' </summary>
''' <returns></returns>
Public ReadOnly Property PaintingObject As PaintingObject
''' <summary>
''' The current offset of the page on the screen.
''' </summary>
''' <returns></returns>
Public ReadOnly Property Offset As PointF
''' <summary>
''' The Grpahics from the parent PaintingControl.
''' </summary>
''' <returns></returns>
Public ReadOnly Property Graphics As Graphics
''' <summary>
''' The position of the PaintingObject on Screen.
''' </summary>
''' <returns></returns>
Public ReadOnly Property Location As PointF
Get
Return New PointF(X, Y)
End Get
End Property
''' <summary>
''' The X position of the PaintingObject on Screen.
''' </summary>
''' <returns></returns>
Public ReadOnly Property X As Single
Get
Return PaintingObject.X - Offset.X
End Get
End Property
''' <summary>
''' The Y position of the PaintingObject on Screen.
''' </summary>
''' <returns></returns>
Public ReadOnly Property Y As Single
Get
Return PaintingObject.Y - Offset.Y
End Get
End Property
''' <summary>
''' The rectangle of the PaintingObject on Screen.
''' </summary>
''' <returns></returns>
Public ReadOnly Property Rectangle As RectangleF
Get
Return New RectangleF(X, Y, PaintingObject.Width, PaintingObject.Height)
End Get
End Property
Friend Sub New(obj As PaintingObject, g As Graphics)
Me.New(obj, g, obj.Parent.Offset)
End Sub
Friend Sub New(obj As PaintingObject, g As Graphics, offset As PointF)
PaintingObject = obj
Me.Offset = offset
Graphics = g
End Sub
End Class

View File

@@ -0,0 +1,6 @@
namespace Pilz.UI.WinForms.PaintingControl;
public interface IPaintingObjectContainer
{
}

View File

@@ -0,0 +1,3 @@
Public Interface IPaintingObjectContainer
End Interface

View File

@@ -0,0 +1,8 @@
namespace Pilz.UI.WinForms.PaintingControl;
public class PaintingObjectImageProperties
{
public bool FlipY { get; set; } = false;
public bool FlipX { get; set; } = false;
public ushort Rotate { get; set; } = 0;
}

View File

@@ -0,0 +1,5 @@
Public Class PaintingObjectImageProperties
Public Property FlipY As Boolean = False
Public Property FlipX As Boolean = False
Public Property Rotate As UShort = 0
End Class

View File

@@ -0,0 +1,26 @@
using System.Drawing.Drawing2D;
namespace Pilz.UI.WinForms.PaintingControl;
public class LineCapConfigurationArgs
{
public LineCap LineCap { get; private set; }
public CustomLineCap CustomLineCap { get; private set; }
public LineCapConfigurationArgs(LineCap lineCap) : this(lineCap, null)
{
}
public LineCapConfigurationArgs(CustomLineCap customLineCap) : this(default, customLineCap)
{
}
public LineCapConfigurationArgs(LineCap lineCap, CustomLineCap customLineCap)
{
LineCap = lineCap;
CustomLineCap = customLineCap;
}
}

View File

@@ -0,0 +1,21 @@
Imports System.Drawing.Drawing2D
Public Class LineCapConfigurationArgs
Public ReadOnly Property LineCap As LineCap
Public ReadOnly Property CustomLineCap As CustomLineCap
Public Sub New(lineCap As LineCap)
Me.New(lineCap, Nothing)
End Sub
Public Sub New(customLineCap As CustomLineCap)
Me.New(Nothing, customLineCap)
End Sub
Public Sub New(lineCap As LineCap, customLineCap As CustomLineCap)
Me.LineCap = lineCap
Me.CustomLineCap = customLineCap
End Sub
End Class

View File

@@ -0,0 +1,8 @@
namespace Pilz.UI.WinForms.PaintingControl;
public abstract class LineCapProps
{
internal abstract LineCapConfigurationArgs Configure();
}

View File

@@ -0,0 +1,5 @@
Public MustInherit Class LineCapProps
Friend MustOverride Function Configure() As LineCapConfigurationArgs
End Class

View File

@@ -0,0 +1,787 @@
using Pilz.Drawing;
using Pilz.UI.WinForms.PaintingControl.EventArgs;
using Pilz.UI.WinForms.Utilities;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
namespace Pilz.UI.WinForms.PaintingControl;
public class PaintingControl : UserControl, IPaintingObjectContainer
{
private PaintingObject curObjMouseDown = null;
private Color bgColor = Color.White;
private Point startMousePos = default;
private Point lastMousePos = default;
private int lastHashCode = 0;
private Point calcOffset_MouseOnTab = Point.Empty;
private bool calcOffset_IsActive = false;
private PointF calcOffset_LastOffset = PointF.Empty;
private new Color ForeColor { get; set; }
private new Font Font { get; set; }
private new string Text { get; set; }
public PointF Offset { get; set; } = PointF.Empty;
public PaintingObjectList PaintingObjects { get; private set; }
public bool VisibleForMouseEvents { get; set; } = true;
public bool AutoAreaSelection { get; set; } = true;
public bool AutoSingleSelection { get; set; } = true;
public bool AutoMultiselection { get; set; } = true;
public bool AutoRemoveSelection { get; set; } = true;
public DashStyle AreaSelectionDashStyle { get; set; } = DashStyle.DashDot;
public Color AreaSelectionColor { get; set; } = Color.CornflowerBlue;
public bool AutoMoveObjects { get; set; } = true;
private bool _IsAreaSelecting = false;
public bool IsMovingObjects { get; private set; } = false;
public bool GridEnabled { get; set; } = true;
public bool GridVisible { get; set; } = false;
public Size GridChunkSize { get; set; } = new Size(20, 20);
public Color GridColor { get; set; } = Color.LightGray;
public DelegateDrawPaintingControlGridMethode DrawGridMethode { get; set; } = DefaultDrawMethodes.DrawGrid;
public DelegateDrawPaintingControlAreaSelectionMethode DrawAreaSelectionMethode { get; set; } = DefaultDrawMethodes.DrawAreaSelection;
private SizeF _ZoomFactor = new SizeF(1f, 1f);
private int _stopDrawing = -1;
private Image bufferedImg = null;
private bool pressedShift = false;
private bool pressedControl = false;
private bool pressedAlt = false;
private Dictionary<PaintingObject, PointF> savedPos = new Dictionary<PaintingObject, PointF>();
public event SelectionChangedEventHandler SelectionChanged;
public delegate void SelectionChangedEventHandler(object sender, PaintingObjectEventArgs e);
public event PaintingObjectAddedEventHandler PaintingObjectAdded;
public delegate void PaintingObjectAddedEventHandler(object sender, PaintingObjectEventArgs e);
public event PaintingObjectRemovedEventHandler PaintingObjectRemoved;
public delegate void PaintingObjectRemovedEventHandler(object sender, PaintingObjectEventArgs e);
public event AfterScrollingDoneEventHandler AfterScrollingDone;
public delegate void AfterScrollingDoneEventHandler(object sender, EventArgs e);
public event ZoomFactorChangedEventHandler ZoomFactorChanged;
public delegate void ZoomFactorChangedEventHandler(object sender, EventArgs e);
public PaintingObject[] SelectedObjects
{
get
{
var objs = new List<PaintingObject>();
foreach (var obj in PaintingObjects)
{
if (obj.Selected)
objs.Add(obj);
}
return objs.ToArray();
}
}
public bool IsLayoutSuspended
{
get
{
return Convert.ToInt32(GetType().GetField("layoutSuspendCount", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(this)) != 0;
}
}
public bool StopDrawing
{
get
{
return _stopDrawing > -1;
}
}
public override Color BackColor
{
get
{
return bgColor;
}
set
{
bgColor = value;
base.BackColor = value;
// If value <> Color.Transparent Then
// MyBase.BackColor = value
// End If
}
}
public bool IsAreaSelecting
{
get
{
return _IsAreaSelecting && startMousePos != lastMousePos;
}
}
public SizeF ZoomFactor
{
get
{
return _ZoomFactor;
}
set
{
if (_ZoomFactor != value)
{
_ZoomFactor = value;
ResetAllBufferedImages();
ZoomFactorChanged?.Invoke(this, new EventArgs());
}
}
}
public PaintingControl()
{
PaintingObjects = new(this);
PaintingObjectResizing.CheckEnabled += PaintingObjectResizing_CheckEnabled;
DoubleBuffered = true;
KeyDown += CheckKeyDown;
KeyUp += CheckKeyDown;
MouseClick += CheckMouseClick;
MouseDown += CheckMouseDown;
MouseUp += CheckMouseUp;
MouseMove += CheckMouseMove;
PaintingObjectAdded += PaintingControl_PaintingObjectAdded;
PaintingObjectRemoved += PaintingControl_PaintingObjectAdded;
MouseWheel += CheckMouseWheel;
}
~PaintingControl()
{
PaintingObjectResizing.CheckEnabled -= PaintingObjectResizing_CheckEnabled;
}
private void PaintingObjectResizing_CheckEnabled(PaintingObjectResizing sender, ref bool enabled)
{
if (PaintingObjects.Contains(sender.PaintingObject) && pressedAlt)
enabled = false;
}
private void ResetAllBufferedImages()
{
foreach (var ob in PaintingObjects)
ob.ResetImageBuffer();
Refresh();
}
protected void CheckKeyDown(object sender, KeyEventArgs e)
{
pressedShift = e.Shift;
pressedControl = e.Control;
pressedAlt = e.Alt;
}
internal RectangleF AreaSelectionRectangle
{
get
{
return HelpfulDrawingFunctions.GetRectangle(startMousePos, lastMousePos);
}
}
protected void CheckMouseClick(object sender, MouseEventArgs e)
{
foreach (var obj in GetObjects(new Point((int)Math.Round(e.X + Offset.X), (int)Math.Round(e.Y + Offset.Y))))
{
if (!obj.MouseTransparency)
obj.RaiseMouseClick(GetMouseEventArgs(e, obj));
}
}
protected void CheckMouseDown(object sender, MouseEventArgs e)
{
lastMousePos = new Point((int)Math.Round(e.X + Offset.X), (int)Math.Round(e.Y + Offset.Y));
curObjMouseDown = GetObjects(lastMousePos).Where(n => !n.MouseTransparency).LastOrDefault();
curObjMouseDown?.RaiseMouseDown(GetMouseEventArgs(e, curObjMouseDown));
if (curObjMouseDown is null || !curObjMouseDown.Selected || pressedControl)
{
var hasMovedObjects = false;
if (IsMovingObjects)
{
foreach (var obj in GetSelectedObjects())
{
if (HelpfulDrawingFunctions.IsPointInRectangle(lastMousePos, obj.Rectangle))
{
hasMovedObjects = true;
break;
}
}
}
if (!hasMovedObjects && !_IsAreaSelecting)
{
var selChanged = new List<PaintingObject>();
if (AutoRemoveSelection && !pressedControl)
{
foreach (var obj in PaintingObjects)
{
if (obj.Selected)
{
obj.SelectedDirect = false;
if (!selChanged.Contains(obj))
selChanged.Add(obj);
}
}
}
if (AutoSingleSelection && curObjMouseDown is not null)
{
var objtosel = curObjMouseDown;
if (objtosel.EnableSelection)
{
objtosel.SelectedDirect = !objtosel.Selected;
if (!selChanged.Contains(objtosel))
selChanged.Add(objtosel);
else
{
selChanged.Remove(objtosel);
}
}
}
SelectionChanged?.Invoke(this, new PaintingObjectEventArgs(selChanged.ToArray()));
}
}
if (pressedAlt)
{
calcOffset_MouseOnTab = new Point(e.X, e.Y);
calcOffset_LastOffset = Offset;
calcOffset_IsActive = true;
Cursor = Cursors.Arrow;
}
else
{
switch (e.Button)
{
case MouseButtons.Left:
{
savedPos.Clear();
if (AutoMoveObjects)
SaveObjectPositions(e, GetSelectedObjects());
if (savedPos.Count > 0)
IsMovingObjects = true;
else if (AutoAreaSelection)
{
startMousePos = new Point((int)Math.Round(e.X + Offset.X), (int)Math.Round(e.Y + Offset.Y));
lastMousePos = startMousePos; // New Point(e.X - Offset.X, e.Y - Offset.Y)
_IsAreaSelecting = true;
}
break;
}
}
}
}
public void RaiseSelectionChanged()
{
SelectionChanged?.Invoke(this, new PaintingObjectEventArgs(SelectedObjects));
}
protected void CheckMouseUp(object sender, MouseEventArgs e)
{
if (_IsAreaSelecting)
_IsAreaSelecting = false;
if (IsMovingObjects)
{
IsMovingObjects = false;
foreach (var obj in GetSelectedObjects())
obj.RaiseMoved(new EventArgs());
AutoArrangeToGrid();
}
if (curObjMouseDown is not null)
{
if (!curObjMouseDown.MouseTransparency)
curObjMouseDown.RaiseMouseUp(GetMouseEventArgs(e, curObjMouseDown));
curObjMouseDown = null;
}
if (calcOffset_IsActive)
{
calcOffset_IsActive = false;
Cursor = Cursors.Default;
CalcNewOffset(e.Location);
AfterScrollingDone?.Invoke(this, new EventArgs());
}
}
protected void CheckMouseMove(object sender, MouseEventArgs e)
{
if (_IsAreaSelecting || IsMovingObjects)
lastMousePos = new Point((int)Math.Round(e.X + Offset.X), (int)Math.Round(e.Y + Offset.Y));
if (_IsAreaSelecting)
SelectControlsInArea();
if (IsMovingObjects)
UpdateObjectPositions(e);
foreach (var obj in GetObjects(new Point((int)Math.Round(e.X + Offset.X), (int)Math.Round(e.Y + Offset.Y))))
{
if (!obj.MouseTransparency)
obj.RaiseMouseMove(GetMouseEventArgs(e, obj));
}
var topObj = GetObject(new Point((int)Math.Round(e.X + Offset.X), (int)Math.Round(e.Y + Offset.Y)), true);
if (topObj is not null)
Cursor = topObj.Cursor;
else if (calcOffset_IsActive)
{
Cursor = Cursors.Arrow;
}
else
{
Cursor = Cursors.Default;
}
if (calcOffset_IsActive)
{
if (pressedAlt)
CalcNewOffset(e.Location);
else
{
calcOffset_IsActive = false;
}
}
Refresh();
}
private void CalcNewOffset(Point newMousePos)
{
Offset = new PointF(calcOffset_LastOffset.X - (newMousePos.X - calcOffset_MouseOnTab.X), calcOffset_LastOffset.Y - (newMousePos.Y - calcOffset_MouseOnTab.Y));
if (Offset.X < 0f)
Offset = new PointF(0f, Offset.Y);
if (Offset.Y < 0f)
Offset = new PointF(Offset.X, 0f);
}
private PaintingObject[] GetSelectedObjects()
{
var objs = new List<PaintingObject>();
foreach (var obj in PaintingObjects)
{
if (obj.Selected)
objs.Add(obj);
}
return objs.ToArray();
}
private void SaveObjectPositions(MouseEventArgs e, IList objs)
{
foreach (PaintingObject obj in objs)
{
if (!obj.HardcodedLocation && !savedPos.ContainsKey(obj))
{
savedPos.Add(obj, new PointF(e.X - obj.Location.X + Offset.X, e.Y - obj.Location.Y + Offset.Y));
SaveObjectPositions(e, obj.PinnedObjects);
}
}
}
private void UpdateObjectPositions(MouseEventArgs e)
{
UpdateObjectPositions(e, GetSelectedObjects());
}
private void UpdateObjectPositions(MouseEventArgs e, IList<PaintingObject> objs, List<PaintingObject> movedObjs = null)
{
if (IsResizingObjs(objs))
return;
if (movedObjs is null)
movedObjs = new List<PaintingObject>();
SuspendDrawing();
foreach (var obj in objs)
{
var sp = savedPos[obj];
if (!movedObjs.Contains(obj))
{
if (UpdateObjectPosition(e, obj, sp))
movedObjs.Add(obj);
}
if (obj.PinnedObjects.Count > 0)
{
UpdateObjectPositions(e, obj.PinnedObjects, movedObjs);
movedObjs.AddRange(obj.PinnedObjects.ToArray());
}
}
ResumeDrawing(false);
}
private bool UpdateObjectPosition(MouseEventArgs e, PaintingObject obj, PointF sp)
{
var moved = false;
var cancel = new CancelEventArgs(false);
obj.RaiseMovingBeforePositionUpdated(cancel);
if (!cancel.Cancel)
{
obj.Location = new Point((int)Math.Round(e.X - sp.X + Offset.X), (int)Math.Round(e.Y - sp.Y + Offset.Y));
obj.RaiseMoving(new EventArgs());
moved = true;
}
return moved;
}
private bool IsResizingObjs(IList<PaintingObject> objs)
{
foreach (var obj in objs)
{
if (obj.IsResizing)
return true;
}
return false;
}
private MouseEventArgs GetMouseEventArgs(MouseEventArgs e, PaintingObject obj)
{
return new MouseEventArgs(e.Button, e.Clicks, (int)Math.Round(e.X - obj.X + Offset.X), (int)Math.Round(e.Y - obj.Y + Offset.Y), e.Delta);
}
public PaintingObject GetObject(PointF p, bool UseExtRect = false)
{
PaintingObject val = null;
for (var i = PaintingObjects.Count - 1; i >= 0; i -= 1)
{
var obj = PaintingObjects[i];
if (val is null)
{
if (UseExtRect)
{
if (HelpfulDrawingFunctions.IsPointInRectangle(p, obj.RectangleExtended))
val = obj;
}
else if (HelpfulDrawingFunctions.IsPointInRectangle(p, obj.Rectangle))
{
val = obj;
}
}
}
return val;
}
public PaintingObject[] GetObjects(Point p)
{
var objs = new List<PaintingObject>();
foreach (var obj in PaintingObjects)
{
if (HelpfulDrawingFunctions.IsPointInRectangle(p, obj.RectangleExtended))
objs.Add(obj);
}
return objs.ToArray();
}
public PaintingObject[] GetObjects(Point startPoint, Point endPoint)
{
return GetObjects(new Rectangle(startPoint, (Size)(endPoint - (Size)startPoint)));
}
public PaintingObject[] GetObjects(Rectangle rect)
{
var objs = new List<PaintingObject>();
foreach (var obj in PaintingObjects)
{
var objRect = obj.Rectangle;
if (HelpfulDrawingFunctions.IsPointInRectangle(objRect.Location, rect) || HelpfulDrawingFunctions.IsPointInRectangle(objRect.Location + objRect.Size, rect) || HelpfulDrawingFunctions.IsPointInRectangle(new PointF(objRect.Left, objRect.Bottom), rect) || HelpfulDrawingFunctions.IsPointInRectangle(new PointF(objRect.Right, objRect.Top), rect))
objs.Add(obj);
}
return objs.ToArray();
}
protected override CreateParams CreateParams
{
get
{
var cp = base.CreateParams;
// If EnableRealTransparency Then
// cp.ExStyle = cp.ExStyle Or &H20 'WS_EX_TRANSPARENT
// End If
return cp;
}
}
/// <summary>
/// Sorg dafür, dass Events durch dieses Control durchdringen zum Parnet-Control.
/// </summary>
/// <param name="m"></param>
protected override void WndProc(ref Message m)
{
const nint WM_NCHITTEST = 0x84;
const nint HTTRANSPARENT = -1;
if (!VisibleForMouseEvents && m.Msg == WM_NCHITTEST)
m.Result = HTTRANSPARENT;
else
{
base.WndProc(ref m);
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
// Stop Drawing directly to the parent
SuspendLayout();
// Draw Background
// If Not EnableRealTransparency Then
base.OnPaintBackground(e);
// End If
}
protected override void OnPaint(PaintEventArgs e)
{
// Draw PaintingObjects stuff
if (StopDrawing)
e.Graphics.DrawImage(bufferedImg, Point.Empty);
else
{
{
var withBlock = e.Graphics;
withBlock.SmoothingMode = SmoothingMode.HighQuality;
withBlock.PixelOffsetMode = PixelOffsetMode.HighQuality;
withBlock.PageUnit = GraphicsUnit.Pixel;
withBlock.InterpolationMode = InterpolationMode.HighQualityBicubic;
}
if (GridVisible)
DrawGridMethode?.Invoke(e, this, Offset);
var baserect = new RectangleF(Offset, Size);
foreach (var obj in PaintingObjects)
{
if (obj.Visible && HelpfulDrawingFunctions.OverlapsTwoRectangles(obj.Rectangle, baserect))
obj.Draw(e, Offset);
}
if (_IsAreaSelecting)
DrawAreaSelectionMethode?.Invoke(e, this, new PointF(startMousePos.X - Offset.X, startMousePos.Y - Offset.Y), new PointF(lastMousePos.X - Offset.X, lastMousePos.Y - Offset.Y));
}
// Do default Drawing Methode
base.OnPaint(e);
// Start Drawing directly to the Form
ResumeLayout(false);
}
public new Graphics CreateGraphics()
{
return base.CreateGraphics();
}
public void PaintFullView(Graphics g)
{
foreach (var obj in PaintingObjects)
{
if (obj.Visible)
obj.Draw(g, PointF.Empty);
}
}
private SizeF CalcTextSize(PaintingObject obj)
{
return CalcTextSize(obj, Parent.CreateGraphics());
}
private SizeF CalcTextSize(PaintingObject obj, Graphics g)
{
return g.MeasureString(obj.Text, obj.TextFont, (int)Math.Round(obj.Width));
}
private void SelectControlsInArea()
{
var rect = HelpfulDrawingFunctions.GetRectangle(startMousePos, lastMousePos);
foreach (var obj in PaintingObjects)
obj.Selected = startMousePos.X >= lastMousePos.X ? HelpfulDrawingFunctions.OverlapsTwoRectangles(obj.Rectangle, rect) : HelpfulDrawingFunctions.RectangleContainsRectangle(rect, obj.Rectangle);
}
public void ArrangeToGrid(PaintingObject obj, bool snapPinnedObjects)
{
if (snapPinnedObjects || !IsPinnedObject(obj))
{
var zoomedGridChunkSize = new SizeF(GridChunkSize.Width * ZoomFactor.Width, GridChunkSize.Height * ZoomFactor.Height);
var modTop = (int)Math.Round(obj.Y % zoomedGridChunkSize.Height);
var modLeft = (int)Math.Round(obj.X % zoomedGridChunkSize.Width);
var halfHeight = (int)Math.Round(zoomedGridChunkSize.Height / 2f);
var halfWidth = (int)Math.Round(zoomedGridChunkSize.Width / 2f);
void zoomLocation(PaintingObject obj2)
{
if (modTop > halfHeight)
obj2.Y += zoomedGridChunkSize.Height - modTop;
else
{
obj2.Y -= modTop;
}
if (modLeft > halfWidth)
obj2.X += zoomedGridChunkSize.Width - modLeft;
else
{
obj2.X -= modLeft;
}
};
zoomLocation(obj);
foreach (var pinned in obj.PinnedObjects)
zoomLocation(pinned);
var modH = (int)Math.Round(obj.Height % zoomedGridChunkSize.Height);
var modW = (int)Math.Round(obj.Width % zoomedGridChunkSize.Width);
void zoomSize(PaintingObject obj2) { if (obj2.EnableResize && !obj2.HardcodedSize) { if (modH > halfHeight) obj2.Height += zoomedGridChunkSize.Height - modH; else { obj2.Height -= modH; } if (modW > halfWidth) obj2.Width += zoomedGridChunkSize.Width - modW; else { obj2.Width -= modW; } } };
zoomSize(obj);
foreach (var pinned in obj.PinnedObjects)
zoomSize(pinned);
}
}
public bool IsPinnedObject(PaintingObject o)
{
foreach (var obj in PaintingObjects)
{
if (obj.PinnedObjects.Contains(o))
return true;
}
return false;
}
public void AutoArrangeToGrid()
{
if (GridEnabled)
{
foreach (var obj in GetSelectedObjects())
{
if (obj.AutoAlignToGrid)
ArrangeToGrid(obj, false);
}
if (!StopDrawing)
Refresh();
}
}
public SizeF GetFullSize()
{
return GetFullSize(PaintingObjects);
}
public static SizeF GetFullSize(IEnumerable<PaintingObject> objects)
{
var curX = 0f;
var curY = 0f;
foreach (var po in objects)
{
var myX = po.X + po.Width;
if (curX < myX)
curX = myX;
var myY = po.Y + po.Height;
if (curY < myY)
curY = myY;
}
return new SizeF(curX + 20f, curY + 20f);
}
internal void RaisePaintingObjectAdded(PaintingObjectEventArgs args)
{
PaintingObjectAdded?.Invoke(this, args);
}
internal void RaisePaintingObjectRemoved(PaintingObjectEventArgs args)
{
PaintingObjectRemoved?.Invoke(this, args);
}
private void PaintingControl_PaintingObjectAdded(object sender, PaintingObjectEventArgs e)
{
// CalculateScrollValues()
}
private void CheckMouseWheel(object sender, MouseEventArgs e)
{
if (pressedAlt)
{
var val = (float)(e.Delta / 120d / 10d);
ZoomFactor = new SizeF((float)Math.Max((double)(ZoomFactor.Width + val), 0.25d), (float)Math.Max((double)(ZoomFactor.Height + val), 0.25d));
Refresh();
}
else
{
// ...
}
}
public void SuspendDrawing()
{
if (_stopDrawing < 0)
// bufferedImg = New Bitmap(Width, Height)
// DrawToBitmap(bufferedImg, New Rectangle(0, 0, bufferedImg.Width, bufferedImg.Height))
DrawingControl.SuspendDrawing(this);
_stopDrawing += 1;
}
public void ResumeDrawing()
{
ResumeDrawing(true);
}
public void ResumeDrawing(bool executeRefresh)
{
if (_stopDrawing >= 0)
_stopDrawing -= 1;
if (_stopDrawing == -1)
// bufferedImg.Dispose()
// bufferedImg = Nothing
// If executeRefresh Then Refresh()
DrawingControl.ResumeDrawing(this, executeRefresh);
}
}

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,689 @@
Imports System.ComponentModel
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Windows.Forms
Imports Pilz.Drawing
Public Class PaintingControl
Inherits UserControl
Implements IPaintingObjectContainer
Private curObjMouseDown As PaintingObject = Nothing
Private bgColor As Color = Color.White
Private startMousePos As Point = Nothing
Private lastMousePos As Point = Nothing
Private lastHashCode As Integer = 0
Private calcOffset_MouseOnTab As Point = Point.Empty
Private calcOffset_IsActive As Boolean = False
Private calcOffset_LastOffset As PointF = PointF.Empty
Private Overloads Property ForeColor As Color
Private Overloads Property Font As Font
Private Overloads Property Text As String
Public Property Offset As PointF = PointF.Empty
Public ReadOnly Property PaintingObjects As New PaintingObjectList(Me)
Public Property VisibleForMouseEvents As Boolean = True
Public Property AutoAreaSelection As Boolean = True
Public Property AutoSingleSelection As Boolean = True
Public Property AutoMultiselection As Boolean = True
Public Property AutoRemoveSelection As Boolean = True
Public Property AreaSelectionDashStyle As DashStyle = DashStyle.DashDot
Public Property AreaSelectionColor As Color = Color.CornflowerBlue
Public Property AutoMoveObjects As Boolean = True
Private _IsAreaSelecting As Boolean = False
Public ReadOnly Property IsMovingObjects As Boolean = False
Public Property GridEnabled As Boolean = True
Public Property GridVisible As Boolean = False
Public Property GridChunkSize As New Size(20, 20)
Public Property GridColor As Color = Color.LightGray
Public Property DrawGridMethode As DelegateDrawPaintingControlGridMethode = AddressOf DefaultDrawMethodes.DrawGrid
Public Property DrawAreaSelectionMethode As DelegateDrawPaintingControlAreaSelectionMethode = AddressOf DefaultDrawMethodes.DrawAreaSelection
Private _ZoomFactor As New SizeF(1, 1)
Private _stopDrawing As Integer = -1
Private bufferedImg As Image = Nothing
Private pressedShift As Boolean = False
Private pressedControl As Boolean = False
Private pressedAlt As Boolean = False
Private savedPos As New Dictionary(Of PaintingObject, PointF)
Public Event SelectionChanged(sender As Object, e As PaintingObjectEventArgs)
Public Event PaintingObjectAdded(sender As Object, e As PaintingObjectEventArgs)
Public Event PaintingObjectRemoved(sender As Object, e As PaintingObjectEventArgs)
Public Event AfterScrollingDone(sender As Object, e As EventArgs)
Public Event ZoomFactorChanged(sender As Object, e As EventArgs)
Public ReadOnly Property SelectedObjects As PaintingObject()
Get
Dim objs As New List(Of PaintingObject)
For Each obj As PaintingObject In PaintingObjects
If obj.Selected Then
objs.Add(obj)
End If
Next
Return objs.ToArray
End Get
End Property
Public ReadOnly Property IsLayoutSuspended As Boolean
Get
Return CInt(Me.GetType.GetField("layoutSuspendCount", Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic).GetValue(Me)) <> 0
End Get
End Property
Public ReadOnly Property StopDrawing As Boolean
Get
Return _stopDrawing > -1
End Get
End Property
Public Overrides Property BackColor As Color
Get
Return bgColor
End Get
Set(value As Color)
bgColor = value
MyBase.BackColor = value
'If value <> Color.Transparent Then
' MyBase.BackColor = value
'End If
End Set
End Property
Public ReadOnly Property IsAreaSelecting As Boolean
Get
Return _IsAreaSelecting AndAlso startMousePos <> lastMousePos
End Get
End Property
Public Property ZoomFactor As SizeF
Get
Return _ZoomFactor
End Get
Set
If _ZoomFactor <> Value Then
_ZoomFactor = Value
ResetAllBufferedImages()
RaiseEvent ZoomFactorChanged(Me, New EventArgs)
End If
End Set
End Property
Public Sub New()
AddHandler PaintingObjectResizing.CheckEnabled, AddressOf PaintingObjectResizing_CheckEnabled
DoubleBuffered = True
End Sub
Protected Overrides Sub Finalize()
RemoveHandler PaintingObjectResizing.CheckEnabled, AddressOf PaintingObjectResizing_CheckEnabled
MyBase.Finalize()
End Sub
Private Sub PaintingObjectResizing_CheckEnabled(sender As PaintingObjectResizing, ByRef enabled As Boolean)
If PaintingObjects.Contains(sender.PaintingObject) AndAlso pressedAlt Then
enabled = False
End If
End Sub
Private Sub ResetAllBufferedImages()
For Each ob As PaintingObject In PaintingObjects
ob.ResetImageBuffer()
Next
Refresh()
End Sub
Protected Sub CheckKeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown, Me.KeyUp
pressedShift = e.Shift
pressedControl = e.Control
pressedAlt = e.Alt
End Sub
Friend ReadOnly Property AreaSelectionRectangle As RectangleF
Get
Return HelpfulDrawingFunctions.GetRectangle(startMousePos, lastMousePos)
End Get
End Property
Protected Sub CheckMouseClick(sender As Object, e As MouseEventArgs) Handles Me.MouseClick
For Each obj As PaintingObject In GetObjects(New Point(e.X + Offset.X, e.Y + Offset.Y))
If Not obj.MouseTransparency Then
obj.RaiseMouseClick(GetMouseEventArgs(e, obj))
End If
Next
End Sub
Protected Sub CheckMouseDown(sender As Object, e As MouseEventArgs) Handles Me.MouseDown
lastMousePos = New Point(e.X + Offset.X, e.Y + Offset.Y)
curObjMouseDown = GetObjects(lastMousePos).Where(Function(n) Not n.MouseTransparency).LastOrDefault
curObjMouseDown?.RaiseMouseDown(GetMouseEventArgs(e, curObjMouseDown))
If curObjMouseDown Is Nothing OrElse Not curObjMouseDown.Selected OrElse pressedControl Then
Dim hasMovedObjects As Boolean = False
If _IsMovingObjects Then
For Each obj As PaintingObject In GetSelectedObjects()
If HelpfulDrawingFunctions.IsPointInRectangle(lastMousePos, obj.Rectangle) Then
hasMovedObjects = True
Exit For
End If
Next
End If
If (Not hasMovedObjects) AndAlso (Not _IsAreaSelecting) Then
Dim selChanged As New List(Of PaintingObject)
If AutoRemoveSelection AndAlso Not pressedControl Then
For Each obj As PaintingObject In PaintingObjects
If obj.Selected Then
obj.SelectedDirect = False
If Not selChanged.Contains(obj) Then
selChanged.Add(obj)
End If
End If
Next
End If
If AutoSingleSelection AndAlso curObjMouseDown IsNot Nothing Then
Dim objtosel As PaintingObject = curObjMouseDown
If objtosel.EnableSelection Then
objtosel.SelectedDirect = Not objtosel.Selected
If Not selChanged.Contains(objtosel) Then
selChanged.Add(objtosel)
Else
selChanged.Remove(objtosel)
End If
End If
End If
RaiseEvent SelectionChanged(Me, New PaintingObjectEventArgs(selChanged.ToArray))
End If
End If
If pressedAlt Then
calcOffset_MouseOnTab = New Point(e.X, e.Y)
calcOffset_LastOffset = Offset
calcOffset_IsActive = True
Cursor = Cursors.Arrow
Else
Select Case e.Button
Case MouseButtons.Left
savedPos.Clear()
If AutoMoveObjects Then
SaveObjectPositions(e, GetSelectedObjects)
End If
If savedPos.Count > 0 Then
_IsMovingObjects = True
ElseIf AutoAreaSelection Then
startMousePos = New Point(e.X + Offset.X, e.Y + Offset.Y)
lastMousePos = startMousePos 'New Point(e.X - Offset.X, e.Y - Offset.Y)
_IsAreaSelecting = True
End If
End Select
End If
End Sub
Public Sub RaiseSelectionChanged()
RaiseEvent SelectionChanged(Me, New PaintingObjectEventArgs(SelectedObjects))
End Sub
Protected Sub CheckMouseUp(sender As Object, e As MouseEventArgs) Handles Me.MouseUp
If _IsAreaSelecting Then
_IsAreaSelecting = False
End If
If _IsMovingObjects Then
_IsMovingObjects = False
For Each obj As PaintingObject In GetSelectedObjects()
obj.RaiseMoved(New EventArgs)
Next
AutoArrangeToGrid()
End If
If curObjMouseDown IsNot Nothing Then
If Not curObjMouseDown.MouseTransparency Then
curObjMouseDown.RaiseMouseUp(GetMouseEventArgs(e, curObjMouseDown))
End If
curObjMouseDown = Nothing
End If
If calcOffset_IsActive Then
calcOffset_IsActive = False
Cursor = Cursors.Default
CalcNewOffset(e.Location)
RaiseEvent AfterScrollingDone(Me, New EventArgs)
End If
End Sub
Protected Sub CheckMouseMove(sender As Object, e As MouseEventArgs) Handles Me.MouseMove
If _IsAreaSelecting OrElse _IsMovingObjects Then
lastMousePos = New Point(e.X + Offset.X, e.Y + Offset.Y)
End If
If _IsAreaSelecting Then
SelectControlsInArea()
End If
If _IsMovingObjects Then
UpdateObjectPositions(e)
End If
For Each obj As PaintingObject In GetObjects(New Point(e.X + Offset.X, e.Y + Offset.Y))
If Not obj.MouseTransparency Then
obj.RaiseMouseMove(GetMouseEventArgs(e, obj))
End If
Next
Dim topObj As PaintingObject = GetObject(New Point(e.X + Offset.X, e.Y + Offset.Y), True)
If topObj IsNot Nothing Then
Cursor = topObj.Cursor
ElseIf calcOffset_IsActive Then
Cursor = Cursors.Arrow
Else
Cursor = Cursors.Default
End If
If calcOffset_IsActive Then
If pressedAlt Then
CalcNewOffset(e.Location)
Else
calcOffset_IsActive = False
End If
End If
Refresh()
End Sub
Private Sub CalcNewOffset(newMousePos As Point)
Offset = New PointF(calcOffset_LastOffset.X - (newMousePos.X - calcOffset_MouseOnTab.X),
calcOffset_LastOffset.Y - (newMousePos.Y - calcOffset_MouseOnTab.Y))
If Offset.X < 0 Then
Offset = New PointF(0, Offset.Y)
End If
If Offset.Y < 0 Then
Offset = New PointF(Offset.X, 0)
End If
End Sub
Private Function GetSelectedObjects() As PaintingObject()
Dim objs As New List(Of PaintingObject)
For Each obj As PaintingObject In PaintingObjects
If obj.Selected Then objs.Add(obj)
Next
Return objs.ToArray
End Function
Private Sub SaveObjectPositions(e As MouseEventArgs, objs As IList)
For Each obj As PaintingObject In objs
If Not obj.HardcodedLocation AndAlso Not savedPos.ContainsKey(obj) Then
savedPos.Add(obj, New PointF(e.X - obj.Location.X + Offset.X, e.Y - obj.Location.Y + Offset.Y))
SaveObjectPositions(e, obj.PinnedObjects)
End If
Next
End Sub
Private Sub UpdateObjectPositions(e As MouseEventArgs)
UpdateObjectPositions(e, GetSelectedObjects)
End Sub
Private Sub UpdateObjectPositions(e As MouseEventArgs, objs As IList(Of PaintingObject), Optional movedObjs As List(Of PaintingObject) = Nothing)
If IsResizingObjs(objs) Then Return
If movedObjs Is Nothing Then movedObjs = New List(Of PaintingObject)
SuspendDrawing()
For Each obj As PaintingObject In objs
Dim sp As PointF = savedPos(obj)
If Not movedObjs.Contains(obj) Then
If UpdateObjectPosition(e, obj, sp) Then
movedObjs.Add(obj)
End If
End If
If obj.PinnedObjects.Count > 0 Then
UpdateObjectPositions(e, obj.PinnedObjects, movedObjs)
movedObjs.AddRange(obj.PinnedObjects.ToArray)
End If
Next
ResumeDrawing(False)
End Sub
Private Function UpdateObjectPosition(e As MouseEventArgs, obj As PaintingObject, sp As PointF) As Boolean
Dim moved As Boolean = False
Dim cancel As New CancelEventArgs(False)
obj.RaiseMovingBeforePositionUpdated(cancel)
If Not cancel.Cancel Then
obj.Location = New Point(e.X - sp.X + Offset.X,
e.Y - sp.Y + Offset.Y)
obj.RaiseMoving(New EventArgs)
moved = True
End If
Return moved
End Function
Private Function IsResizingObjs(objs As IList(Of PaintingObject)) As Boolean
For Each obj As PaintingObject In objs
If obj.IsResizing Then Return True
Next
Return False
End Function
Private Function GetMouseEventArgs(e As MouseEventArgs, obj As PaintingObject) As MouseEventArgs
Return New MouseEventArgs(e.Button, e.Clicks, e.X - obj.X + Offset.X, e.Y - obj.Y + Offset.Y, e.Delta)
End Function
Public Function GetObject(p As PointF, Optional UseExtRect As Boolean = False) As PaintingObject
Dim val As PaintingObject = Nothing
For i As Integer = PaintingObjects.Count - 1 To 0 Step -1
Dim obj As PaintingObject = PaintingObjects(i)
If val Is Nothing Then
If UseExtRect Then
If HelpfulDrawingFunctions.IsPointInRectangle(p, obj.RectangleExtended) Then
val = obj
End If
Else
If HelpfulDrawingFunctions.IsPointInRectangle(p, obj.Rectangle) Then
val = obj
End If
End If
End If
Next
Return val
End Function
Public Function GetObjects(p As Point) As PaintingObject()
Dim objs As New List(Of PaintingObject)
For Each obj As PaintingObject In PaintingObjects
If HelpfulDrawingFunctions.IsPointInRectangle(p, obj.RectangleExtended) Then
objs.Add(obj)
End If
Next
Return objs.ToArray
End Function
Public Function GetObjects(startPoint As Point, endPoint As Point) As PaintingObject()
Return GetObjects(New Rectangle(startPoint, CType(endPoint - startPoint, Size)))
End Function
Public Function GetObjects(rect As Rectangle) As PaintingObject()
Dim objs As New List(Of PaintingObject)
For Each obj As PaintingObject In PaintingObjects
Dim objRect As RectangleF = obj.Rectangle
If HelpfulDrawingFunctions.IsPointInRectangle(objRect.Location, rect) OrElse
HelpfulDrawingFunctions.IsPointInRectangle(objRect.Location + objRect.Size, rect) OrElse
HelpfulDrawingFunctions.IsPointInRectangle(New PointF(objRect.Left, objRect.Bottom), rect) OrElse
HelpfulDrawingFunctions.IsPointInRectangle(New PointF(objRect.Right, objRect.Top), rect) Then
objs.Add(obj)
End If
Next
Return objs.ToArray
End Function
Protected Overrides ReadOnly Property CreateParams As CreateParams
Get
Dim cp = MyBase.CreateParams
'If EnableRealTransparency Then
' cp.ExStyle = cp.ExStyle Or &H20 'WS_EX_TRANSPARENT
'End If
Return cp
End Get
End Property
''' <summary>
''' Sorg dafür, dass Events durch dieses Control durchdringen zum Parnet-Control.
''' </summary>
''' <param name="m"></param>
Protected Overrides Sub WndProc(ByRef m As Message)
Const WM_NCHITTEST As Integer = &H84
Const HTTRANSPARENT As Integer = -1
If Not VisibleForMouseEvents AndAlso m.Msg = WM_NCHITTEST Then
m.Result = CType(HTTRANSPARENT, IntPtr)
Else
MyBase.WndProc(m)
End If
End Sub
Protected Overrides Sub OnPaintBackground(e As PaintEventArgs)
'Stop Drawing directly to the parent
Me.SuspendLayout()
'Draw Background
'If Not EnableRealTransparency Then
MyBase.OnPaintBackground(e)
'End If
End Sub
Protected Overrides Sub OnPaint(e As PaintEventArgs)
'Draw PaintingObjects stuff
If StopDrawing Then
e.Graphics.DrawImage(bufferedImg, Point.Empty)
Else
With e.Graphics
.SmoothingMode = SmoothingMode.HighQuality
.PixelOffsetMode = PixelOffsetMode.HighQuality
.PageUnit = GraphicsUnit.Pixel
.InterpolationMode = InterpolationMode.HighQualityBicubic
End With
If GridVisible Then
DrawGridMethode?.Invoke(e, Me, Offset)
End If
Dim baserect As RectangleF = New RectangleF(Offset, Size)
For Each obj As PaintingObject In PaintingObjects
If obj.Visible AndAlso HelpfulDrawingFunctions.OverlapsTwoRectangles(obj.Rectangle, baserect) Then
obj.Draw(e, Offset)
End If
Next
If _IsAreaSelecting Then
DrawAreaSelectionMethode?.Invoke(e, Me, New PointF(startMousePos.X - Offset.X, startMousePos.Y - Offset.Y), New PointF(lastMousePos.X - Offset.X, lastMousePos.Y - Offset.Y))
End If
End If
'Do default Drawing Methode
MyBase.OnPaint(e)
'Start Drawing directly to the Form
ResumeLayout(False)
End Sub
Public Overloads Function CreateGraphics() As Graphics
Return MyBase.CreateGraphics
End Function
Public Sub PaintFullView(g As Graphics)
For Each obj As PaintingObject In PaintingObjects
If obj.Visible Then
obj.Draw(g, PointF.Empty)
End If
Next
End Sub
Private Function CalcTextSize(obj As PaintingObject) As SizeF
Return CalcTextSize(obj, Parent.CreateGraphics)
End Function
Private Function CalcTextSize(obj As PaintingObject, g As Graphics) As SizeF
Return g.MeasureString(obj.Text, obj.TextFont, CInt(obj.Width))
End Function
Private Sub SelectControlsInArea()
Dim rect As RectangleF = GetRectangle(startMousePos, lastMousePos)
For Each obj As PaintingObject In PaintingObjects
obj.Selected = If(startMousePos.X >= lastMousePos.X,
OverlapsTwoRectangles(obj.Rectangle, rect),
RectangleContainsRectangle(rect, obj.Rectangle))
Next
End Sub
Public Sub ArrangeToGrid(obj As PaintingObject, snapPinnedObjects As Boolean)
If snapPinnedObjects OrElse Not IsPinnedObject(obj) Then
Dim zoomedGridChunkSize As New SizeF(GridChunkSize.Width * ZoomFactor.Width, Me.GridChunkSize.Height * ZoomFactor.Height)
Dim modTop As Integer = obj.Y Mod zoomedGridChunkSize.Height
Dim modLeft As Integer = obj.X Mod zoomedGridChunkSize.Width
Dim halfHeight As Integer = zoomedGridChunkSize.Height / 2
Dim halfWidth As Integer = zoomedGridChunkSize.Width / 2
Dim zoomLocation =
Sub(obj2 As PaintingObject)
If modTop > halfHeight Then
obj2.Y += (zoomedGridChunkSize.Height - modTop)
Else
obj2.Y -= modTop
End If
If modLeft > halfWidth Then
obj2.X += (zoomedGridChunkSize.Width - modLeft)
Else
obj2.X -= modLeft
End If
End Sub
zoomLocation(obj)
For Each pinned As PaintingObject In obj.PinnedObjects
zoomLocation(pinned)
Next
Dim modH As Integer = obj.Height Mod zoomedGridChunkSize.Height
Dim modW As Integer = obj.Width Mod zoomedGridChunkSize.Width
Dim zoomSize =
Sub(obj2 As PaintingObject)
If obj2.EnableResize AndAlso Not obj2.HardcodedSize Then
If modH > halfHeight Then
obj2.Height += (zoomedGridChunkSize.Height - modH)
Else
obj2.Height -= modH
End If
If modW > halfWidth Then
obj2.Width += (zoomedGridChunkSize.Width - modW)
Else
obj2.Width -= modW
End If
End If
End Sub
zoomSize(obj)
For Each pinned As PaintingObject In obj.PinnedObjects
zoomSize(pinned)
Next
End If
End Sub
Public Function IsPinnedObject(o As PaintingObject) As Boolean
For Each obj As PaintingObject In PaintingObjects
If obj.PinnedObjects.Contains(o) Then
Return True
End If
Next
Return False
End Function
Public Sub AutoArrangeToGrid()
If GridEnabled Then
For Each obj As PaintingObject In GetSelectedObjects()
If obj.AutoAlignToGrid Then
ArrangeToGrid(obj, False)
End If
Next
If Not StopDrawing Then Refresh()
End If
End Sub
Public Function GetFullSize() As SizeF
Return GetFullSize(PaintingObjects)
End Function
Public Shared Function GetFullSize(objects As IEnumerable(Of PaintingObject)) As SizeF
Dim curX As Single = 0
Dim curY As Single = 0
For Each po As PaintingObject In objects
Dim myX As Single = po.X + po.Width
If curX < myX Then
curX = myX
End If
Dim myY As Single = po.Y + po.Height
If curY < myY Then
curY = myY
End If
Next
Return New SizeF(curX + 20, curY + 20)
End Function
Friend Sub RaisePaintingObjectAdded(args As PaintingObjectEventArgs)
RaiseEvent PaintingObjectAdded(Me, args)
End Sub
Friend Sub RaisePaintingObjectRemoved(args As PaintingObjectEventArgs)
RaiseEvent PaintingObjectRemoved(Me, args)
End Sub
Private Sub PaintingControl_PaintingObjectAdded(sender As Object, e As PaintingObjectEventArgs) Handles Me.PaintingObjectAdded, Me.PaintingObjectRemoved
'CalculateScrollValues()
End Sub
Private Sub CheckMouseWheel(sender As Object, e As MouseEventArgs) Handles Me.MouseWheel
If pressedAlt Then
Dim val As Single = e.Delta / 120 / 10
ZoomFactor = New SizeF(Math.Max(ZoomFactor.Width + val, 0.25), Math.Max(ZoomFactor.Height + val, 0.25))
Refresh()
Else
'...
End If
End Sub
Public Sub SuspendDrawing()
If _stopDrawing < 0 Then
'bufferedImg = New Bitmap(Width, Height)
'DrawToBitmap(bufferedImg, New Rectangle(0, 0, bufferedImg.Width, bufferedImg.Height))
Utils.SuspendDrawing(Me)
End If
_stopDrawing += 1
End Sub
Public Sub ResumeDrawing()
ResumeDrawing(True)
End Sub
Public Sub ResumeDrawing(executeRefresh As Boolean)
If _stopDrawing >= 0 Then
_stopDrawing -= 1
End If
If _stopDrawing = -1 Then
'bufferedImg.Dispose()
'bufferedImg = Nothing
'If executeRefresh Then Refresh()
Utils.ResumeDrawing(Me, executeRefresh)
End If
End Sub
End Class

View File

@@ -0,0 +1,12 @@
using Pilz.UI.WinForms.PaintingControl.EventArgs;
using System.Drawing;
using System.Windows.Forms;
namespace Pilz.UI.WinForms.PaintingControl;
public delegate void DelegateDrawPaintingObjectMethode(PaintingObjectPaintEventArgs e);
public delegate void DelegateDrawPaintingControlGridMethode(PaintEventArgs e, PaintingControl pc, PointF offset);
public delegate void DelegateDrawPaintingControlAreaSelectionMethode(PaintEventArgs e, PaintingControl pc, PointF startMousePos, PointF lastMousePos);

View File

@@ -0,0 +1,6 @@
Imports System.Drawing
Imports System.Windows.Forms
Public Delegate Sub DelegateDrawPaintingObjectMethode(e As PaintingObjectPaintEventArgs)
Public Delegate Sub DelegateDrawPaintingControlGridMethode(e As PaintEventArgs, pc As PaintingControl, offset As PointF)
Public Delegate Sub DelegateDrawPaintingControlAreaSelectionMethode(e As PaintEventArgs, pc As PaintingControl, startMousePos As PointF, lastMousePos As PointF)

View File

@@ -0,0 +1,828 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
namespace Pilz.UI.WinForms.PaintingControl;
[Serializable]
public class PaintingObject : ICloneable, IPaintingObjectContainer
{
private PaintingObjectResizing resizeEngine = null;
private bool _Selected = false;
private PaintingControl _Parent = null;
public Color FillColor { get; set; } = Color.Blue;
public Color OutlineColor { get; set; } = Color.DarkBlue;
public float OutlineThicknes { get; set; } = 1f;
public DashStyle OutlineDashStyle { get; set; } = DashStyle.Solid;
public LineCapProps LineStartCap { get; set; } = null;
public LineCapProps LineEndCap { get; set; } = null;
[JsonProperty]
private string _Text = "";
public TextPosition TextPosition { get; set; } = TextPosition.FullCenter;
public StringAlignment VerticalTextAlignment { get; set; } = StringAlignment.Center;
public StringAlignment HorizontalTextAlignment { get; set; } = StringAlignment.Center;
public Font TextFont { get; set; } = new Font(FontFamily.GenericSansSerif, 8.25f);
public Color TextColor { get; set; } = Color.Black;
[JsonProperty]
private PointF _Location = new PointF(50f, 50f);
[JsonProperty]
private SizeF _Size = new SizeF(50f, 80f);
public bool EnableFill { get; set; } = true;
public bool EnableOutline { get; set; } = true;
public Color SelectionColor { get; set; } = Color.CornflowerBlue;
public DashStyle SelectionDashStyle { get; set; } = DashStyle.Dot;
[JsonProperty]
private bool _EnableSelection = true;
public Image Image { get; set; } = null;
[JsonIgnore]
public Image BufferedImage { get; set; } = null;
public ImageSizeMode ImageSizeMode { get; set; }
public PaintingObjectImageProperties ImageProperties { get; set; } = new PaintingObjectImageProperties();
[JsonIgnore]
public object Tag { get; set; } = null;
public string Name { get; set; } = "";
public List<PaintingObject> PinnedObjects { get; private set; } = new List<PaintingObject>();
[JsonIgnore]
public List<DelegateDrawPaintingObjectMethode> DrawMethodes { get; private set; } = new List<DelegateDrawPaintingObjectMethode>();
[JsonIgnore]
public DelegateDrawPaintingObjectMethode DrawSelectionMethode { get; private set; } = DefaultDrawMethodes.DrawSelection;
public Cursor Cursor { get; set; } = Cursors.Default;
public bool HardcodedSize { get; set; } = false;
public bool HardcodedLocation { get; set; } = false;
[JsonProperty]
private bool _Visible = true;
[JsonProperty]
private bool _AutoAlignToGrid = false;
public bool MouseTransparency { get; set; } = false;
public PaintingObjectLayering Layering { get; private set; }
public PaintingObjectList PaintingObjects { get; private set; }
[JsonIgnore]
public ulong ErrorsAtDrawing { get; private set; } = 0UL;
public event MouseClickEventHandler MouseClick;
public delegate void MouseClickEventHandler(PaintingObject sender, MouseEventArgs e);
public event MouseDownEventHandler MouseDown;
public delegate void MouseDownEventHandler(PaintingObject sender, MouseEventArgs e);
public event MouseUpEventHandler MouseUp;
public delegate void MouseUpEventHandler(PaintingObject sender, MouseEventArgs e);
public event MouseMoveEventHandler MouseMove;
public delegate void MouseMoveEventHandler(PaintingObject sender, MouseEventArgs e);
public event SelectedChangedEventHandler SelectedChanged;
public delegate void SelectedChangedEventHandler(PaintingObject sender, EventArgs e);
public event PaintEventHandler Paint;
public delegate void PaintEventHandler(PaintingObject sender, PaintEventArgs e);
public event ParentChangedEventHandler ParentChanged;
public delegate void ParentChangedEventHandler(PaintingObject sender, EventArgs e);
public event VisibleChangedEventHandler VisibleChanged;
public delegate void VisibleChangedEventHandler(PaintingObject sender, EventArgs e);
public event MovedEventHandler Moved;
public delegate void MovedEventHandler(PaintingObject sender, EventArgs e);
public event MovingEventHandler Moving;
public delegate void MovingEventHandler(PaintingObject sender, EventArgs e);
public event MovingBeforePositionUpdatedEventHandler MovingBeforePositionUpdated;
public delegate void MovingBeforePositionUpdatedEventHandler(PaintingObject sender, CancelEventArgs e);
public PaintingObject()
{
Layering = new(this);
PaintingObjects = new PaintingObjectList(_Parent) { EnableRaisingEvents = false };
}
public PaintingObject(PaintingObjectType @type) : this()
{
Type = type;
}
public PaintingObject(PaintingObjectType @type, DelegateDrawPaintingObjectMethode[] drawMethodes) : this(type)
{
DrawMethodes.AddRange(drawMethodes);
}
internal void RaiseMouseClick(MouseEventArgs e)
{
MouseClick?.Invoke(this, e);
}
internal void RaiseMouseDown(MouseEventArgs e)
{
MouseDown?.Invoke(this, e);
}
internal void RaiseMouseUp(MouseEventArgs e)
{
MouseUp?.Invoke(this, e);
}
internal void RaiseMouseMove(MouseEventArgs e)
{
MouseMove?.Invoke(this, e);
}
private void RaisePaint(PaintEventArgs e)
{
Paint?.Invoke(this, e);
}
internal void RaiseMoved(EventArgs e)
{
Moved?.Invoke(this, e);
}
internal void RaiseMoving(EventArgs e)
{
Moving?.Invoke(this, e);
}
internal void RaiseMovingBeforePositionUpdated(EventArgs e)
{
MovingBeforePositionUpdated?.Invoke(this, (CancelEventArgs)e);
}
public PaintingObjectType Type
{
get
{
var tt = PaintingObjectType.Custom;
foreach (var d in DrawMethodes)
{
if (ReferenceEquals(d.Method.DeclaringType, typeof(DefaultDrawMethodes)))
{
switch (d.Method.Name ?? "")
{
case "DrawPicture":
{
tt = tt | PaintingObjectType.Picture;
break;
}
case "DrawText":
{
tt = tt | PaintingObjectType.Text;
break;
}
case "DrawRectangle":
{
tt = tt | PaintingObjectType.Rectangle;
break;
}
case "DrawEllipse":
{
tt = tt | PaintingObjectType.Elipse;
break;
}
case "DrawTriangle":
{
tt = tt | PaintingObjectType.Triangle;
break;
}
case "DrawLine":
{
tt = tt | PaintingObjectType.Line;
break;
}
}
}
}
return tt;
}
set
{
DrawMethodes.Clear();
if ((value & PaintingObjectType.Picture) == PaintingObjectType.Picture)
DrawMethodes.Add(DefaultDrawMethodes.DrawPicture);
if ((value & PaintingObjectType.Rectangle) == PaintingObjectType.Rectangle)
DrawMethodes.Add(DefaultDrawMethodes.DrawRectangle);
if ((value & PaintingObjectType.Elipse) == PaintingObjectType.Elipse)
DrawMethodes.Add(DefaultDrawMethodes.DrawEllipse);
if ((value & PaintingObjectType.Triangle) == PaintingObjectType.Triangle)
DrawMethodes.Add(DefaultDrawMethodes.DrawTriangle);
if ((value & PaintingObjectType.Line) == PaintingObjectType.Line)
DrawMethodes.Add(DefaultDrawMethodes.DrawLine);
if ((value & PaintingObjectType.Text) == PaintingObjectType.Text)
DrawMethodes.Add(DefaultDrawMethodes.DrawText);
}
}
[JsonIgnore]
public PointF Location
{
get
{
if (Parent is not null)
return new PointF(_Location.X * Parent.ZoomFactor.Width, _Location.Y * Parent.ZoomFactor.Height);
else
{
return _Location;
}
}
set
{
if (Parent is not null)
_Location = new PointF(value.X / Parent.ZoomFactor.Width, value.Y / Parent.ZoomFactor.Height);
else
{
_Location = value;
}
}
}
[JsonIgnore]
public PointF LocationDirect
{
get
{
return _Location;
}
set
{
_Location = value;
}
}
[JsonIgnore]
public SizeF Size
{
get
{
if (Parent is not null)
return new SizeF(_Size.Width * Parent.ZoomFactor.Width, _Size.Height * Parent.ZoomFactor.Height);
else
{
return _Size;
}
}
set
{
if (Parent is not null)
_Size = new SizeF(value.Width / Parent.ZoomFactor.Width, value.Height / Parent.ZoomFactor.Height);
else
{
_Size = value;
}
ResetImageBuffer();
}
}
[JsonIgnore]
public SizeF SizeDirect
{
get
{
return _Size;
}
set
{
_Size = value;
ResetImageBuffer();
}
}
[JsonIgnore]
public bool AutoAlignToGrid
{
get
{
return _AutoAlignToGrid;
}
set
{
_AutoAlignToGrid = value;
if (value)
ArrangeToGrid();
}
}
[JsonIgnore]
public bool IsResizing
{
get
{
if (resizeEngine is null)
return false;
else
{
return (bool)resizeEngine?.IsResizing;
}
}
}
[JsonIgnore]
public PaintingControl Parent
{
get
{
return _Parent;
}
set
{
var re = !ReferenceEquals(value, _Parent);
_Parent = value;
if (re)
ParentChanged?.Invoke(this, new EventArgs());
}
}
[JsonIgnore]
public bool Visible
{
get
{
return _Visible;
}
set
{
if (value != _Visible)
{
_Visible = value;
if (!value && !_EnableSelection)
EnableResize = false;
VisibleChanged?.Invoke(this, new EventArgs());
}
}
}
[JsonIgnore]
public bool Selected
{
get
{
return _Selected;
}
set
{
SetSelection(value, true);
}
}
[JsonIgnore]
public bool SelectedDirect
{
get
{
return Selected;
}
set
{
SetSelection(value, false);
}
}
private void SetSelection(bool value, bool raiseEventOnParent)
{
if (EnableSelection)
{
if (_Selected != value)
{
_Selected = value;
SelectedChanged?.Invoke(this, new EventArgs());
if (raiseEventOnParent)
Parent.RaiseSelectionChanged();
}
}
else
{
_Selected = false;
}
}
[JsonIgnore]
public float Width
{
get
{
return Size.Width;
}
set
{
Size = new SizeF(value, Size.Height);
}
}
[JsonIgnore]
public float Height
{
get
{
return Size.Height;
}
set
{
Size = new SizeF(Size.Width, value);
}
}
[JsonIgnore]
public float X
{
get
{
return Location.X;
}
set
{
Location = new PointF(value, Location.Y);
}
}
[JsonIgnore]
public float Y
{
get
{
return Location.Y;
}
set
{
Location = new PointF(Location.X, value);
}
}
[JsonIgnore]
public string Text
{
get
{
return _Text;
}
set
{
_Text = value;
}
}
[JsonIgnore]
public RectangleF Rectangle
{
get
{
return new RectangleF(Location, Size);
}
set
{
Location = value.Location;
Size = value.Size;
}
}
[JsonIgnore]
public bool EnableSelection
{
get
{
return _EnableSelection;
}
set
{
_EnableSelection = value;
if (!value && !_Visible)
EnableResize = false;
if (!value)
Selected = false;
}
}
[JsonIgnore]
public Rectangle RectangleExtended
{
get
{
return new Rectangle((int)Math.Round(X - 12f), (int)Math.Round(Y - 12f), (int)Math.Round(Width + 12f + 12f), (int)Math.Round(Height + 12f + 12f));
}
set
{
X = value.X + 12;
Y = value.Y + 12;
Width = value.Width - 12 - 12;
Height = value.Height - 12 - 12;
}
}
public void FitSizeToText()
{
if (Parent is null)
throw new Exception("You have to put that PaintingObject to a PaintingControl before.");
var g = Parent.CreateGraphics();
var newSize = g.MeasureString(Text, TextFont);
SizeDirect = newSize + new SizeF(1f, 0f);
}
public void SetBounds(int x, int y, int width, int height)
{
Location = new Point(x, y);
Size = new Size(width, height);
}
[JsonIgnore]
public int Left
{
get
{
return (int)Math.Round(X);
}
set
{
X = value;
}
}
[JsonIgnore]
public int Top
{
get
{
return (int)Math.Round(Y);
}
set
{
Y = value;
}
}
[JsonIgnore]
public int Right
{
get
{
return (int)Math.Round(X + Width);
}
set
{
X = value - Width;
}
}
[JsonIgnore]
public int Bottom
{
get
{
return (int)Math.Round(Y + Height);
}
set
{
Y = value - Height;
}
}
[JsonProperty(nameof(Tag))]
public string TagString
{
get
{
if (Tag is string)
return Convert.ToString(Tag);
else
{
return string.Empty;
}
}
set
{
Tag = value;
}
}
public bool EnableResize
{
get
{
if (resizeEngine is null)
return false;
else
{
return resizeEngine.Enabled;
}
}
set
{
if (resizeEngine is null && value)
resizeEngine = new PaintingObjectResizing(this);
else if (resizeEngine is not null)
{
resizeEngine.Enabled = value;
}
}
}
public void Remove()
{
Parent?.PaintingObjects.Remove(this);
}
public void AutoArrangeToGrid()
{
if (((Parent?.GridEnabled) is var arg1 && !arg1.HasValue || arg1.Value) && AutoAlignToGrid && arg1.HasValue)
ArrangeToGrid();
}
public void ArrangeToGrid()
{
if (Parent is not null)
{
Parent.ArrangeToGrid(this, true);
if (!Parent.StopDrawing)
Parent.Refresh();
}
}
public void Draw(PaintEventArgs e)
{
Draw(e, e.ClipRectangle.Location);
}
public void Draw(PaintEventArgs e, PointF offset)
{
Draw(e.Graphics, offset);
if (Visible)
RaisePaint(e);
}
public void Draw(Graphics g, PointF offset)
{
if (Visible)
{
var poevargs = new PaintingObjectPaintEventArgs(this, g, offset);
foreach (var dm in DrawMethodes)
{
try
{
dm?.Invoke(poevargs);
}
catch (Exception ex)
{
ErrorsAtDrawing = (ulong)Math.Round(ErrorsAtDrawing + 1m);
}
}
if (Selected && DrawSelectionMethode is not null)
DrawSelectionMethode?.Invoke(poevargs);
}
}
public object Clone()
{
return Clone(true);
}
public object Clone(bool includePinnedObject)
{
var obj = new PaintingObject();
var metype = GetType();
var blackField = new[] { nameof(PinnedObjects), nameof(resizeEngine), nameof(_Parent), nameof(BufferedImage), nameof(ImageProperties) };
void copyFields(object source, object dest, string[] blackFields, Type t)
{
var fields = new List<FieldInfo>(t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.Instance));
foreach (var @field in fields)
{
if (!blackFields.Contains(@field.Name))
@field.SetValue(dest, @field.GetValue(source));
}
};
copyFields(this, obj, blackField, metype);
copyFields(ImageProperties, obj.ImageProperties, Array.Empty<string>(), ImageProperties.GetType());
if (includePinnedObject)
obj.PinnedObjects.AddRange(PinnedObjects);
obj.EnableResize = EnableResize;
return obj;
}
[Obsolete("Use Layering.BringToTop() instead!")]
public void BringToFront()
{
Layering.BringToTop();
}
[Obsolete("Use Layering.SendToBack() instead!")]
public void SendToBack()
{
Layering.SendToBack();
}
public void ResetImageBuffer()
{
BufferedImage = null;
}
}
public class PaintingObjectList : List<PaintingObject>
{
[JsonIgnore]
internal PaintingControl MyParent { get; private set; }
internal bool EnableRaisingEvents { get; set; } = true;
[JsonIgnore]
public PaintingObjectListLayering Layering { get; private set; }
public PaintingObjectList() : this(null)
{
}
public PaintingObjectList(PaintingControl parent)
{
Layering = new(this);
MyParent = parent;
}
public new void Add(PaintingObject item)
{
item.Parent = MyParent;
base.Add(item);
item.AutoArrangeToGrid();
if (EnableRaisingEvents)
MyParent?.RaisePaintingObjectAdded(new PaintingObjectEventArgs(new[] { item }));
}
public void AddRange(PaintingObject[] items)
{
foreach (var item in items)
item.Parent = MyParent;
base.AddRange(items);
foreach (var item in items)
item.AutoArrangeToGrid();
if (EnableRaisingEvents)
MyParent?.RaisePaintingObjectAdded(new PaintingObjectEventArgs(items));
}
public new void Insert(int index, PaintingObject item)
{
item.Parent = MyParent;
base.Insert(index, item);
MyParent?.AutoArrangeToGrid();
if (EnableRaisingEvents)
MyParent?.RaisePaintingObjectAdded(new PaintingObjectEventArgs(new[] { item }));
}
public new void Remove(PaintingObject item)
{
item.Parent = null;
base.Remove(item);
if (EnableRaisingEvents)
MyParent?.RaisePaintingObjectRemoved(new PaintingObjectEventArgs(new[] { item }));
}
public new void RemoveAt(int index)
{
this[index].Parent = null;
var item = this[index];
base.RemoveAt(index);
if (EnableRaisingEvents)
MyParent?.RaisePaintingObjectRemoved(new PaintingObjectEventArgs(new[] { item }));
}
}
public enum PaintingObjectType
{
Custom = 0,
Text = 1,
Picture = 2,
Line = 4,
Triangle = 8,
Rectangle = 16,
Elipse = 32
}
public enum ImageSizeMode
{
Fit,
Zoom,
Original
}
public enum TextPosition
{
HLeft = 0x1,
HRight = 0x2,
HCenter = 0x4,
VUp = 0x10,
VDown = 0x20,
VCenter = 0x40,
FullCenter = HCenter | VCenter
}

View File

@@ -0,0 +1,655 @@
Imports System.ComponentModel
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Reflection
Imports System.Windows.Forms
Imports Newtonsoft.Json
<Serializable> Public Class PaintingObject
Implements ICloneable, IPaintingObjectContainer
Private resizeEngine As PaintingObjectResizing = Nothing
Private _Selected As Boolean = False
Private _Parent As PaintingControl = Nothing
Public Property FillColor As Color = Color.Blue
Public Property OutlineColor As Color = Color.DarkBlue
Public Property OutlineThicknes As Single = 1
Public Property OutlineDashStyle As DashStyle = DashStyle.Solid
Public Property LineStartCap As LineCapProps = Nothing
Public Property LineEndCap As LineCapProps = Nothing
<JsonProperty>
Private _Text As String = ""
Public Property TextPosition As TextPosition = TextPosition.FullCenter
Public Property VerticalTextAlignment As StringAlignment = StringAlignment.Center
Public Property HorizontalTextAlignment As StringAlignment = StringAlignment.Center
Public Property TextFont As New Font(FontFamily.GenericSansSerif, 8.25)
Public Property TextColor As Color = Color.Black
<JsonProperty>
Private _Location As New PointF(50, 50)
<JsonProperty>
Private _Size As New SizeF(50, 80)
Public Property EnableFill As Boolean = True
Public Property EnableOutline As Boolean = True
Public Property SelectionColor As Color = Color.CornflowerBlue
Public Property SelectionDashStyle As DashStyle = DashStyle.Dot
<JsonProperty>
Private _EnableSelection As Boolean = True
Public Property Image As Image = Nothing
<JsonIgnore> Public Property BufferedImage As Image = Nothing
Public Property ImageSizeMode As ImageSizeMode
Public Property ImageProperties As New PaintingObjectImageProperties
<JsonIgnore>
Public Property Tag As Object = Nothing
Public Property Name As String = ""
Public ReadOnly Property PinnedObjects As New List(Of PaintingObject)
<JsonIgnore>
Public ReadOnly Property DrawMethodes As New List(Of DelegateDrawPaintingObjectMethode)
<JsonIgnore>
Public ReadOnly Property DrawSelectionMethode As DelegateDrawPaintingObjectMethode = AddressOf DefaultDrawMethodes.DrawSelection
Public Property Cursor As Cursor = Cursors.Default
Public Property HardcodedSize As Boolean = False
Public Property HardcodedLocation As Boolean = False
<JsonProperty>
Private _Visible As Boolean = True
<JsonProperty>
Private _AutoAlignToGrid As Boolean = False
Public Property MouseTransparency As Boolean = False
Public ReadOnly Property Layering As New PaintingObjectLayering(Me)
Public ReadOnly Property PaintingObjects As New PaintingObjectList(_Parent) With {.EnableRaisingEvents = False}
<JsonIgnore>
Public ReadOnly Property ErrorsAtDrawing As ULong = 0
Public Event MouseClick(sender As PaintingObject, e As MouseEventArgs)
Public Event MouseDown(sender As PaintingObject, e As MouseEventArgs)
Public Event MouseUp(sender As PaintingObject, e As MouseEventArgs)
Public Event MouseMove(sender As PaintingObject, e As MouseEventArgs)
Public Event SelectedChanged(sender As PaintingObject, e As EventArgs)
Public Event Paint(sender As PaintingObject, e As PaintEventArgs)
Public Event ParentChanged(sender As PaintingObject, e As EventArgs)
Public Event VisibleChanged(sender As PaintingObject, e As EventArgs)
Public Event Moved(sender As PaintingObject, e As EventArgs)
Public Event Moving(sender As PaintingObject, e As EventArgs)
Public Event MovingBeforePositionUpdated(sender As PaintingObject, e As CancelEventArgs)
Public Sub New()
End Sub
Public Sub New(type As PaintingObjectType)
Me.Type = type
End Sub
Public Sub New(type As PaintingObjectType, drawMethodes As DelegateDrawPaintingObjectMethode())
Me.New(type)
Me.DrawMethodes.AddRange(drawMethodes)
End Sub
Friend Sub RaiseMouseClick(e As MouseEventArgs)
RaiseEvent MouseClick(Me, e)
End Sub
Friend Sub RaiseMouseDown(e As MouseEventArgs)
RaiseEvent MouseDown(Me, e)
End Sub
Friend Sub RaiseMouseUp(e As MouseEventArgs)
RaiseEvent MouseUp(Me, e)
End Sub
Friend Sub RaiseMouseMove(e As MouseEventArgs)
RaiseEvent MouseMove(Me, e)
End Sub
Private Sub RaisePaint(e As PaintEventArgs)
RaiseEvent Paint(Me, e)
End Sub
Friend Sub RaiseMoved(e As EventArgs)
RaiseEvent Moved(Me, e)
End Sub
Friend Sub RaiseMoving(e As EventArgs)
RaiseEvent Moving(Me, e)
End Sub
Friend Sub RaiseMovingBeforePositionUpdated(e As EventArgs)
RaiseEvent MovingBeforePositionUpdated(Me, e)
End Sub
Public Property Type As PaintingObjectType
Get
Dim tt As PaintingObjectType = PaintingObjectType.Custom
For Each d As DelegateDrawPaintingObjectMethode In DrawMethodes
If d.Method.DeclaringType Is GetType(DefaultDrawMethodes) Then
Select Case d.Method.Name
Case "DrawPicture"
tt = tt Or PaintingObjectType.Picture
Case "DrawText"
tt = tt Or PaintingObjectType.Text
Case "DrawRectangle"
tt = tt Or PaintingObjectType.Rectangle
Case "DrawEllipse"
tt = tt Or PaintingObjectType.Elipse
Case "DrawTriangle"
tt = tt Or PaintingObjectType.Triangle
Case "DrawLine"
tt = tt Or PaintingObjectType.Line
End Select
End If
Next
Return tt
End Get
Set(value As PaintingObjectType)
DrawMethodes.Clear()
If (value And PaintingObjectType.Picture) = PaintingObjectType.Picture Then
DrawMethodes.Add(AddressOf DefaultDrawMethodes.DrawPicture)
End If
If (value And PaintingObjectType.Rectangle) = PaintingObjectType.Rectangle Then
DrawMethodes.Add(AddressOf DefaultDrawMethodes.DrawRectangle)
End If
If (value And PaintingObjectType.Elipse) = PaintingObjectType.Elipse Then
DrawMethodes.Add(AddressOf DefaultDrawMethodes.DrawEllipse)
End If
If (value And PaintingObjectType.Triangle) = PaintingObjectType.Triangle Then
DrawMethodes.Add(AddressOf DefaultDrawMethodes.DrawTriangle)
End If
If (value And PaintingObjectType.Line) = PaintingObjectType.Line Then
DrawMethodes.Add(AddressOf DefaultDrawMethodes.DrawLine)
End If
If (value And PaintingObjectType.Text) = PaintingObjectType.Text Then
DrawMethodes.Add(AddressOf DefaultDrawMethodes.DrawText)
End If
End Set
End Property
<JsonIgnore> Public Property Location As PointF
Get
If Parent IsNot Nothing Then
Return New PointF(_Location.X * Parent.ZoomFactor.Width,
_Location.Y * Parent.ZoomFactor.Height)
Else
Return _Location
End If
End Get
Set(value As PointF)
If Parent IsNot Nothing Then
_Location = New PointF(value.X / Parent.ZoomFactor.Width,
value.Y / Parent.ZoomFactor.Height)
Else
_Location = value
End If
End Set
End Property
<JsonIgnore> Public Property LocationDirect As PointF
Get
Return _Location
End Get
Set(value As PointF)
_Location = value
End Set
End Property
<JsonIgnore> Public Property Size As SizeF
Get
If Parent IsNot Nothing Then
Return New SizeF(_Size.Width * Parent.ZoomFactor.Width,
_Size.Height * Parent.ZoomFactor.Height)
Else
Return _Size
End If
End Get
Set(value As SizeF)
If Parent IsNot Nothing Then
_Size = New SizeF(value.Width / Parent.ZoomFactor.Width,
value.Height / Parent.ZoomFactor.Height)
Else
_Size = value
End If
ResetImageBuffer()
End Set
End Property
<JsonIgnore> Public Property SizeDirect As SizeF
Get
Return _Size
End Get
Set(value As SizeF)
_Size = value
ResetImageBuffer()
End Set
End Property
<JsonIgnore> Public Property AutoAlignToGrid As Boolean
Get
Return _AutoAlignToGrid
End Get
Set(value As Boolean)
_AutoAlignToGrid = value
If value Then ArrangeToGrid()
End Set
End Property
<JsonIgnore> Public ReadOnly Property IsResizing As Boolean
Get
If resizeEngine Is Nothing Then
Return False
Else
Return resizeEngine?.IsResizing
End If
End Get
End Property
<JsonIgnore> Public Property Parent As PaintingControl
Get
Return _Parent
End Get
Set(value As PaintingControl)
Dim re As Boolean = value IsNot _Parent
_Parent = value
If re Then RaiseEvent ParentChanged(Me, New EventArgs)
End Set
End Property
<JsonIgnore> Public Property Visible As Boolean
Get
Return _Visible
End Get
Set(value As Boolean)
If value <> _Visible Then
_Visible = value
If Not value AndAlso Not _EnableSelection Then EnableResize = False
RaiseEvent VisibleChanged(Me, New EventArgs)
End If
End Set
End Property
<JsonIgnore>
Public Property Selected As Boolean
Get
Return _Selected
End Get
Set(value As Boolean)
SetSelection(value, True)
End Set
End Property
<JsonIgnore> Public Property SelectedDirect As Boolean
Get
Return Selected
End Get
Set(value As Boolean)
SetSelection(value, False)
End Set
End Property
Private Sub SetSelection(value As Boolean, raiseEventOnParent As Boolean)
If EnableSelection Then
If _Selected <> value Then
_Selected = value
RaiseEvent SelectedChanged(Me, New EventArgs)
If raiseEventOnParent Then
Parent.RaiseSelectionChanged()
End If
End If
Else
_Selected = False
End If
End Sub
<JsonIgnore> Public Property Width As Single
Get
Return Size.Width
End Get
Set(value As Single)
Size = New SizeF(value, Size.Height)
End Set
End Property
<JsonIgnore> Public Property Height As Single
Get
Return Size.Height
End Get
Set(value As Single)
Size = New SizeF(Size.Width, value)
End Set
End Property
<JsonIgnore> Public Property X As Single
Get
Return Location.X
End Get
Set(value As Single)
Location = New PointF(value, Location.Y)
End Set
End Property
<JsonIgnore> Public Property Y As Single
Get
Return Location.Y
End Get
Set(value As Single)
Location = New PointF(Location.X, value)
End Set
End Property
<JsonIgnore> Public Property Text As String
Get
Return _Text
End Get
Set(value As String)
_Text = value
End Set
End Property
<JsonIgnore>
Public Property Rectangle As RectangleF
Get
Return New RectangleF(Location, Size)
End Get
Set(value As RectangleF)
Location = value.Location
Size = value.Size
End Set
End Property
<JsonIgnore> Public Property EnableSelection As Boolean
Get
Return _EnableSelection
End Get
Set(value As Boolean)
_EnableSelection = value
If Not value AndAlso Not _Visible Then EnableResize = False
If Not value Then Selected = False
End Set
End Property
<JsonIgnore> Public Property RectangleExtended As Rectangle
Get
Return New Rectangle(X - 12,
Y - 12,
Width + 12 + 12,
Height + 12 + 12)
End Get
Set(value As Rectangle)
X = value.X + 12
Y = value.Y + 12
Width = value.Width - 12 - 12
Height = value.Height - 12 - 12
End Set
End Property
Public Sub FitSizeToText()
If Parent Is Nothing Then
Throw New Exception("You have to put that PaintingObject to a PaintingControl before.")
End If
Dim g As Graphics = Parent.CreateGraphics()
Dim newSize As SizeF = g.MeasureString(Text, TextFont)
SizeDirect = newSize + New SizeF(1, 0)
End Sub
Public Sub SetBounds(x As Integer, y As Integer, width As Integer, height As Integer)
Location = New Point(x, y)
Size = New Size(width, height)
End Sub
<JsonIgnore> Public Property Left As Integer
Get
Return X
End Get
Set(value As Integer)
X = value
End Set
End Property
<JsonIgnore> Public Property Top() As Integer
Get
Return Y
End Get
Set(value As Integer)
Y = value
End Set
End Property
<JsonIgnore> Public Property Right As Integer
Get
Return X + Width
End Get
Set(value As Integer)
X = value - Width
End Set
End Property
<JsonIgnore> Public Property Bottom() As Integer
Get
Return Y + Height
End Get
Set(value As Integer)
Y = value - Height
End Set
End Property
<JsonProperty(NameOf(Tag))>
Public Property TagString As String
Get
If TypeOf Tag Is String Then
Return Tag
Else
Return String.Empty
End If
End Get
Set(value As String)
Tag = value
End Set
End Property
Public Property EnableResize As Boolean
Get
If resizeEngine Is Nothing Then
Return False
Else
Return resizeEngine.Enabled
End If
End Get
Set(value As Boolean)
If resizeEngine Is Nothing AndAlso value Then
resizeEngine = New PaintingObjectResizing(Me)
ElseIf resizeEngine IsNot Nothing Then
resizeEngine.Enabled = value
End If
End Set
End Property
Public Sub Remove()
Parent?.PaintingObjects.Remove(Me)
End Sub
Public Sub AutoArrangeToGrid()
If Parent?.GridEnabled AndAlso AutoAlignToGrid Then
ArrangeToGrid()
End If
End Sub
Public Sub ArrangeToGrid()
If Parent IsNot Nothing Then
Parent.ArrangeToGrid(Me, True)
If Not Parent.StopDrawing Then Parent.Refresh()
End If
End Sub
Public Sub Draw(e As PaintEventArgs)
Draw(e, e.ClipRectangle.Location)
End Sub
Public Sub Draw(e As PaintEventArgs, offset As PointF)
Draw(e.Graphics, offset)
If Visible Then
RaisePaint(e)
End If
End Sub
Public Sub Draw(g As Graphics, offset As PointF)
If Visible Then
Dim poevargs As New PaintingObjectPaintEventArgs(Me, g, offset)
For Each dm As DelegateDrawPaintingObjectMethode In DrawMethodes
Try
dm?.Invoke(poevargs)
Catch ex As Exception
_ErrorsAtDrawing += 1
End Try
Next
If Selected AndAlso DrawSelectionMethode IsNot Nothing Then
DrawSelectionMethode?.Invoke(poevargs)
End If
End If
End Sub
Public Function Clone() As Object Implements ICloneable.Clone
Return Clone(True)
End Function
Public Function Clone(includePinnedObject As Boolean) As Object
Dim obj As New PaintingObject
Dim metype As Type = Me.GetType
Dim blackField As String() = {
NameOf(_PinnedObjects),
NameOf(resizeEngine),
NameOf(_Parent),
NameOf(BufferedImage),
NameOf(_ImageProperties)
}
Dim copyFields =
Sub(source As Object, dest As Object, blackFields As String(), t As Type)
Dim fields As New List(Of FieldInfo)(t.GetFields(BindingFlags.Public Or BindingFlags.NonPublic Or BindingFlags.IgnoreCase Or BindingFlags.Instance))
For Each field As FieldInfo In fields
If Not blackFields.Contains(field.Name) Then
field.SetValue(dest, field.GetValue(source))
End If
Next
End Sub
copyFields(Me, obj, blackField, metype)
copyFields(ImageProperties, obj.ImageProperties, {}, ImageProperties.GetType)
If includePinnedObject Then
obj.PinnedObjects.AddRange(PinnedObjects)
End If
obj.EnableResize = EnableResize
Return obj
End Function
<Obsolete("Use Layering.BringToTop() instead!")>
Public Sub BringToFront()
Layering.BringToTop()
End Sub
<Obsolete("Use Layering.SendToBack() instead!")>
Public Sub SendToBack()
Layering.SendToBack()
End Sub
Public Sub ResetImageBuffer()
BufferedImage = Nothing
End Sub
End Class
Public Class PaintingObjectList
Inherits List(Of PaintingObject)
<JsonIgnore>
Friend ReadOnly Property MyParent As PaintingControl
Friend Property EnableRaisingEvents As Boolean = True
<JsonIgnore>
Public ReadOnly Property Layering As New PaintingObjectListLayering(Me)
Public Sub New()
Me.New(Nothing)
End Sub
Public Sub New(parent As PaintingControl)
MyParent = parent
End Sub
Public Overloads Sub Add(item As PaintingObject)
item.Parent = MyParent
MyBase.Add(item)
item.AutoArrangeToGrid()
If EnableRaisingEvents Then
MyParent?.RaisePaintingObjectAdded(New PaintingObjectEventArgs({item}))
End If
End Sub
Public Overloads Sub AddRange(items As PaintingObject())
For Each item As PaintingObject In items
item.Parent = MyParent
Next
MyBase.AddRange(items)
For Each item As PaintingObject In items
item.AutoArrangeToGrid()
Next
If EnableRaisingEvents Then
MyParent?.RaisePaintingObjectAdded(New PaintingObjectEventArgs(items))
End If
End Sub
Public Overloads Sub Insert(index As Integer, item As PaintingObject)
item.Parent = MyParent
MyBase.Insert(index, item)
MyParent?.AutoArrangeToGrid()
If EnableRaisingEvents Then
MyParent?.RaisePaintingObjectAdded(New PaintingObjectEventArgs({item}))
End If
End Sub
Public Overloads Sub Remove(item As PaintingObject)
item.Parent = Nothing
MyBase.Remove(item)
If EnableRaisingEvents Then
MyParent?.RaisePaintingObjectRemoved(New PaintingObjectEventArgs({item}))
End If
End Sub
Public Overloads Sub RemoveAt(index As Integer)
Me(index).Parent = Nothing
Dim item As PaintingObject = Me(index)
MyBase.RemoveAt(index)
If EnableRaisingEvents Then
MyParent?.RaisePaintingObjectRemoved(New PaintingObjectEventArgs({item}))
End If
End Sub
End Class
Public Enum PaintingObjectType
Custom = 0
Text = 1
Picture = 2
Line = 4
Triangle = 8
Rectangle = 16
Elipse = 32
End Enum
Public Enum ImageSizeMode
Fit
Zoom
Original
End Enum
Public Enum TextPosition
HLeft = &H1
HRight = &H2
HCenter = &H4
VUp = &H10
VDown = &H20
VCenter = &H40
FullCenter = HCenter Or VCenter
End Enum

View File

@@ -0,0 +1,6 @@
namespace Pilz.UI.WinForms.PaintingControl;
public class PaintingObjectJsonSerializer
{
}

View File

@@ -0,0 +1,3 @@
Public Class PaintingObjectJsonSerializer
End Class

View File

@@ -0,0 +1,112 @@
using Newtonsoft.Json;
namespace Pilz.UI.WinForms.PaintingControl;
public class PaintingObjectLayering
{
// <JsonProperty(NameOf(PaintingObject))>
private readonly PaintingObject _PaintingObject;
[JsonIgnore]
public PaintingObject PaintingObject
{
get
{
return _PaintingObject;
}
}
/// <summary>
/// Get the current object list from the painting object.
/// </summary>
/// <returns>Returns the current object list from the painting object.</returns>
[JsonIgnore]
public PaintingObjectList ObjectList
{
get
{
return PaintingObject.Parent.PaintingObjects;
}
}
/// <summary>
/// Create a new instance of object layer managing.
/// </summary>
/// <param name="obj"></param>
public PaintingObjectLayering(PaintingObject obj)
{
_PaintingObject = obj;
}
/// <summary>
/// Moves the object by the given number of indicies.
/// </summary>
/// <param name="count">The number how many objects it should be moved.</param>
public void MoveObject(int count)
{
var oldIndex = ObjectList.IndexOf(PaintingObject);
var newIndex = oldIndex + count;
MoveObjectTo(newIndex);
}
/// <summary>
/// Moves the object to the new index.
/// </summary>
/// <param name="newIndex"></param>
public void MoveObjectTo(int newIndex)
{
var list = ObjectList;
// Check & make index valid
if (newIndex >= ObjectList.Count)
newIndex = ObjectList.Count - 1;
else if (newIndex < 0)
{
newIndex = 0;
}
// Remove object
list.Remove(PaintingObject);
// Insert object at new index
list.Insert(newIndex, PaintingObject);
// Order all objects again
list.Layering.OrderAll();
}
/// <summary>
/// Moves the object to the front.
/// </summary>
public void BringToTop()
{
MoveObjectTo(ObjectList.Count - 1);
}
/// <summary>
/// Moves the object to the back.
/// </summary>
public void SendToBack()
{
MoveObjectTo(0);
}
/// <summary>
/// Moves the object fordward by one
/// </summary>
public void OneToTop()
{
MoveObject(+1);
}
/// <summary>
/// Moves the object backward by one
/// </summary>
public void OneToBack()
{
MoveObject(-1);
}
}

View File

@@ -0,0 +1,96 @@
Imports Newtonsoft.Json
Public Class PaintingObjectLayering
'<JsonProperty(NameOf(PaintingObject))>
Private ReadOnly _PaintingObject As PaintingObject
<JsonIgnore>
Public ReadOnly Property PaintingObject As PaintingObject
Get
Return _PaintingObject
End Get
End Property
''' <summary>
''' Get the current object list from the painting object.
''' </summary>
''' <returns>Returns the current object list from the painting object.</returns>
<JsonIgnore>
Public ReadOnly Property ObjectList As PaintingObjectList
Get
Return PaintingObject.Parent.PaintingObjects
End Get
End Property
''' <summary>
''' Create a new instance of object layer managing.
''' </summary>
''' <param name="obj"></param>
Public Sub New(obj As PaintingObject)
_PaintingObject = obj
End Sub
''' <summary>
''' Moves the object by the given number of indicies.
''' </summary>
''' <param name="count">The number how many objects it should be moved.</param>
Public Sub MoveObject(count As Integer)
Dim oldIndex As Integer = ObjectList.IndexOf(PaintingObject)
Dim newIndex As Integer = oldIndex + count
MoveObjectTo(newIndex)
End Sub
''' <summary>
''' Moves the object to the new index.
''' </summary>
''' <param name="newIndex"></param>
Public Sub MoveObjectTo(newIndex As Integer)
Dim list As PaintingObjectList = ObjectList
'Check & make index valid
If newIndex >= ObjectList.Count Then
newIndex = ObjectList.Count - 1
ElseIf newIndex < 0 Then
newIndex = 0
End If
'Remove object
list.Remove(PaintingObject)
'Insert object at new index
list.Insert(newIndex, PaintingObject)
'Order all objects again
list.Layering.OrderAll()
End Sub
''' <summary>
''' Moves the object to the front.
''' </summary>
Public Sub BringToTop()
MoveObjectTo(ObjectList.Count - 1)
End Sub
''' <summary>
''' Moves the object to the back.
''' </summary>
Public Sub SendToBack()
MoveObjectTo(0)
End Sub
''' <summary>
''' Moves the object fordward by one
''' </summary>
Public Sub OneToTop()
MoveObject(+1)
End Sub
''' <summary>
''' Moves the object backward by one
''' </summary>
Public Sub OneToBack()
MoveObject(-1)
End Sub
End Class

View File

@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace Pilz.UI.WinForms.PaintingControl;
public class PaintingObjectListLayering
{
public PaintingObjectList ObjectList { get; private set; }
public Dictionary<int, Func<PaintingObject, bool>> Conditions { get; private set; } = new Dictionary<int, Func<PaintingObject, bool>>();
/// <summary>
/// Get the order function will checkout the conditions.
/// </summary>
/// <returns>Returns true, if conditions are aviable, otherwise false.</returns>
public bool EnableConditions
{
get
{
return Conditions.Any();
}
}
/// <summary>
/// Create a new instance of object list layer managing.
/// </summary>
/// <param name="list"></param>
public PaintingObjectListLayering(PaintingObjectList list)
{
ObjectList = list;
}
/// <summary>
/// Order all objects using the conditions. If no conditions are setted, this method will do nothing.
/// </summary>
public void OrderAll()
{
if (EnableConditions)
OrderAllPrivate();
}
private void OrderAllPrivate()
{
var list = ObjectList;
var listOld = list.ToList();
var toRemove = new List<PaintingObject>();
// Disable raising events
ObjectList.EnableRaisingEvents = false;
// Clear list
list.Clear();
// Add ordered
foreach (var kvp in Conditions.OrderBy(n => n.Key))
{
var func = kvp.Value;
foreach (var obj in listOld)
{
if (func(obj))
{
// Add to list
list.Add(obj);
// Add to remove
toRemove.Add(obj);
}
}
// Remove remembered objects
foreach (var obj in toRemove)
listOld.Remove(obj);
toRemove.Clear();
}
// Enable raising events
ObjectList.EnableRaisingEvents = true;
// Refresh
ObjectList.MyParent?.Refresh();
}
}

View File

@@ -0,0 +1,73 @@
Public Class PaintingObjectListLayering
Public ReadOnly Property ObjectList As PaintingObjectList
Public ReadOnly Property Conditions As New Dictionary(Of Integer, Func(Of PaintingObject, Boolean))
''' <summary>
''' Get the order function will checkout the conditions.
''' </summary>
''' <returns>Returns true, if conditions are aviable, otherwise false.</returns>
Public ReadOnly Property EnableConditions As Boolean
Get
Return Conditions.Any
End Get
End Property
''' <summary>
''' Create a new instance of object list layer managing.
''' </summary>
''' <param name="list"></param>
Public Sub New(list As PaintingObjectList)
ObjectList = list
End Sub
''' <summary>
''' Order all objects using the conditions. If no conditions are setted, this method will do nothing.
''' </summary>
Public Sub OrderAll()
If EnableConditions Then
OrderAllPrivate()
End If
End Sub
Private Sub OrderAllPrivate()
Dim list As PaintingObjectList = ObjectList
Dim listOld As List(Of PaintingObject) = list.ToList
Dim toRemove As New List(Of PaintingObject)
'Disable raising events
ObjectList.EnableRaisingEvents = False
'Clear list
list.Clear()
'Add ordered
For Each kvp In Conditions.OrderBy(Function(n) n.Key)
Dim func = kvp.Value
For Each obj As PaintingObject In listOld
If func(obj) Then
'Add to list
list.Add(obj)
'Add to remove
toRemove.Add(obj)
End If
Next
'Remove remembered objects
For Each obj As PaintingObject In toRemove
listOld.Remove(obj)
Next
toRemove.Clear()
Next
'Enable raising events
ObjectList.EnableRaisingEvents = True
'Refresh
ObjectList.MyParent?.Refresh()
End Sub
End Class

View File

@@ -0,0 +1,346 @@
using Pilz.Drawing;
using System;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
namespace Pilz.UI.WinForms.PaintingControl;
[Serializable]
internal class PaintingObjectResizing
{
public static event CheckEnabledEventHandler CheckEnabled;
public delegate void CheckEnabledEventHandler(PaintingObjectResizing sender, ref bool enabled);
private PaintingObject _mObj;
private PaintingObject mObj
{
[MethodImpl(MethodImplOptions.Synchronized)]
get
{
return _mObj;
}
[MethodImpl(MethodImplOptions.Synchronized)]
set
{
if (_mObj != null)
{
_mObj.MouseDown -= mControl_MouseDown;
_mObj.MouseUp -= mControl_MouseUp;
_mObj.Paint -= mControl_Paint;
_mObj.SelectedChanged -= mControl_MouseLeave;
_mObj.ParentChanged -= mObj_ParentChanged;
}
_mObj = value;
if (_mObj != null)
{
_mObj.MouseDown += mControl_MouseDown;
_mObj.MouseUp += mControl_MouseUp;
_mObj.Paint += mControl_Paint;
_mObj.SelectedChanged += mControl_MouseLeave;
_mObj.ParentChanged += mObj_ParentChanged;
}
}
}
private Control _mObjParent;
private Control mObjParent
{
[MethodImpl(MethodImplOptions.Synchronized)]
get
{
return _mObjParent;
}
[MethodImpl(MethodImplOptions.Synchronized)]
set
{
_mObjParent = value;
}
}
private Control _mObjControl;
private Control mObjControl
{
[MethodImpl(MethodImplOptions.Synchronized)]
get
{
return _mObjControl;
}
[MethodImpl(MethodImplOptions.Synchronized)]
set
{
if (_mObjControl != null)
{
_mObjControl.MouseMove -= mControl_MouseMove;
_mObjControl.ParentChanged -= mObjParent_ParentChanged;
}
_mObjControl = value;
if (_mObjControl != null)
{
_mObjControl.MouseMove += mControl_MouseMove;
_mObjControl.ParentChanged += mObjParent_ParentChanged;
}
}
}
private bool mMouseDown = false;
private EdgeEnum mEdge = EdgeEnum.None;
private int mWidth = 4;
private int qWidth = 4 * 4;
private Rectangle rect = new Rectangle();
public bool Enabled { get; set; } = true;
public SizeF MinimumSize { get; set; } = new SizeF(15f, 15f);
[Serializable]
private enum EdgeEnum
{
None,
Right,
Left,
Top,
Bottom,
TopLeft,
TopRight,
BottomLeft,
BottomRight
}
public bool IsResizing
{
get
{
return mMouseDown && mEdge != EdgeEnum.None;
}
}
public PaintingObject PaintingObject
{
get
{
return mObj;
}
}
public PaintingObjectResizing(PaintingObject obj)
{
mObjParent = null;
mObjControl = null;
mObj = obj;
mObjControl = mObj.Parent;
}
private bool IsEnabled()
{
var enabled = Enabled;
CheckEnabled?.Invoke(this, ref enabled);
return enabled;
}
private void mControl_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
mMouseDown = true;
}
private void mControl_MouseUp(object sender, MouseEventArgs e)
{
mMouseDown = false;
if (mObj.Selected)
mObj.AutoArrangeToGrid();
}
private void KeepInRange(ref SizeF size)
{
if (size.Height < MinimumSize.Height || size.Width < MinimumSize.Width)
size = new SizeF(Math.Max(size.Width, MinimumSize.Width), Math.Max(size.Height, MinimumSize.Height));
}
private void mControl_MouseMove(object sender, MouseEventArgs e)
{
if (mMouseDown && mEdge != EdgeEnum.None)
{
var eX = (int)Math.Round(e.X + mObj.Parent.Offset.X);
var eY = (int)Math.Round(e.Y + mObj.Parent.Offset.Y);
switch (mEdge)
{
case EdgeEnum.TopLeft:
{
mObj.SetBounds(eX, eY, (int)Math.Round(mObj.Width + (mObj.Left - eX)), (int)Math.Round(mObj.Height + (mObj.Top - eY)));
break;
}
case EdgeEnum.TopRight:
{
mObj.SetBounds(mObj.Left, eY, eX - mObj.Left, (int)Math.Round(mObj.Height + (mObj.Top - eY)));
break;
}
case EdgeEnum.BottomRight:
{
mObj.SetBounds(mObj.Left, mObj.Top, eX - mObj.Left, eY - mObj.Top);
break;
}
case EdgeEnum.BottomLeft:
{
mObj.SetBounds(eX, mObj.Top, (int)Math.Round(mObj.Width + (mObj.Left - eX)), eY - mObj.Top);
break;
}
case EdgeEnum.Left:
{
mObj.SetBounds(eX, mObj.Top, (int)Math.Round(mObj.Width + (mObj.Left - eX)), (int)Math.Round(mObj.Height));
break;
}
case EdgeEnum.Right:
{
mObj.SetBounds(mObj.Left, mObj.Top, eX - mObj.Left, (int)Math.Round(mObj.Height));
break;
}
case EdgeEnum.Top:
{
mObj.SetBounds(mObj.Left, eY, (int)Math.Round(mObj.Width), (int)Math.Round(mObj.Height + (mObj.Top - eY)));
break;
}
case EdgeEnum.Bottom:
{
mObj.SetBounds(mObj.Left, mObj.Top, (int)Math.Round(mObj.Width), eY - mObj.Top);
break;
}
}
var argsize = mObj.Size;
KeepInRange(ref argsize);
mObj.Size = argsize;
}
else if (!mMouseDown)
{
var eXo = e.X;
var eYo = e.Y;
var realX = (int)Math.Round(eXo + mObj.Parent.Offset.X);
var realY = (int)Math.Round(eYo + mObj.Parent.Offset.Y);
var eXwo = (int)Math.Round(eXo - mObj.X);
var eYwo = (int)Math.Round(eYo - mObj.Y);
var eX = (int)Math.Round(eXwo + mObj.Parent.Offset.X);
var eY = (int)Math.Round(eYwo + mObj.Parent.Offset.Y);
var eLocation = new Point(eX, eY);
RectangleF extRect = mObj.RectangleExtended;
var oldRect = mObj.Rectangle;
var newRect = new RectangleF();
newRect.X = extRect.X - oldRect.X;
newRect.Y = extRect.Y - oldRect.Y;
newRect.Width = (extRect.Width - oldRect.Width) / 2f;
newRect.Height = (extRect.Height - oldRect.Height) / 2f;
var setToNone = false;
var isOnTop = ReferenceEquals(mObj.Parent.GetObject(new PointF(realX, realY), true), mObj);
if (IsEnabled() && isOnTop)
{
switch (true)
{
case object _ when HelpfulDrawingFunctions.IsPointInRectangle(eLocation, new Rectangle((int)Math.Round(newRect.X), (int)Math.Round(newRect.Y), (int)Math.Round(newRect.Width), (int)Math.Round(newRect.Height))):
{
mObj.Cursor = Cursors.SizeNWSE;
mEdge = EdgeEnum.TopLeft;
break;
}
case object _ when HelpfulDrawingFunctions.IsPointInRectangle(eLocation, new Rectangle((int)Math.Round(mObj.Width), (int)Math.Round(newRect.Y), (int)Math.Round(newRect.Width), (int)Math.Round(newRect.Height))):
{
mObj.Cursor = Cursors.SizeNESW;
mEdge = EdgeEnum.TopRight;
break;
}
case object _ when HelpfulDrawingFunctions.IsPointInRectangle(eLocation, new Rectangle((int)Math.Round(mObj.Width), (int)Math.Round(mObj.Height), (int)Math.Round(newRect.Width), (int)Math.Round(newRect.Height))):
{
mObj.Cursor = Cursors.SizeNWSE;
mEdge = EdgeEnum.BottomRight;
break;
}
case object _ when HelpfulDrawingFunctions.IsPointInRectangle(eLocation, new Rectangle((int)Math.Round(newRect.X), (int)Math.Round(mObj.Height), (int)Math.Round(newRect.Width), (int)Math.Round(newRect.Height))):
{
mObj.Cursor = Cursors.SizeNESW;
mEdge = EdgeEnum.BottomLeft;
break;
}
case object _ when HelpfulDrawingFunctions.IsPointInRectangle(eLocation, new Rectangle((int)Math.Round(-newRect.Width), 0, (int)Math.Round(newRect.Width), (int)Math.Round(mObj.Height))):
{
mObj.Cursor = Cursors.SizeWE;
mEdge = EdgeEnum.Left;
break;
}
case object _ when HelpfulDrawingFunctions.IsPointInRectangle(eLocation, new Rectangle((int)Math.Round(mObj.Width), 0, (int)Math.Round(newRect.Width), (int)Math.Round(mObj.Height))):
{
mObj.Cursor = Cursors.SizeWE;
mEdge = EdgeEnum.Right;
break;
}
case object _ when HelpfulDrawingFunctions.IsPointInRectangle(eLocation, new Rectangle(0, (int)Math.Round(-newRect.Height), (int)Math.Round(mObj.Width), (int)Math.Round(newRect.Height))):
{
mObj.Cursor = Cursors.SizeNS;
mEdge = EdgeEnum.Top;
break;
}
case object _ when HelpfulDrawingFunctions.IsPointInRectangle(eLocation, new Rectangle(0, (int)Math.Round(mObj.Height), (int)Math.Round(mObj.Width), (int)Math.Round(newRect.Height))):
{
mObj.Cursor = Cursors.SizeNS;
mEdge = EdgeEnum.Bottom;
break;
}
default:
{
setToNone = true;
break;
}
}
}
else
{
setToNone = true;
}
if (setToNone)
{
mObj.Cursor = Cursors.Default;
mEdge = EdgeEnum.None;
}
}
}
private void mControl_Paint(PaintingObject sender, PaintEventArgs e)
{
// e.Graphics.FillRectangle(brush, rect)
}
private void mControl_MouseLeave(PaintingObject sender, EventArgs e)
{
if (!sender.Selected)
mEdge = EdgeEnum.None;
}
private void mObjParent_ParentChanged(object sender, EventArgs e)
{
mObjParent = mObjControl.Parent;
}
private void mObj_ParentChanged(PaintingObject sender, EventArgs e)
{
mObjControl = mObj.Parent;
mObjParent = mObjControl?.Parent;
}
}

View File

@@ -0,0 +1,189 @@
Imports System.Drawing
Imports System.Windows.Forms
Imports Pilz.Drawing
<Serializable> Friend Class PaintingObjectResizing
Public Shared Event CheckEnabled(sender As PaintingObjectResizing, ByRef enabled As Boolean)
Private WithEvents mObj As PaintingObject
Private WithEvents mObjParent As Control = Nothing
Private WithEvents mObjControl As Control = Nothing
Private mMouseDown As Boolean = False
Private mEdge As EdgeEnum = EdgeEnum.None
Private mWidth As Integer = 4
Private qWidth As Integer = 4 * 4
Private rect As New Rectangle
Public Property Enabled As Boolean = True
Public Property MinimumSize As New SizeF(15, 15)
<Serializable> Private Enum EdgeEnum
None
Right
Left
Top
Bottom
TopLeft
TopRight
BottomLeft
BottomRight
End Enum
Public ReadOnly Property IsResizing As Boolean
Get
Return mMouseDown AndAlso mEdge <> EdgeEnum.None
End Get
End Property
Public ReadOnly Property PaintingObject As PaintingObject
Get
Return mObj
End Get
End Property
Public Sub New(obj As PaintingObject)
mObj = obj
mObjControl = mObj.Parent
End Sub
Private Function IsEnabled() As Boolean
Dim enabled = Me.Enabled
RaiseEvent CheckEnabled(Me, enabled)
Return enabled
End Function
Private Sub mControl_MouseDown(sender As Object, e As MouseEventArgs) Handles mObj.MouseDown
If e.Button = System.Windows.Forms.MouseButtons.Left Then
mMouseDown = True
End If
End Sub
Private Sub mControl_MouseUp(sender As Object, e As MouseEventArgs) Handles mObj.MouseUp
mMouseDown = False
If mObj.Selected Then
mObj.AutoArrangeToGrid()
End If
End Sub
Private Sub KeepInRange(ByRef size As SizeF)
If size.Height < MinimumSize.Height OrElse size.Width < MinimumSize.Width Then
size = New SizeF(Math.Max(size.Width, MinimumSize.Width),
Math.Max(size.Height, MinimumSize.Height))
End If
End Sub
Private Sub mControl_MouseMove(sender As Object, e As MouseEventArgs) Handles mObjControl.MouseMove
If mMouseDown AndAlso mEdge <> EdgeEnum.None Then
Dim eX As Integer = e.X + mObj.Parent.Offset.X
Dim eY As Integer = e.Y + mObj.Parent.Offset.Y
Select Case mEdge
Case EdgeEnum.TopLeft
mObj.SetBounds(eX, eY, mObj.Width + (mObj.Left - eX), mObj.Height + (mObj.Top - eY))
Case EdgeEnum.TopRight
mObj.SetBounds(mObj.Left, eY, eX - mObj.Left, mObj.Height + (mObj.Top - eY))
Case EdgeEnum.BottomRight
mObj.SetBounds(mObj.Left, mObj.Top, eX - mObj.Left, eY - mObj.Top)
Case EdgeEnum.BottomLeft
mObj.SetBounds(eX, mObj.Top, mObj.Width + (mObj.Left - eX), eY - mObj.Top)
Case EdgeEnum.Left
mObj.SetBounds(eX, mObj.Top, mObj.Width + (mObj.Left - eX), mObj.Height)
Case EdgeEnum.Right
mObj.SetBounds(mObj.Left, mObj.Top, eX - mObj.Left, mObj.Height)
Case EdgeEnum.Top
mObj.SetBounds(mObj.Left, eY, mObj.Width, mObj.Height + (mObj.Top - eY))
Case EdgeEnum.Bottom
mObj.SetBounds(mObj.Left, mObj.Top, mObj.Width, eY - mObj.Top)
End Select
KeepInRange(mObj.Size)
ElseIf Not mMouseDown Then
Dim eXo As Integer = e.X
Dim eYo As Integer = e.Y
Dim realX As Integer = eXo + mObj.Parent.Offset.X
Dim realY As Integer = eYo + mObj.Parent.Offset.Y
Dim eXwo As Integer = eXo - mObj.X
Dim eYwo As Integer = eYo - mObj.Y
Dim eX As Integer = eXwo + mObj.Parent.Offset.X
Dim eY As Integer = eYwo + mObj.Parent.Offset.Y
Dim eLocation As New Point(eX, eY)
Dim extRect As RectangleF = mObj.RectangleExtended
Dim oldRect As RectangleF = mObj.Rectangle
Dim newRect As New RectangleF
newRect.X = extRect.X - oldRect.X
newRect.Y = extRect.Y - oldRect.Y
newRect.Width = (extRect.Width - oldRect.Width) / 2
newRect.Height = (extRect.Height - oldRect.Height) / 2
Dim setToNone As Boolean = False
Dim isOnTop As Boolean = mObj.Parent.GetObject(New PointF(realX, realY), True) Is mObj
If IsEnabled() AndAlso isOnTop Then
Select Case True
Case HelpfulDrawingFunctions.IsPointInRectangle(eLocation, New Rectangle(newRect.X, newRect.Y, newRect.Width, newRect.Height))
mObj.Cursor = Cursors.SizeNWSE
mEdge = EdgeEnum.TopLeft
Case HelpfulDrawingFunctions.IsPointInRectangle(eLocation, New Rectangle(mObj.Width, newRect.Y, newRect.Width, newRect.Height))
mObj.Cursor = Cursors.SizeNESW
mEdge = EdgeEnum.TopRight
Case HelpfulDrawingFunctions.IsPointInRectangle(eLocation, New Rectangle(mObj.Width, mObj.Height, newRect.Width, newRect.Height))
mObj.Cursor = Cursors.SizeNWSE
mEdge = EdgeEnum.BottomRight
Case HelpfulDrawingFunctions.IsPointInRectangle(eLocation, New Rectangle(newRect.X, mObj.Height, newRect.Width, newRect.Height))
mObj.Cursor = Cursors.SizeNESW
mEdge = EdgeEnum.BottomLeft
Case HelpfulDrawingFunctions.IsPointInRectangle(eLocation, New Rectangle(-newRect.Width, 0, newRect.Width, mObj.Height))
mObj.Cursor = Cursors.SizeWE
mEdge = EdgeEnum.Left
Case HelpfulDrawingFunctions.IsPointInRectangle(eLocation, New Rectangle(mObj.Width, 0, newRect.Width, mObj.Height))
mObj.Cursor = Cursors.SizeWE
mEdge = EdgeEnum.Right
Case HelpfulDrawingFunctions.IsPointInRectangle(eLocation, New Rectangle(0, -newRect.Height, mObj.Width, newRect.Height))
mObj.Cursor = Cursors.SizeNS
mEdge = EdgeEnum.Top
Case HelpfulDrawingFunctions.IsPointInRectangle(eLocation, New Rectangle(0, mObj.Height, mObj.Width, newRect.Height))
mObj.Cursor = Cursors.SizeNS
mEdge = EdgeEnum.Bottom
Case Else
setToNone = True
End Select
Else
setToNone = True
End If
If setToNone Then
mObj.Cursor = Cursors.Default
mEdge = EdgeEnum.None
End If
End If
End Sub
Private Sub mControl_Paint(sender As PaintingObject, e As PaintEventArgs) Handles mObj.Paint
'e.Graphics.FillRectangle(brush, rect)
End Sub
Private Sub mControl_MouseLeave(ByVal sender As PaintingObject, ByVal e As EventArgs) Handles mObj.SelectedChanged
If Not sender.Selected Then
mEdge = EdgeEnum.None
End If
End Sub
Private Sub mObjParent_ParentChanged(sender As Object, e As EventArgs) Handles mObjControl.ParentChanged
mObjParent = mObjControl.Parent
End Sub
Private Sub mObj_ParentChanged(sender As PaintingObject, e As EventArgs) Handles mObj.ParentChanged
mObjControl = mObj.Parent
mObjParent = mObjControl?.Parent
End Sub
End Class

View File

@@ -0,0 +1,52 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<MyType>Windows</MyType>
<TargetFrameworks>net8.0-windows</TargetFrameworks>
<LangVersion>latest</LangVersion>
<DocumentationFile>Pilz.UI.xml</DocumentationFile>
<UseWindowsForms>true</UseWindowsForms>
<DefaultItemExcludes>$(DefaultItemExcludes);$(ProjectDir)**\*.vb</DefaultItemExcludes>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>annotations</Nullable>
</PropertyGroup>
<PropertyGroup>
<Version>2.6.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
<Import Include="Microsoft.VisualBasic" />
<Import Include="System" />
<Import Include="System.Collections" />
<Import Include="System.Collections.Generic" />
<Import Include="System.Data" />
<Import Include="System.Diagnostics" />
<Import Include="System.Linq" />
<Import Include="System.Xml.Linq" />
<Import Include="System.Threading.Tasks" />
</ItemGroup>
<ItemGroup>
<Compile Update="PaintingControl\PaintingControl.cs" />
<Compile Update="Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<CustomToolNamespace>Pilz.UI.My.Resources</CustomToolNamespace>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Update="PaintingControl\PaintingControl.resx">
<DependentUpon>PaintingControl.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Pilz.Drawing\Pilz.Drawing.vbproj" />
<ProjectReference Include="..\Pilz.Win32\Pilz.Win32.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,104 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<MyType>Windows</MyType>
<TargetFrameworks>net6.0-windows;net8.0-windows</TargetFrameworks>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
<DocumentationFile>Pilz.UI.xml</DocumentationFile>
<DefineTrace>true</DefineTrace>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DefineDebug>true</DefineDebug>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DefineDebug>false</DefineDebug>
<RemoveIntegerChecks>true</RemoveIntegerChecks>
</PropertyGroup>
<PropertyGroup>
<OptionExplicit>On</OptionExplicit>
</PropertyGroup>
<PropertyGroup>
<OptionCompare>Binary</OptionCompare>
</PropertyGroup>
<PropertyGroup>
<OptionStrict>Off</OptionStrict>
</PropertyGroup>
<PropertyGroup>
<OptionInfer>On</OptionInfer>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DefineDebug>true</DefineDebug>
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<UseVSHostingProcess>true</UseVSHostingProcess>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
<RemoveIntegerChecks>true</RemoveIntegerChecks>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup>
<Version>2.0.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
<Import Include="Microsoft.VisualBasic" />
<Import Include="System" />
<Import Include="System.Collections" />
<Import Include="System.Collections.Generic" />
<Import Include="System.Data" />
<Import Include="System.Diagnostics" />
<Import Include="System.Linq" />
<Import Include="System.Xml.Linq" />
<Import Include="System.Threading.Tasks" />
</ItemGroup>
<ItemGroup>
<Compile Update="PaintingControl\PaintingControl.vb" />
<Compile Update="My Project\Application.Designer.vb">
<AutoGen>True</AutoGen>
<DependentUpon>Application.myapp</DependentUpon>
</Compile>
<Compile Update="My Project\Resources.Designer.vb">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Update="My Project\Settings.Designer.vb">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="My Project\Resources.resx">
<Generator>VbMyResourcesResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.vb</LastGenOutput>
<CustomToolNamespace>My.Resources</CustomToolNamespace>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Update="PaintingControl\PaintingControl.resx">
<DependentUpon>PaintingControl.vb</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="My Project\Application.myapp">
<Generator>MyApplicationCodeGenerator</Generator>
<LastGenOutput>Application.Designer.vb</LastGenOutput>
</None>
<None Include="My Project\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<CustomToolNamespace>My</CustomToolNamespace>
<LastGenOutput>Settings.Designer.vb</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Pilz.Drawing\Pilz.Drawing.vbproj" />
<ProjectReference Include="..\Pilz.Win32\Pilz.Win32.vbproj" />
</ItemGroup>
</Project>

63
Pilz.UI.WinForms/Resources.Designer.cs generated Normal file
View File

@@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// Dieser Code wurde von einem Tool generiert.
// Laufzeitversion:4.0.30319.42000
//
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
// der Code erneut generiert wird.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Pilz.UI.My.Resources {
using System;
/// <summary>
/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
/// </summary>
// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Pilz.UI.WinForms.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
/// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,12 @@
using System.Reflection;
namespace Pilz.UI.WinForms.Symbols;
public interface ISymbolFactory<TSymbols>
{
Image? GetImage(TSymbols svgImage, Size size);
Image? GetImage(TSymbols svgImage, SymbolSize size);
Assembly GetImageResourceAssembly();
string GetImageRessourcePath(TSymbols svgImage);
Stream? GetImageRessourceStream(TSymbols svgImage);
}

View File

@@ -0,0 +1,54 @@
using System.Reflection;
namespace Pilz.UI.WinForms.Symbols;
public abstract class SymbolFactory<TSymbols> : ISymbolFactory<TSymbols>
{
public abstract string GetImageRessourcePath(TSymbols svgImage);
public abstract Assembly GetImageResourceAssembly();
protected virtual Size ResolveCommonSize(SymbolSize size)
{
return size switch
{
SymbolSize.Default => Size.Empty,
SymbolSize.Small => new Size(16, 16),
SymbolSize.Medium => new Size(20, 20),
SymbolSize.Large => new Size(32, 32),
_ => new Size((int)size, (int)size),
};
}
public virtual Stream? GetImageRessourceStream(TSymbols svgImage)
{
var asm = GetImageResourceAssembly();
var path = GetImageRessourcePath(svgImage);
return asm.GetManifestResourceStream(path);
}
public virtual Image? GetImage(TSymbols svgImage, SymbolSize size)
{
return GetImage(svgImage, ResolveCommonSize(size));
}
public virtual Image? GetImage(TSymbols svgImage, Size size)
{
using var stream = GetImageRessourceStream(svgImage);
if (stream is null)
return null;
var img = Image.FromStream(stream);
if (!size.IsEmpty)
{
var img2 = new Bitmap(size.Width, size.Height);
using var g = Graphics.FromImage(img2);
g.DrawImage(img2, 0, 0, size.Width, size.Height);
img.Dispose();
img = img2;
}
return img;
}
}

View File

@@ -0,0 +1,72 @@
namespace Pilz.UI.WinForms.Symbols;
public enum SymbolSize
{
/// <summary>
/// Don't resize and just returns the original size.
/// </summary>
Default,
/// <summary>
/// Resizes the symbol to 16 x 16 pixels.
/// <br/><b>Deprecated!</b> This is just present due legacy reasons. Use <see cref="x16"/> instead.
/// </summary>
Small,
/// <summary>
/// Resizes the symbol to 20 x 20 pixels.
/// <br/><b>Deprecated!</b> This is just present due legacy reasons. Use <see cref="x20"/> instead.
/// </summary>
Medium,
/// <summary>
/// Resizes the symbol to 32 x 32 pixels.
/// <br/><b>Deprecated!</b> This is just present due legacy reasons. Use <see cref="x32"/> instead.
/// </summary>
Large,
/// <summary>
/// Resizes the symbol to 8 x 8 pixels.
/// </summary>
x8 = 8,
/// <summary>
/// Resizes the symbol to 12 x 12 pixels.
/// </summary>
x12 = 12,
/// <summary>
/// Resizes the symbol to 16 x 16 pixels.
/// </summary>
x16 = 16,
/// <summary>
/// Resizes the symbol to 20 x 20 pixels.
/// </summary>
x20 = 20,
/// <summary>
/// Resizes the symbol to 32 x 32 pixels.
/// </summary>
x32 = 32,
/// <summary>
/// Resizes the symbol to 48 x 48 pixels.
/// </summary>
x48 = 48,
/// <summary>
/// Resizes the symbol to 64 x 64 pixels.
/// </summary>
x64 = 64,
/// <summary>
/// Resizes the symbol to 96 x 96 pixels.
/// </summary>
x96 = 96,
/// <summary>
/// Resizes the symbol to 128 x 128 pixels.
/// </summary>
x128 = 128,
/// <summary>
/// Resizes the symbol to 192 x 192 pixels.
/// </summary>
x192 = 192,
/// <summary>
/// Resizes the symbol to 256 x 256 pixels.
/// </summary>
x256 = 256,
/// <summary>
/// Resizes the symbol to 512 x 512 pixels.
/// </summary>
x512 = 512,
}

View File

@@ -0,0 +1,50 @@
using System.Collections.Generic;
using System.Windows.Forms;
namespace Pilz.UI.WinForms.Utilities;
public static class DrawingControl
{
private const int WM_SETREDRAW = 11;
private readonly static Dictionary<nint, int> dicSuspendCount = new Dictionary<nint, int>();
public static void SuspendDrawing(this Control control)
{
if (!dicSuspendCount.ContainsKey(control.Handle))
dicSuspendCount.Add(control.Handle, 1);
else
{
dicSuspendCount[control.Handle] += 1;
}
if (dicSuspendCount[control.Handle] == 1)
User32Bridge.SendMessage(control.Handle, WM_SETREDRAW, false, 0);
}
public static void ResumeDrawing(this Control control)
{
control.ResumeDrawing(true);
}
public static void ResumeDrawing(this Control control, bool redraw)
{
var doRedraw = true;
if (dicSuspendCount.ContainsKey(control.Handle))
{
dicSuspendCount[control.Handle] -= 1;
if (dicSuspendCount[control.Handle] >= 1)
doRedraw = false;
}
if (doRedraw)
{
User32Bridge.SendMessage(control.Handle, WM_SETREDRAW, true, 0);
if (redraw)
control.Refresh();
}
}
}

View File

@@ -0,0 +1,48 @@
Imports System.Runtime.CompilerServices
Imports System.Windows.Forms
Namespace Utils
Public Module DrawingControl
Private Const WM_SETREDRAW = 11
Private ReadOnly dicSuspendCount As New Dictionary(Of IntPtr, Integer)
<Extension>
Public Sub SuspendDrawing(control As Control)
If Not dicSuspendCount.ContainsKey(control.Handle) Then
dicSuspendCount.Add(control.Handle, 1)
Else
dicSuspendCount(control.Handle) += 1
End If
If dicSuspendCount(control.Handle) = 1 Then
SendMessage(control.Handle, WM_SETREDRAW, False, 0)
End If
End Sub
<Extension>
Public Sub ResumeDrawing(control As Control)
ResumeDrawing(control, True)
End Sub
<Extension>
Public Sub ResumeDrawing(control As Control, redraw As Boolean)
Dim doRedraw As Boolean = True
If dicSuspendCount.ContainsKey(control.Handle) Then
dicSuspendCount(control.Handle) -= 1
If dicSuspendCount(control.Handle) >= 1 Then
doRedraw = False
End If
End If
If doRedraw Then
SendMessage(control.Handle, WM_SETREDRAW, True, 0)
If redraw Then control.Refresh()
End If
End Sub
End Module
End Namespace

View File

@@ -0,0 +1,9 @@
using System.Runtime.InteropServices;
namespace Pilz.UI.WinForms.Utilities;
public static class User32Bridge
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern int SendMessage(nint hWnd, int Msg, bool wParam, int lParam);
}

View File

@@ -0,0 +1,5 @@
Public Module User32Bridge
Friend Declare Auto Function SendMessage Lib "user32.dll" (hWnd As IntPtr, Msg As Integer, wParam As Boolean, lParam As Integer) As Integer
End Module