using System.Collections.Generic; using System.Drawing; namespace System.Windows.Forms { internal class LayoutEngine { private Control control; private List transactions = new(); private bool suspended; private bool updatingLayout; public void SuspendLayout() { suspended = true; } public void AddTransaction(int deltaX, int deltaY) { if (updatingLayout) return; int width = control.DisplayRectangle.Width; int height = control.DisplayRectangle.Height; if (suspended) transactions.Add(new LayoutTransaction(deltaX, deltaY, width, height)); else UpdateLayout(deltaX, deltaY, width, height); } public void AddTransaction() => AddTransaction(0, 0); public void ResumeLayout(bool resume) { if (resume) { foreach (LayoutTransaction tr in transactions) { UpdateLayout(tr.DeltaX, tr.DeltaY, tr.Width, tr.Height); } } transactions.Clear(); suspended = false; } private Rectangle Bounds(int left, int top, int width, int height) { if (left < 0) left = 0; if (top < 0) top = 0; if (width < 0) width = 0; if (height < 0) height = 0; return new Rectangle(left, top, width, height); } private void UpdateLayout(int dx, int dy, int width, int height) { if (updatingLayout) return; updatingLayout = true; try { Rectangle remainingBounds = new Rectangle(control.Padding.Left + control.DefaultPadding.Left, control.Padding.Top + control.DefaultPadding.Top, width - control.Padding.Horizontal - control.DefaultPadding.Horizontal, height - control.Padding.Vertical - control.DefaultPadding.Vertical); remainingBounds.Width += dx; remainingBounds.Height += dy; // phase 1: everything except Dock = Fill for (int i = control.Controls.Count - 1; i >= 0; i--) { var c = control.Controls[i]; if ((c.Anchor & AnchorStyles.Right) != 0) { if ((c.Anchor & AnchorStyles.Left) != 0) c.Width += dx; else c.Left += dx; } else if ((c.Anchor & AnchorStyles.Left) == 0) { c.Left += dx / 2; } if ((c.Anchor & AnchorStyles.Bottom) != 0) { if ((c.Anchor & AnchorStyles.Top) != 0) c.Height += dy; else c.Top += dy; } else if ((c.Anchor & AnchorStyles.Top) == 0) { c.Top += dy / 2; } if (!c.Visible) continue; switch (c.Dock) { case DockStyle.Left: c.Bounds = Bounds(remainingBounds.Left, remainingBounds.Top, c.Width, remainingBounds.Height); remainingBounds.X += c.Width; remainingBounds.Width -= c.Width; break; case DockStyle.Top: c.Bounds = Bounds(remainingBounds.Left, remainingBounds.Top, remainingBounds.Width, c.Height); remainingBounds.Y += c.Height; remainingBounds.Height -= c.Height; break; case DockStyle.Right: c.Bounds = Bounds(remainingBounds.Right - c.Width, remainingBounds.Top, c.Width, remainingBounds.Height); remainingBounds.Width -= c.Width; break; case DockStyle.Bottom: c.Bounds = Bounds(remainingBounds.Left, remainingBounds.Bottom - c.Height, remainingBounds.Width, c.Height); remainingBounds.Height -= c.Height; break; } } // phase 2: Dock = Fill for (int i = control.Controls.Count - 1; i >= 0; i--) { var c = control.Controls[i]; if (!c.Visible) continue; switch (c.Dock) { case DockStyle.Fill: c.Bounds = Bounds(remainingBounds.Left, remainingBounds.Top, remainingBounds.Width, remainingBounds.Height); break; } } } finally { updatingLayout = false; control.OnLayout(); } } public LayoutEngine(Control control) => this.control = control; private struct LayoutTransaction { public int DeltaX { get; } public int DeltaY { get; } public int Width { get; } public int Height { get; } public LayoutTransaction(int deltaX, int deltaY, int width, int height) { DeltaX = deltaX; DeltaY = deltaY; Width = width; Height = height; } } } }