using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Windows.Forms; namespace Vanara.Interop.DesktopWindowManager { /// /// GlassExtenderProvider extends a and provides glass margins. /// [ProvideProperty("GlassEnabled", typeof(Form))] [ProvideProperty("GlassMarginMovesForm", typeof(Form))] [ProvideProperty("GlassMargins", typeof(Form))] [ToolboxItem(true), ToolboxBitmap(typeof(AeroWizard.WizardControl), "GlassExtenderProvider.bmp")] [Description("Extender for a Form that adds Aero glass properties.")] public class GlassExtenderProvider : Component, IExtenderProvider { private readonly Dictionary formProps = new Dictionary(); /// /// Properties for each form that is extended. /// [SuppressMessage("ReSharper", "InconsistentNaming")] private class GlassFormProperties { public Point FormMoveLastMousePos = Point.Empty; public bool FormMoveTracking; public bool GlassEnabled = true; public Padding GlassMargins = Padding.Empty; public bool GlassMarginMovesForm = true; } /// /// 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) { GlassFormProperties prop; if (formProps.TryGetValue(form, out prop)) return prop.GlassEnabled; return true; } /// /// 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) { GlassFormProperties prop; if (formProps.TryGetValue(form, out prop)) return prop.GlassMarginMovesForm; return true; } /// /// 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) { GlassFormProperties prop; if (formProps.TryGetValue(form, out prop)) return prop.GlassMargins; return Padding.Empty; } /// /// 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) => (form != this) && (form is Form); /// /// 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) { var prop = GetFormProperties(form); prop.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) { var prop = GetFormProperties(form); prop.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(); } /// /// 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 control in formProps.Keys) { var form = control as Form; if (form != null && !form.IsDisposed) UnhookForm(form); } } base.Dispose(disposing); } private void form_MouseDown(object sender, MouseEventArgs e) { if (e.Button != MouseButtons.Left) return; var prop = GetFormProperties(sender as Form); if (!prop.GlassMarginMovesForm) return; prop.FormMoveTracking = true; prop.FormMoveLastMousePos = ((Control)sender).PointToScreen(e.Location); } private void form_MouseMove(object sender, MouseEventArgs e) { var form = sender as Form; if (form == null) return; var prop = GetFormProperties(form); if (!prop.FormMoveTracking || GetNonGlassArea(form, prop).Contains(e.Location)) return; 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) return; var prop = GetFormProperties(sender as Form); prop.FormMoveTracking = false; } private void form_Paint(object sender, PaintEventArgs e) { GlassifyForm(sender as Form, e.Graphics); } private void form_Resize(object sender, EventArgs e) { var form = sender as Form; if (form != null && (DesktopWindowManager.IsCompositionEnabled() && GetGlassEnabled(form)) || form.IsDesignMode()) InvalidateNonGlassClientArea(form); } private void form_Shown(object sender, EventArgs e) { GlassifyForm(sender as Form); } private GlassFormProperties GetFormProperties(Form form) { GlassFormProperties prop; if (!formProps.TryGetValue(form, out prop)) formProps.Add(form, prop = new GlassFormProperties()); return prop; } 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 GlassifyForm(Form form, Graphics g = null) { if (!(DesktopWindowManager.IsCompositionEnabled() && GetGlassEnabled(form)) && !form.IsDesignMode()) return; if (g == null) g = form.CreateGraphics(); GlassFormProperties prop; if (!formProps.TryGetValue(form, out 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) return; 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; } } }