#region Imports using DPumpHydr.WinFrmUI.RLT.Drawing.Poison; using DPumpHydr.WinFrmUI.RLT.Enum.Poison; using DPumpHydr.WinFrmUI.RLT.Extension.Poison; using DPumpHydr.WinFrmUI.RLT.Interface.Poison; using DPumpHydr.WinFrmUI.RLT.Manager; using DPumpHydr.WinFrmUI.RLT.Native; using DPumpHydr.WinFrmUI.RLT.Util; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Security; using System.Windows.Forms; #endregion namespace DPumpHydr.WinFrmUI.RLT.Forms { #region PoisonForm public class PoisonForm : Form, IPoisonForm, IDisposable { #region Interface private ColorStyle poisonStyle = ColorStyle.Blue; [Category(PoisonDefaults.PropertyCategory.Appearance)] public ColorStyle Style { get { if (StyleManager != null) { return StyleManager.Style; } return poisonStyle; } set => poisonStyle = value; } private ThemeStyle poisonTheme = ThemeStyle.Light; [Category(PoisonDefaults.PropertyCategory.Appearance)] public ThemeStyle Theme { get { if (StyleManager != null) { return StyleManager.Theme; } return poisonTheme; } set => poisonTheme = value; } [Browsable(false)] public PoisonStyleManager StyleManager { get; set; } = null; #endregion #region Fields [Browsable(true)] [Category(PoisonDefaults.PropertyCategory.Appearance)] public FormTextAlignType TextAlign { get; set; } = FormTextAlignType.Left; [Browsable(false)] public override Color BackColor => PoisonPaint.BackColor.Form(Theme); [DefaultValue(Enum.Poison.FormBorderStyle.None)] [Browsable(true)] [Category(PoisonDefaults.PropertyCategory.Appearance)] public Enum.Poison.FormBorderStyle PoisonBorderStyle { get; set; } = Enum.Poison.FormBorderStyle.None; [Category(PoisonDefaults.PropertyCategory.Appearance)] public bool Movable { get; set; } = true; public new Padding Padding { get => base.Padding; set { value.Top = Math.Max(value.Top, DisplayHeader ? 60 : 30); base.Padding = value; } } protected override Padding DefaultPadding => new(20, DisplayHeader ? 60 : 20, 20, 20); private bool displayHeader = true; [Category(PoisonDefaults.PropertyCategory.Appearance)] [DefaultValue(true)] public bool DisplayHeader { get => displayHeader; set { if (value != displayHeader) { Padding p = base.Padding; p.Top += value ? 30 : -30; base.Padding = p; } displayHeader = value; } } [Category(PoisonDefaults.PropertyCategory.Appearance)] public bool Resizable { get; set; } = true; private FormShadowType shadowType = FormShadowType.Flat; [Category(PoisonDefaults.PropertyCategory.Appearance)] [DefaultValue(FormShadowType.Flat)] public FormShadowType ShadowType { get => IsMdiChild ? FormShadowType.None : shadowType; set => shadowType = value; } [Browsable(false)] public new System.Windows.Forms.FormBorderStyle FormBorderStyle { get => base.FormBorderStyle; set => base.FormBorderStyle = value; } public new Form MdiParent { get => base.MdiParent; set { if (value != null) { RemoveShadow(); shadowType = FormShadowType.None; } base.MdiParent = value; } } private const int borderWidth = 5; private Bitmap _image = null; private Image backImage; [Category(PoisonDefaults.PropertyCategory.Appearance)] [DefaultValue(null)] public Image BackImage { get => backImage; set { backImage = value; if (value != null) { _image = ApplyInvert(new Bitmap(value)); } Refresh(); } } private Padding backImagePadding; [Category(PoisonDefaults.PropertyCategory.Appearance)] public Padding BackImagePadding { get => backImagePadding; set { backImagePadding = value; Refresh(); } } private int backMaxSize; [Category(PoisonDefaults.PropertyCategory.Appearance)] public int BackMaxSize { get => backMaxSize; set { backMaxSize = value; Refresh(); } } private BackLocationType backLocation; [Category(PoisonDefaults.PropertyCategory.Appearance)] [DefaultValue(BackLocationType.TopLeft)] public BackLocationType BackLocation { get => backLocation; set { backLocation = value; Refresh(); } } private bool _imageinvert; [Category(PoisonDefaults.PropertyCategory.Appearance)] [DefaultValue(true)] public bool ApplyImageInvert { get => _imageinvert; set { _imageinvert = value; Refresh(); } } #endregion #region Constructor public PoisonForm() { SetStyle ( ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true ); FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; Name = "PoisonForm"; StartPosition = FormStartPosition.CenterScreen; TransparencyKey = Color.Lavender; } protected override void Dispose(bool disposing) { if (disposing) { RemoveShadow(); } base.Dispose(disposing); } #endregion #region Paint Methods public static Bitmap ApplyInvert(Bitmap bitmapImage) { byte R, G, B; Color pixelColor; for (int y = 0; y < bitmapImage.Height; y++) { for (int x = 0; x < bitmapImage.Width; x++) { pixelColor = bitmapImage.GetPixel(x, y); //A = pixelColor.A; R = (byte)(255 - pixelColor.R); G = (byte)(255 - pixelColor.G); B = (byte)(255 - pixelColor.B); if (R <= 0) { R = 17; } if (G <= 0) { G = 17; } if (B <= 0) { B = 17; } //bitmapImage.SetPixel(x, y, Color.FromArgb((int)A, (int)R, (int)G, (int)B)); bitmapImage.SetPixel(x, y, Color.FromArgb(R, G, B)); } } return bitmapImage; } protected override void OnPaint(PaintEventArgs e) { Color backColor = PoisonPaint.BackColor.Form(Theme); Color foreColor = PoisonPaint.ForeColor.Title(Theme); e.Graphics.Clear(backColor); using (SolidBrush b = PoisonPaint.GetStyleBrush(Style)) { Rectangle topRect = new(0, 0, Width, borderWidth); e.Graphics.FillRectangle(b, topRect); } if (PoisonBorderStyle != Enum.Poison.FormBorderStyle.None) { Color c = PoisonPaint.BorderColor.Form(Theme); using Pen pen = new(c); e.Graphics.DrawLines ( pen, new[] { new Point(0, borderWidth), new Point(0, Height - 1), new Point(Width - 1, Height - 1), new Point(Width - 1, borderWidth) } ); } if (backImage != null && backMaxSize != 0) { Image img = PoisonImage.ResizeImage(backImage, new Rectangle(0, 0, backMaxSize, backMaxSize)); if (_imageinvert) { img = PoisonImage.ResizeImage((Theme == ThemeStyle.Dark) ? _image : backImage, new Rectangle(0, 0, backMaxSize, backMaxSize)); } switch (backLocation) { case BackLocationType.TopLeft: e.Graphics.DrawImage(img, 0 + backImagePadding.Left, 0 + backImagePadding.Top); break; case BackLocationType.TopRight: e.Graphics.DrawImage(img, ClientRectangle.Right - (backImagePadding.Right + img.Width), 0 + backImagePadding.Top); break; case BackLocationType.BottomLeft: e.Graphics.DrawImage(img, 0 + backImagePadding.Left, ClientRectangle.Bottom - (img.Height + backImagePadding.Bottom)); break; case BackLocationType.BottomRight: e.Graphics.DrawImage(img, ClientRectangle.Right - (backImagePadding.Right + img.Width), ClientRectangle.Bottom - (img.Height + backImagePadding.Bottom)); break; } } if (displayHeader) { Rectangle bounds = new(20, 20, ClientRectangle.Width - (2 * 20), 40); TextFormatFlags flags = TextFormatFlags.EndEllipsis | GetTextFormatFlags(); TextRenderer.DrawText(e.Graphics, Text, PoisonFonts.Title, bounds, foreColor, flags); } if (Resizable && (SizeGripStyle == SizeGripStyle.Auto || SizeGripStyle == SizeGripStyle.Show)) { using SolidBrush b = new(PoisonPaint.ForeColor.Button.Disabled(Theme)); Size resizeHandleSize = new(2, 2); e.Graphics.FillRectangles ( b, new Rectangle[] { new(new Point(ClientRectangle.Width-6,ClientRectangle.Height-6), resizeHandleSize), new(new Point(ClientRectangle.Width-10,ClientRectangle.Height-10), resizeHandleSize), new(new Point(ClientRectangle.Width-10,ClientRectangle.Height-6), resizeHandleSize), new(new Point(ClientRectangle.Width-6,ClientRectangle.Height-10), resizeHandleSize), new(new Point(ClientRectangle.Width-14,ClientRectangle.Height-6), resizeHandleSize), new(new Point(ClientRectangle.Width-6,ClientRectangle.Height-14), resizeHandleSize) } ); } } private TextFormatFlags GetTextFormatFlags() { return TextAlign switch { FormTextAlignType.Left => TextFormatFlags.Left, FormTextAlignType.Center => TextFormatFlags.HorizontalCenter, FormTextAlignType.Right => TextFormatFlags.Right, _ => throw new InvalidOperationException(), }; } #endregion #region Management Methods protected override void OnClosing(CancelEventArgs e) { if (this is not DPumpHydr.WinFrmUI.RLT.Controls.PoisonTaskWindow) { DPumpHydr.WinFrmUI.RLT.Controls.PoisonTaskWindow.ForceClose(); } base.OnClosing(e); } protected override void OnClosed(EventArgs e) { if (Owner != null) { Owner = null; } RemoveShadow(); base.OnClosed(e); } [SecuritySafeCritical] public bool FocusMe() { return WinApi.SetForegroundWindow(Handle); } protected override void OnLoad(EventArgs e) { base.OnLoad(e); if (DesignMode) { return; } switch (StartPosition) { case FormStartPosition.CenterParent: CenterToParent(); break; case FormStartPosition.CenterScreen: if (IsMdiChild) { CenterToParent(); } else { CenterToScreen(); } break; } RemoveCloseButton(); if (ControlBox) { AddWindowButton(WindowButtons.Close); if (MaximizeBox) { AddWindowButton(WindowButtons.Maximize); } if (MinimizeBox) { AddWindowButton(WindowButtons.Minimize); } UpdateWindowButtonPosition(); } CreateShadow(); } protected override void OnActivated(EventArgs e) { base.OnActivated(e); if (shadowType == FormShadowType.AeroShadow && IsAeroThemeEnabled() && IsDropShadowSupported()) { int val = 2; _ = DwmApi.DwmSetWindowAttribute(Handle, 2, ref val, 4); DwmApi.MARGINS m = new() { cyBottomHeight = 1, cxLeftWidth = 0, cxRightWidth = 0, cyTopHeight = 0 }; _ = DwmApi.DwmExtendFrameIntoClientArea(Handle, ref m); } } protected override void OnEnabledChanged(EventArgs e) { base.OnEnabledChanged(e); Invalidate(); } protected override void OnResizeEnd(EventArgs e) { base.OnResizeEnd(e); UpdateWindowButtonPosition(); } protected override void WndProc(ref Message m) { if (DesignMode) { base.WndProc(ref m); return; } switch (m.Msg) { case (int)WinApi.Messages.WM_SYSCOMMAND: int sc = m.WParam.ToInt32() & 0xFFF0; switch (sc) { case (int)WinApi.Messages.SC_MOVE: if (!Movable) { return; } break; case (int)WinApi.Messages.SC_MAXIMIZE: break; case (int)WinApi.Messages.SC_RESTORE: break; } break; case (int)WinApi.Messages.WM_NCLBUTTONDBLCLK: case (int)WinApi.Messages.WM_LBUTTONDBLCLK: if (!MaximizeBox) { return; } break; case (int)WinApi.Messages.WM_NCHITTEST: WinApi.HitTest ht = HitTestNCA(m.HWnd, m.WParam, m.LParam); if (ht != WinApi.HitTest.HTCLIENT) { m.Result = (IntPtr)ht; return; } break; case (int)WinApi.Messages.WM_DWMCOMPOSITIONCHANGED: break; } base.WndProc(ref m); switch (m.Msg) { case (int)WinApi.Messages.WM_GETMINMAXINFO: OnGetMinMaxInfo(m.HWnd, m.LParam); break; case (int)WinApi.Messages.WM_SIZE: if (windowButtonList != null) { windowButtonList.TryGetValue(WindowButtons.Maximize, out PoisonFormButton btn); if (btn == null) { return; } if (WindowState == FormWindowState.Normal) { if (shadowForm != null) { shadowForm.Visible = true; } btn.Text = "1"; } if (WindowState == FormWindowState.Maximized) { btn.Text = "2"; } } break; } } [SecuritySafeCritical] private unsafe void OnGetMinMaxInfo(IntPtr hwnd, IntPtr lParam) { WinApi.MINMAXINFO* pmmi = (WinApi.MINMAXINFO*)lParam; //YOROCA MDI PARENT Screen s = Screen.FromHandle(hwnd); //if (IsMdiChild) if (Parent != null) { pmmi->ptMaxSize.x = Parent.ClientRectangle.Size.Width; pmmi->ptMaxSize.y = Parent.ClientRectangle.Size.Height; } else { pmmi->ptMaxSize.x = s.WorkingArea.Width; pmmi->ptMaxSize.y = s.WorkingArea.Height; } pmmi->ptMaxPosition.x = Math.Abs(s.WorkingArea.Left - s.Bounds.Left); pmmi->ptMaxPosition.y = Math.Abs(s.WorkingArea.Top - s.Bounds.Top); //if (MinimumSize.Width > 0) pmmi->ptMinTrackSize.x = MinimumSize.Width; //if (MinimumSize.Height > 0) pmmi->ptMinTrackSize.y = MinimumSize.Height; //if (MaximumSize.Width > 0) pmmi->ptMaxTrackSize.x = MaximumSize.Width; //if (MaximumSize.Height > 0) pmmi->ptMaxTrackSize.y = MaximumSize.Height; } private WinApi.HitTest HitTestNCA(IntPtr hwnd, IntPtr wparam, IntPtr lparam) { //Point vPoint = PointToClient(new Point((int)lparam & 0xFFFF, (int)lparam >> 16 & 0xFFFF)); //Point vPoint = PointToClient(new Point((Int16)lparam, (Int16)((int)lparam >> 16))); Point vPoint = new((short)lparam, (short)((int)lparam >> 16)); int vPadding = Math.Max(Padding.Right, Padding.Bottom); if (Resizable) { if (RectangleToScreen(new Rectangle(ClientRectangle.Width - vPadding, ClientRectangle.Height - vPadding, vPadding, vPadding)).Contains(vPoint)) { return WinApi.HitTest.HTBOTTOMRIGHT; } } if (RectangleToScreen(new Rectangle(borderWidth, borderWidth, ClientRectangle.Width - (2 * borderWidth), 50)).Contains(vPoint)) { return WinApi.HitTest.HTCAPTION; } return WinApi.HitTest.HTCLIENT; } protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); if (e.Button == MouseButtons.Left && Movable) { if (WindowState == FormWindowState.Maximized) { return; } if (Width - borderWidth > e.Location.X && e.Location.X > borderWidth && e.Location.Y > borderWidth) { MoveControl(); } } } [SecuritySafeCritical] private void MoveControl() { WinApi.ReleaseCapture(); _ = WinApi.SendMessage(Handle, (int)WinApi.Messages.WM_NCLBUTTONDOWN, (int)WinApi.HitTest.HTCAPTION, 0); } [SecuritySafeCritical] private static bool IsAeroThemeEnabled() { if (Environment.OSVersion.Version.Major <= 5) { return false; } _ = DwmApi.DwmIsCompositionEnabled(out bool aeroEnabled); return aeroEnabled; } private static bool IsDropShadowSupported() { return Environment.OSVersion.Version.Major > 5 && SystemInformation.IsDropShadowEnabled; } #endregion #region Window Buttons private enum WindowButtons { Minimize, Maximize, Close } private Dictionary windowButtonList; private void AddWindowButton(WindowButtons button) { if (windowButtonList == null) { windowButtonList = new Dictionary(); } if (windowButtonList.ContainsKey(button)) { return; } PoisonFormButton newButton = new(); if (button == WindowButtons.Close) { newButton.Text = "r"; } else if (button == WindowButtons.Minimize) { newButton.Text = "0"; } else if (button == WindowButtons.Maximize) { if (WindowState == FormWindowState.Normal) { newButton.Text = "1"; } else { newButton.Text = "2"; } } newButton.Style = Style; newButton.Theme = Theme; newButton.Tag = button; newButton.Size = new(25, 20); newButton.Anchor = AnchorStyles.Top | AnchorStyles.Right; newButton.TabStop = false; //remove the form controls from the tab stop newButton.Click += WindowButton_Click; Controls.Add(newButton); windowButtonList.Add(button, newButton); } private void WindowButton_Click(object sender, EventArgs e) { if (sender is PoisonFormButton btn) { WindowButtons btnFlag = (WindowButtons)btn.Tag; if (btnFlag == WindowButtons.Close) { Close(); } else if (btnFlag == WindowButtons.Minimize) { WindowState = FormWindowState.Minimized; } else if (btnFlag == WindowButtons.Maximize) { if (WindowState == FormWindowState.Normal) { WindowState = FormWindowState.Maximized; btn.Text = "2"; } else { WindowState = FormWindowState.Normal; btn.Text = "1"; } } } } private void UpdateWindowButtonPosition() { if (!ControlBox) { return; } Dictionary priorityOrder = new(3) { { 0, WindowButtons.Close }, { 1, WindowButtons.Maximize }, { 2, WindowButtons.Minimize } }; Point firstButtonLocation = new(ClientRectangle.Width - borderWidth - 25, borderWidth); int lastDrawedButtonPosition = firstButtonLocation.X - 25; PoisonFormButton firstButton = null; if (windowButtonList.Count == 1) { foreach (KeyValuePair button in windowButtonList) { button.Value.Location = firstButtonLocation; } } else { foreach (KeyValuePair button in priorityOrder) { bool buttonExists = windowButtonList.ContainsKey(button.Value); if (firstButton == null && buttonExists) { firstButton = windowButtonList[button.Value]; firstButton.Location = firstButtonLocation; continue; } if (firstButton == null || !buttonExists) { continue; } windowButtonList[button.Value].Location = new(lastDrawedButtonPosition, borderWidth); lastDrawedButtonPosition -= 25; } } Refresh(); } private class PoisonFormButton : Button, IPoisonControl { #region Interface [Category(PoisonDefaults.PropertyCategory.Appearance)] public event EventHandler CustomPaintBackground; protected virtual void OnCustomPaintBackground(PoisonPaintEventArgs e) { if (GetStyle(ControlStyles.UserPaint) && CustomPaintBackground != null) { CustomPaintBackground(this, e); } } [Category(PoisonDefaults.PropertyCategory.Appearance)] public event EventHandler CustomPaint; protected virtual void OnCustomPaint(PoisonPaintEventArgs e) { if (GetStyle(ControlStyles.UserPaint) && CustomPaint != null) { CustomPaint(this, e); } } [Category(PoisonDefaults.PropertyCategory.Appearance)] public event EventHandler CustomPaintForeground; protected virtual void OnCustomPaintForeground(PoisonPaintEventArgs e) { if (GetStyle(ControlStyles.UserPaint) && CustomPaintForeground != null) { CustomPaintForeground(this, e); } } private ColorStyle poisonStyle = ColorStyle.Default; [Category(PoisonDefaults.PropertyCategory.Appearance)] [DefaultValue(ColorStyle.Default)] public ColorStyle Style { get { if (DesignMode || poisonStyle != ColorStyle.Default) { return poisonStyle; } if (StyleManager != null && poisonStyle == ColorStyle.Default) { return StyleManager.Style; } if (StyleManager == null && poisonStyle == ColorStyle.Default) { return PoisonDefaults.Style; } return poisonStyle; } set => poisonStyle = value; } private ThemeStyle poisonTheme = ThemeStyle.Default; [Category(PoisonDefaults.PropertyCategory.Appearance)] [DefaultValue(ThemeStyle.Default)] public ThemeStyle Theme { get { if (DesignMode || poisonTheme != ThemeStyle.Default) { return poisonTheme; } if (StyleManager != null && poisonTheme == ThemeStyle.Default) { return StyleManager.Theme; } if (StyleManager == null && poisonTheme == ThemeStyle.Default) { return PoisonDefaults.Theme; } return poisonTheme; } set => poisonTheme = value; } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public PoisonStyleManager StyleManager { get; set; } = null; [DefaultValue(false)] [Category(PoisonDefaults.PropertyCategory.Appearance)] public bool UseCustomBackColor { get; set; } = false; [DefaultValue(false)] [Category(PoisonDefaults.PropertyCategory.Appearance)] public bool UseCustomForeColor { get; set; } = false; [DefaultValue(false)] [Category(PoisonDefaults.PropertyCategory.Appearance)] public bool UseStyleColors { get; set; } = false; [Browsable(false)] [Category(PoisonDefaults.PropertyCategory.Behaviour)] [DefaultValue(false)] public bool UseSelectable { get => GetStyle(ControlStyles.Selectable); set => SetStyle(ControlStyles.Selectable, value); } #endregion #region Fields private bool isHovered = false; private bool isPressed = false; #endregion #region Constructor public PoisonFormButton() { SetStyle ( ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true ); } #endregion #region Paint Methods protected override void OnPaint(PaintEventArgs e) { Color backColor, foreColor; ThemeStyle _Theme = Theme; if (Parent != null) { if (Parent is IPoisonForm form) { _Theme = form.Theme; backColor = PoisonPaint.BackColor.Form(_Theme); } else if (Parent is IPoisonControl) { backColor = PoisonPaint.GetStyleColor(Style); } else { backColor = Parent.BackColor; } } else { backColor = PoisonPaint.BackColor.Form(_Theme); } if (isHovered && !isPressed && Enabled) { foreColor = PoisonPaint.ForeColor.Button.Normal(_Theme); backColor = PoisonPaint.BackColor.Button.Normal(_Theme); } else if (isHovered && isPressed && Enabled) { foreColor = PoisonPaint.ForeColor.Button.Press(_Theme); backColor = PoisonPaint.GetStyleColor(Style); } else if (!Enabled) { foreColor = PoisonPaint.ForeColor.Button.Disabled(_Theme); backColor = PoisonPaint.BackColor.Button.Disabled(_Theme); } else { foreColor = PoisonPaint.ForeColor.Button.Normal(_Theme); } e.Graphics.Clear(backColor); Font buttonFont = new("Webdings", 9.25f); TextRenderer.DrawText(e.Graphics, Text, buttonFont, ClientRectangle, foreColor, backColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.EndEllipsis); } #endregion #region Mouse Methods protected override void OnMouseEnter(EventArgs e) { isHovered = true; Invalidate(); base.OnMouseEnter(e); } protected override void OnMouseDown(MouseEventArgs e) { if (e.Button == MouseButtons.Left) { isPressed = true; Invalidate(); } base.OnMouseDown(e); } protected override void OnMouseUp(MouseEventArgs e) { isPressed = false; Invalidate(); base.OnMouseUp(e); } protected override void OnMouseLeave(EventArgs e) { isHovered = false; Invalidate(); base.OnMouseLeave(e); } #endregion } #endregion #region Shadows private const int CS_DROPSHADOW = 0x20000; private const int WS_MINIMIZEBOX = 0x20000; protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.Style |= WS_MINIMIZEBOX; if (ShadowType == FormShadowType.SystemShadow) { cp.ClassStyle |= CS_DROPSHADOW; } return cp; } } private Form shadowForm; private void CreateShadow() { switch (ShadowType) { case FormShadowType.Flat: shadowForm = new PoisonFlatDropShadow(this); return; case FormShadowType.DropShadow: shadowForm = new PoisonRealisticDropShadow(this); return; case FormShadowType.None: return; //default: // shadowForm = new PoisonFlatDropShadow(this); // return; } } private void RemoveShadow() { if (shadowForm == null || shadowForm.IsDisposed) { return; } shadowForm.Visible = false; Owner = shadowForm.Owner; shadowForm.Owner = null; shadowForm.Dispose(); shadowForm = null; } #region PoisonShadowBase protected abstract class PoisonShadowBase : Form { protected Form TargetForm { get; private set; } private readonly int shadowSize; private readonly int wsExStyle; protected PoisonShadowBase(Form targetForm, int shadowSizee, int wsExStylee) { TargetForm = targetForm; shadowSize = shadowSizee; wsExStyle = wsExStylee; TargetForm.Activated += OnTargetFormActivated; TargetForm.ResizeBegin += OnTargetFormResizeBegin; TargetForm.ResizeEnd += OnTargetFormResizeEnd; TargetForm.VisibleChanged += OnTargetFormVisibleChanged; TargetForm.SizeChanged += OnTargetFormSizeChanged; TargetForm.Move += OnTargetFormMove; TargetForm.Resize += OnTargetFormResize; if (TargetForm.Owner != null) { Owner = TargetForm.Owner; } TargetForm.Owner = this; MaximizeBox = false; MinimizeBox = false; ShowInTaskbar = false; ShowIcon = false; FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; Bounds = GetShadowBounds(); } protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= wsExStyle; return cp; } } private Rectangle GetShadowBounds() { Rectangle r = TargetForm.Bounds; r.Inflate(shadowSize, shadowSize); return r; } protected abstract void PaintShadow(); protected abstract void ClearShadow(); #region Event Handlers private bool isBringingToFront; protected override void OnDeactivate(EventArgs e) { base.OnDeactivate(e); isBringingToFront = true; } private void OnTargetFormActivated(object sender, EventArgs e) { if (Visible) { Update(); } if (isBringingToFront) { Visible = true; isBringingToFront = false; return; } BringToFront(); } private void OnTargetFormVisibleChanged(object sender, EventArgs e) { Visible = TargetForm.Visible && TargetForm.WindowState != FormWindowState.Minimized; Update(); } private long lastResizedOn; private bool IsResizing => lastResizedOn > 0; private void OnTargetFormResizeBegin(object sender, EventArgs e) { lastResizedOn = DateTime.Now.Ticks; } private void OnTargetFormMove(object sender, EventArgs e) { if (!TargetForm.Visible || TargetForm.WindowState != FormWindowState.Normal) { Visible = false; } else { Bounds = GetShadowBounds(); } } private void OnTargetFormResize(object sender, EventArgs e) { ClearShadow(); } private void OnTargetFormSizeChanged(object sender, EventArgs e) { Bounds = GetShadowBounds(); if (IsResizing) { return; } PaintShadowIfVisible(); } private void OnTargetFormResizeEnd(object sender, EventArgs e) { lastResizedOn = 0; PaintShadowIfVisible(); } private void PaintShadowIfVisible() { if (TargetForm.Visible && TargetForm.WindowState != FormWindowState.Minimized) { PaintShadow(); } } #endregion #region Constants protected const int WS_EX_TRANSPARENT = 0x20; protected const int WS_EX_LAYERED = 0x80000; protected const int WS_EX_NOACTIVATE = 0x8000000; private const int TICKS_PER_MS = 10000; private const long RESIZE_REDRAW_INTERVAL = 1000 * TICKS_PER_MS; #endregion } #endregion #region Aero DropShadow protected class PoisonAeroDropShadow : PoisonShadowBase { public PoisonAeroDropShadow(Form targetForm) : base(targetForm, 0, WS_EX_TRANSPARENT | WS_EX_NOACTIVATE) { FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow; } protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { if (specified == BoundsSpecified.Size) { return; } base.SetBoundsCore(x, y, width, height, specified); } protected override void PaintShadow() { Visible = true; } protected override void ClearShadow() { } } #endregion #region Flat DropShadow protected class PoisonFlatDropShadow : PoisonShadowBase { private Point Offset = new(-6, -6); public PoisonFlatDropShadow(Form targetForm) : base(targetForm, 6, WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVATE) { } protected override void OnLoad(EventArgs e) { base.OnLoad(e); PaintShadow(); } protected override void OnPaint(PaintEventArgs e) { Visible = true; PaintShadow(); } protected override void PaintShadow() { using Bitmap getShadow = DrawBlurBorder(); SetBitmap(getShadow, 255); } protected override void ClearShadow() { Bitmap img = new(Width, Height, PixelFormat.Format32bppArgb); Graphics g = Graphics.FromImage(img); g.Clear(Color.Transparent); g.Flush(); g.Dispose(); SetBitmap(img, 255); img.Dispose(); } #region Drawing methods [SecuritySafeCritical] private void SetBitmap(Bitmap bitmap, byte opacity) { if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) { throw new ApplicationException("The bitmap must be 32ppp with alpha-channel."); } IntPtr screenDc = WinApi.GetDC(IntPtr.Zero); IntPtr memDc = WinApi.CreateCompatibleDC(screenDc); IntPtr hBitmap = IntPtr.Zero; IntPtr oldBitmap = IntPtr.Zero; try { hBitmap = bitmap.GetHbitmap(Color.FromArgb(0)); oldBitmap = WinApi.SelectObject(memDc, hBitmap); WinApi.SIZE size = new(bitmap.Width, bitmap.Height); WinApi.POINT pointSource = new(0, 0); WinApi.POINT topPos = new(Left, Top); WinApi.BLENDFUNCTION blend = new() { BlendOp = WinApi.AC_SRC_OVER, BlendFlags = 0, SourceConstantAlpha = opacity, AlphaFormat = WinApi.AC_SRC_ALPHA }; WinApi.UpdateLayeredWindow(Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, WinApi.ULW_ALPHA); } finally { WinApi.ReleaseDC(IntPtr.Zero, screenDc); if (hBitmap != IntPtr.Zero) { WinApi.SelectObject(memDc, oldBitmap); WinApi.DeleteObject(hBitmap); } WinApi.DeleteDC(memDc); } } private Bitmap DrawBlurBorder() { return (Bitmap)DrawOutsetShadow(Color.Black, new Rectangle(0, 0, ClientRectangle.Width, ClientRectangle.Height)); } private Image DrawOutsetShadow(Color color, Rectangle shadowCanvasArea) { Rectangle rOuter = shadowCanvasArea; Rectangle rInner = new(shadowCanvasArea.X + (-Offset.X - 1), shadowCanvasArea.Y + (-Offset.Y - 1), shadowCanvasArea.Width - ((-Offset.X * 2) - 1), shadowCanvasArea.Height - ((-Offset.Y * 2) - 1)); Bitmap img = new(rOuter.Width, rOuter.Height, PixelFormat.Format32bppArgb); Graphics g = Graphics.FromImage(img); g.SmoothingMode = SmoothingMode.AntiAlias; g.InterpolationMode = InterpolationMode.HighQualityBicubic; using (Brush bgBrush = new SolidBrush(Color.FromArgb(30, Color.Black))) { g.FillRectangle(bgBrush, rOuter); } using (Brush bgBrush = new SolidBrush(Color.FromArgb(60, Color.Black))) { g.FillRectangle(bgBrush, rInner); } g.Flush(); g.Dispose(); return img; } #endregion } #endregion #region Realistic DropShadow protected class PoisonRealisticDropShadow : PoisonShadowBase { public PoisonRealisticDropShadow(Form targetForm) : base(targetForm, 15, WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVATE) { } protected override void OnLoad(EventArgs e) { base.OnLoad(e); PaintShadow(); } protected override void OnPaint(PaintEventArgs e) { Visible = true; PaintShadow(); } protected override void PaintShadow() { using Bitmap getShadow = DrawBlurBorder(); SetBitmap(getShadow, 255); } protected override void ClearShadow() { Bitmap img = new(Width, Height, PixelFormat.Format32bppArgb); Graphics g = Graphics.FromImage(img); g.Clear(Color.Transparent); g.Flush(); g.Dispose(); SetBitmap(img, 255); img.Dispose(); } #region Drawing methods [SecuritySafeCritical] private void SetBitmap(Bitmap bitmap, byte opacity) { if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) { throw new ApplicationException("The bitmap must be 32ppp with alpha-channel."); } IntPtr screenDc = WinApi.GetDC(IntPtr.Zero); IntPtr memDc = WinApi.CreateCompatibleDC(screenDc); IntPtr hBitmap = IntPtr.Zero; IntPtr oldBitmap = IntPtr.Zero; try { hBitmap = bitmap.GetHbitmap(Color.FromArgb(0)); oldBitmap = WinApi.SelectObject(memDc, hBitmap); WinApi.SIZE size = new(bitmap.Width, bitmap.Height); WinApi.POINT pointSource = new(0, 0); WinApi.POINT topPos = new(Left, Top); WinApi.BLENDFUNCTION blend = new() { BlendOp = WinApi.AC_SRC_OVER, BlendFlags = 0, SourceConstantAlpha = opacity, AlphaFormat = WinApi.AC_SRC_ALPHA }; WinApi.UpdateLayeredWindow(Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, WinApi.ULW_ALPHA); } finally { WinApi.ReleaseDC(IntPtr.Zero, screenDc); if (hBitmap != IntPtr.Zero) { WinApi.SelectObject(memDc, oldBitmap); WinApi.DeleteObject(hBitmap); } WinApi.DeleteDC(memDc); } } private Bitmap DrawBlurBorder() { return (Bitmap)DrawOutsetShadow(0, 0, 40, 1, Color.Black, new Rectangle(1, 1, ClientRectangle.Width, ClientRectangle.Height)); } private static Image DrawOutsetShadow(int hShadow, int vShadow, int blur, int spread, Color color, Rectangle shadowCanvasArea) { Rectangle rOuter = shadowCanvasArea; Rectangle rInner = shadowCanvasArea; rInner.Offset(hShadow, vShadow); rInner.Inflate(-blur, -blur); rOuter.Inflate(spread, spread); rOuter.Offset(hShadow, vShadow); Rectangle originalOuter = rOuter; Bitmap img = new(originalOuter.Width, originalOuter.Height, PixelFormat.Format32bppArgb); Graphics g = Graphics.FromImage(img); g.SmoothingMode = SmoothingMode.AntiAlias; g.InterpolationMode = InterpolationMode.HighQualityBicubic; int currentBlur = 0; do { double transparency = (rOuter.Height - rInner.Height) / (double)((blur * 2) + (spread * 2)); Color shadowColor = Color.FromArgb((int)(200 * (transparency * transparency)), color); Rectangle rOutput = rInner; rOutput.Offset(-originalOuter.Left, -originalOuter.Top); DrawRoundedRectangle(g, rOutput, currentBlur, Pens.Transparent, shadowColor); rInner.Inflate(1, 1); currentBlur = (int)(blur * (1 - (transparency * transparency))); } while (rOuter.Contains(rInner)); g.Flush(); g.Dispose(); return img; } private static void DrawRoundedRectangle(Graphics g, Rectangle bounds, int cornerRadius, Pen drawPen, Color fillColor) { int strokeOffset = Convert.ToInt32(Math.Ceiling(drawPen.Width)); bounds = Rectangle.Inflate(bounds, -strokeOffset, -strokeOffset); GraphicsPath gfxPath = new(); if (cornerRadius > 0) { gfxPath.AddArc(bounds.X, bounds.Y, cornerRadius, cornerRadius, 180, 90); gfxPath.AddArc(bounds.X + bounds.Width - cornerRadius, bounds.Y, cornerRadius, cornerRadius, 270, 90); gfxPath.AddArc(bounds.X + bounds.Width - cornerRadius, bounds.Y + bounds.Height - cornerRadius, cornerRadius, cornerRadius, 0, 90); gfxPath.AddArc(bounds.X, bounds.Y + bounds.Height - cornerRadius, cornerRadius, cornerRadius, 90, 90); } else { gfxPath.AddRectangle(bounds); } gfxPath.CloseAllFigures(); if (cornerRadius > 5) { using SolidBrush b = new(fillColor); g.FillPath(b, gfxPath); } if (drawPen != Pens.Transparent) { using Pen p = new(drawPen.Color); p.EndCap = p.StartCap = LineCap.Round; g.DrawPath(p, gfxPath); } } #endregion } #endregion #endregion #region Helper Methods [SecuritySafeCritical] public void RemoveCloseButton() { IntPtr hMenu = WinApi.GetSystemMenu(Handle, false); if (hMenu == IntPtr.Zero) { return; } int n = WinApi.GetMenuItemCount(hMenu); if (n <= 0) { return; } WinApi.RemoveMenu(hMenu, (uint)(n - 1), WinApi.MfByposition | WinApi.MfRemove); WinApi.RemoveMenu(hMenu, (uint)(n - 2), WinApi.MfByposition | WinApi.MfRemove); WinApi.DrawMenuBar(Handle); } private static Rectangle MeasureText(Graphics g, Rectangle clientRectangle, Font font, string text, TextFormatFlags flags) { Size proposedSize = new(int.MaxValue, int.MinValue); Size actualSize = TextRenderer.MeasureText(g, text, font, proposedSize, flags); return new Rectangle(clientRectangle.X, clientRectangle.Y, actualSize.Width, actualSize.Height); } #endregion } #endregion }