using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; namespace Vanara.Windows.Forms; /// GlassExtenderProvider extends a and provides glass margins. [ProvideProperty("GlassEnabled", typeof(Form))] [ProvideProperty("GlassMarginMovesForm", typeof(Form))] [ProvideProperty("GlassMargins", typeof(Form))] [ToolboxItem(true), ToolboxBitmap(typeof(GlassExtenderProvider), "GlassExtenderProvider.bmp")] [Description("Extender for a Form that adds Aero glass properties.")] public sealed class GlassExtenderProvider : Component, IExtenderProvider { private readonly Dictionary formProps = new(); /// Initializes a new instance of the class. public GlassExtenderProvider() { } /// Gets whether glass should be extended into the client space. /// The to be extended. /// true if the glass is enabled; otherwise false. [DisplayName("GlassEnabled")] [DefaultValue(true)] [Category("Behavior")] [Description("Indicates whether extending glass into the client area is enabled.")] public bool GetGlassEnabled(Form form) => !formProps.TryGetValue(form, out var prop) || prop.GlassEnabled; /// Gets a value indicating whether clicking and dragging within the top margin will move the form. /// The to be extended. /// true if clicking and dragging on the top margin moves the form; otherwise, false. [DisplayName("GlassMarginMovesForm")] [DefaultValue(true)] [Category("Behavior")] [Description("Specifies if clicking and dragging within the margin will move the form. ")] public bool GetGlassMarginMovesForm(Form form) => !formProps.TryGetValue(form, out var prop) || prop.GlassMarginMovesForm; /// Gets the glass margins. /// The to be extended. /// The margins where the glass will be extended. [DefaultValue(typeof(Padding), "0")] [DisplayName("GlassMargins")] [Description("Specifies the interior glass margin of the form. Set to -1 for full window glass.")] [Category("Layout")] public Padding GetGlassMargins(Form form) => formProps.TryGetValue(form, out var prop) ? prop.GlassMargins : Padding.Empty; /// Set whether the glass should be extended into the client space. /// The to be extended. /// The enabled value. public void SetGlassEnabled(Form form, bool value) { GetFormProperties(form).GlassEnabled = value; GlassifyForm(form); } /// Sets a value indicating whether clicking and dragging within the margin will move the form. /// The to be extended. /// true if clicking and dragging within the margin moves the form; otherwise, false. public void SetGlassMarginMovesForm(Form form, bool value) => GetFormProperties(form).GlassMarginMovesForm = value; /// Sets the glass margins. /// The to be extended. /// The margins where the glass will be extended. public void SetGlassMargins(Form form, Padding value) { if (form == null) throw new ArgumentNullException(nameof(form)); var prop = GetFormProperties(form); if (value == Padding.Empty) { prop.GlassMargins = Padding.Empty; UnhookForm(form); } else { prop.GlassMargins = value; form.Paint += form_Paint; if (!form.IsDesignMode()) { form.MouseDown += form_MouseDown; form.MouseMove += form_MouseMove; form.MouseUp += form_MouseUp; form.Resize += form_Resize; form.Shown += form_Shown; } } form.Invalidate(); } /// Specifies whether this object can provide its extender properties to the specified object. /// The to receive the extender properties. /// true if this object can provide extender properties to the specified object; otherwise, false. bool IExtenderProvider.CanExtend(object form) => !ReferenceEquals(form, this) && form is Form; /// /// Releases the unmanaged resources used by the and optionally releases the managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { if (disposing) { foreach (var form in formProps.Keys) { if (!form.IsDisposed) { UnhookForm(form); } } } base.Dispose(disposing); } private static Rectangle GetNonGlassArea(Form form, GlassFormProperties prop) { if (prop == null) return form.ClientRectangle; return new Rectangle(form.ClientRectangle.Left + prop.GlassMargins.Left, form.ClientRectangle.Top + prop.GlassMargins.Top, form.ClientRectangle.Width - prop.GlassMargins.Horizontal, form.ClientRectangle.Height - prop.GlassMargins.Vertical); } private void form_MouseDown(object? sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { var prop = GetFormProperties((Form)sender!); if (prop.GlassMarginMovesForm) { prop.FormMoveTracking = true; prop.FormMoveLastMousePos = ((Control)sender!).PointToScreen(e.Location); } } } private void form_MouseMove(object? sender, MouseEventArgs e) { var form = (Form)sender!; var prop = GetFormProperties(form); if (prop.FormMoveTracking && !GetNonGlassArea(form, prop).Contains(e.Location)) { var screen = form.PointToScreen(e.Location); var diff = new Point(screen.X - prop.FormMoveLastMousePos.X, screen.Y - prop.FormMoveLastMousePos.Y); var loc = form.Location; loc.Offset(diff); form.Location = loc; prop.FormMoveLastMousePos = screen; } } private void form_MouseUp(object? sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { GetFormProperties((Form)sender!).FormMoveTracking = false; } } private void form_Paint(object? sender, PaintEventArgs e) => GlassifyForm((Form)sender!, e.Graphics); private void form_Resize(object? sender, EventArgs e) { var form = (Form)sender!; if (DesktopWindowManager.CompositionEnabled && GetGlassEnabled(form) || form.IsDesignMode()) InvalidateNonGlassClientArea(form); } private void form_Shown(object? sender, EventArgs e) => GlassifyForm((Form)sender!); private GlassFormProperties GetFormProperties(Form form) { if (!formProps.TryGetValue(form, out var prop)) formProps.Add(form, prop = new GlassFormProperties()); return prop; } private void GlassifyForm(Form form, Graphics? g = null) { if (!(DesktopWindowManager.CompositionEnabled && GetGlassEnabled(form)) && !form.IsDesignMode()) return; g ??= form.CreateGraphics(); if (!formProps.TryGetValue(form, out var prop)) return; // Paint the glass effect. if (prop.GlassMargins == new Padding(-1)) g.FillRectangle(Brushes.Black, form.ClientRectangle); else { using var r = new Region(form.ClientRectangle); r.Exclude(GetNonGlassArea(form, prop)); g.FillRegion(Brushes.Black, r); } if (!form.IsDesignMode()) form.ExtendFrameIntoClientArea(prop.GlassMargins); } private void InvalidateNonGlassClientArea(Form form) { var glassMargin = GetGlassMargins(form); if (glassMargin != Padding.Empty) { var rect = new Rectangle(glassMargin.Left, glassMargin.Top, form.ClientRectangle.Width - glassMargin.Right, form.ClientRectangle.Height - glassMargin.Bottom); form.Invalidate(rect, false); } } private void UnhookForm(Form form) { form.MouseDown -= form_MouseDown; form.MouseMove -= form_MouseMove; form.MouseUp -= form_MouseUp; form.Shown -= form_Shown; form.Resize -= form_Resize; form.Paint -= form_Paint; } /// Properties for each form that is extended. private class GlassFormProperties { public Point FormMoveLastMousePos = Point.Empty; public bool FormMoveTracking = false; public bool GlassEnabled = true; public bool GlassMarginMovesForm = true; public Padding GlassMargins = Padding.Empty; } }