// COPYRIGHT (C) Tom. ALL RIGHTS RESERVED. // THE AntdUI PROJECT IS AN WINFORM LIBRARY LICENSED UNDER THE Apache-2.0 License. // LICENSED UNDER THE Apache License, VERSION 2.0 (THE "License") // YOU MAY NOT USE THIS FILE EXCEPT IN COMPLIANCE WITH THE License. // YOU MAY OBTAIN A COPY OF THE LICENSE AT // // http://www.apache.org/licenses/LICENSE-2.0 // // UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING, SOFTWARE // DISTRIBUTED UNDER THE LICENSE IS DISTRIBUTED ON AN "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. // SEE THE LICENSE FOR THE SPECIFIC LANGUAGE GOVERNING PERMISSIONS AND // LIMITATIONS UNDER THE License. // GITEE: https://gitee.com/antdui/AntdUI // GITHUB: https://github.com/AntdUI/AntdUI // CSDN: https://blog.csdn.net/v_132 // QQ: 17379620 using System; using System.ComponentModel; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms; namespace AntdUI { internal class LayeredFormDrawer : ILayeredForm { int FrmRadius = 0, FrmBor = 0; bool HasBor = false; Drawer.Config config; int padding = 24; ILayeredForm? formMask = null; public LayeredFormDrawer(Drawer.Config _config, ILayeredForm mask) : this(_config) { formMask = mask; if (config.MaskClosable) { mask.Click += (s1, e1) => { IClose(); }; } } public LayeredFormDrawer(Drawer.Config _config) { config = _config; TopMost = config.Form.TopMost; Font = config.Form.Font; padding = (int)Math.Round(config.Padding * Config.Dpi); Padding = new Padding(padding); var version = OS.Version; if (version.Major >= 10 && version.Build > 22000) FrmRadius = 8; //Win11 if (config.Form is Window) { //无边框处理 } else if (config.Form.FormBorderStyle != FormBorderStyle.None && config.Form.WindowState != FormWindowState.Maximized) { HasBor = true; FrmBor = 8; } config.Content.BackColor = Style.Db.BgElevated; config.Content.ForeColor = Style.Db.Text; SetPoint(); SetSize(start_W, start_H); SetLocation(start_X, start_Y); if (vertical) tempContent = new Bitmap(end_W - padding * 2, end_H - 20 - padding * 2); else tempContent = new Bitmap(end_W - 20 - padding * 2, end_H - padding * 2); if (config.Content.Tag is Size) { } else { config.Content.Tag = config.Content.Size; Helper.DpiAuto(Config.Dpi, config.Content); } config.Content.Size = new Size(tempContent.Width, tempContent.Height); LoadContent(); config.Content.DrawToBitmap(tempContent, new Rectangle(0, 0, tempContent.Width, tempContent.Height)); config.Form.LocationChanged += Form_LocationChanged; config.Form.SizeChanged += Form_SizeChanged; } bool vertical = false; void SetPoint() { switch (config.Align) { case TAlignMini.Top: vertical = true; start_H = 0; end_H = (int)(config.Content.Height * Config.Dpi) + padding * 2 + 20; if (config.Form is Window windowT) { start_W = end_W = windowT.Width; start_X = end_X = windowT.Left; start_Y = end_Y = windowT.Top; } else { start_W = end_W = config.Form.Width; start_X = end_X = config.Form.Left; start_Y = end_Y = config.Form.Top; } break; case TAlignMini.Bottom: vertical = true; start_H = 0; end_H = (int)(config.Content.Height * Config.Dpi) + padding * 2 + 20; if (config.Form is Window windowB) { start_W = end_W = windowB.Width; start_X = end_X = windowB.Left; start_Y = windowB.Top + windowB.Height; } else { start_W = end_W = config.Form.Width; start_X = end_X = config.Form.Left; start_Y = config.Form.Top + config.Form.Height; } end_Y = start_Y - end_H; break; case TAlignMini.Left: start_W = 0; end_W = (int)(config.Content.Width * Config.Dpi) + padding * 2 + 20; if (config.Form is Window windowL) { start_H = end_H = windowL.Height; start_X = end_X = windowL.Left; start_Y = end_Y = windowL.Top; } else { start_H = end_H = config.Form.Height; start_X = end_X = config.Form.Left; start_Y = end_Y = config.Form.Top; } break; case TAlignMini.Right: default: start_W = 0; end_W = (int)(config.Content.Width * Config.Dpi) + padding * 2 + 20; if (config.Form is Window windowR) { start_H = end_H = windowR.Height; start_X = windowR.Left + windowR.Width; start_Y = end_Y = windowR.Top; } else { start_H = end_H = config.Form.Height; start_X = config.Form.Left + config.Form.Width; start_Y = end_Y = config.Form.Top; } end_X = start_X - end_W; break; } } Bitmap? tempContent; private void Form_SizeChanged(object? sender, EventArgs e) { switch (config.Align) { case TAlignMini.Top: if (config.Form is Window windowT) { start_W = end_W = windowT.Width; start_X = end_X = windowT.Left; start_Y = end_Y = windowT.Top; } else { start_W = end_W = config.Form.Width; start_X = end_X = config.Form.Left; start_Y = end_Y = config.Form.Top; } break; case TAlignMini.Bottom: if (config.Form is Window windowB) { start_W = end_W = windowB.Width; start_X = end_X = windowB.Left; start_Y = windowB.Top + windowB.Height; } else { start_W = end_W = config.Form.Width; start_X = end_X = config.Form.Left; start_Y = config.Form.Top + config.Form.Height; } end_Y = start_Y - end_H; break; case TAlignMini.Left: if (config.Form is Window windowL) { start_H = end_H = windowL.Height; start_X = end_X = windowL.Left; start_Y = end_Y = windowL.Top; } else { start_H = end_H = config.Form.Height; start_X = end_X = config.Form.Left; start_Y = end_Y = config.Form.Top; } break; case TAlignMini.Right: default: if (config.Form is Window windowR) { start_H = end_H = windowR.Height; start_X = windowR.Left + windowR.Width; start_Y = end_Y = windowR.Top; } else { start_H = end_H = config.Form.Height; start_X = config.Form.Left + config.Form.Width; start_Y = end_Y = config.Form.Top; } end_X = start_X - end_W; break; } if (task_start == null) { SetLocation(end_X, end_Y); SetSize(end_W, end_H); if (form != null) { isok = false; var rect = Ang(); form.Location = rect.Location; form.Size = rect.Size; isok = true; } Print(); } } private void Form_LocationChanged(object? sender, EventArgs e) { switch (config.Align) { case TAlignMini.Top: if (config.Form is Window windowT) { start_W = end_W = windowT.Width; start_X = end_X = windowT.Left; start_Y = end_Y = windowT.Top; } else { start_W = end_W = config.Form.Width; start_X = end_X = config.Form.Left; start_Y = end_Y = config.Form.Top; } break; case TAlignMini.Bottom: if (config.Form is Window windowB) { start_W = end_W = windowB.Width; start_X = end_X = windowB.Left; start_Y = windowB.Top + windowB.Height; } else { start_W = end_W = config.Form.Width; start_X = end_X = config.Form.Left; start_Y = config.Form.Top + config.Form.Height; } end_Y = start_Y - end_H; break; case TAlignMini.Left: if (config.Form is Window windowL) { start_H = end_H = windowL.Height; start_X = end_X = windowL.Left; start_Y = end_Y = windowL.Top; } else { start_H = end_H = config.Form.Height; start_X = end_X = config.Form.Left; start_Y = end_Y = config.Form.Top; } break; case TAlignMini.Right: default: if (config.Form is Window windowR) { start_H = end_H = windowR.Height; start_X = windowR.Left + windowR.Width; start_Y = end_Y = windowR.Top; } else { start_H = end_H = config.Form.Height; start_X = config.Form.Left + config.Form.Width; start_Y = end_Y = config.Form.Top; } end_X = start_X - end_W; break; } if (task_start == null) { SetLocation(end_X, end_Y); if (form != null) form.Location = Ang().Location; Print(); } } #region 动画 int start_X = 0, end_X = 0, start_Y = 0, end_Y = 0; int start_W = 0, end_W = 0, start_H = 0, end_H = 0; ITask? task_start = null; bool run_end = false, ok_end = false; protected override void OnLoad(EventArgs e) { if (Config.Animation) { var t = Animation.TotalFrames(10, 100); int sleep = config.Mask ? 200 : 0; task_start = new ITask(vertical ? i => { var val = Animation.Animate(i, t, 1F, AnimationType.Ball); SetAnimateValueY(start_Y + (int)((end_Y - start_Y) * val), (int)(end_H * val), (byte)(255 * val)); return true; } : i => { var val = Animation.Animate(i, t, 1F, AnimationType.Ball); SetAnimateValueX(start_X + (int)((end_X - start_X) * val), (int)(end_W * val), (byte)(255 * val)); return true; }, 10, t, () => { if (IsHandleCreated) BeginInvoke(new Action(ShowContent)); SetAnimateValue(end_X, end_Y, end_W, end_H, 255); task_start = null; }, sleep); base.OnLoad(e); } else { SetAnimateValue(end_X, end_Y, end_W, end_H, 255); base.OnLoad(e); ShowContent(); } } #region 控件 Form? form = null; void LoadContent() { var rect = Ang(); var hidelocation = new Point(-rect.Width, -rect.Height); if (config.Content is Form form_) { form_.BackColor = Style.Db.BgElevated; form_.FormBorderStyle = FormBorderStyle.None; form_.Location = hidelocation; form_.Size = rect.Size; form = form_; } else { form = new DoubleBufferForm(this, config.Content) { BackColor = Style.Db.BgElevated, FormBorderStyle = FormBorderStyle.None, Location = hidelocation, Size = rect.Size }; } if (!config.Dispose && config.Content.Tag is Size size) { form.FormClosing += (a, b) => { config.Content.Dock = DockStyle.None; config.Content.Size = size; config.Content.Location = new Point(-config.Content.Width, -config.Content.Height); config.Form.Controls.Add(config.Content); }; } config.Content.Disposed += (a, b) => { config.Content.SizeChanged -= Content_SizeChanged; Close(); }; form.Show(this); form.Location = hidelocation; form.Size = rect.Size; } void ShowContent() { if (form == null) return; var rect = Ang(); form.Location = rect.Location; form.Size = rect.Size; tempContent?.Dispose(); tempContent = null; config.OnLoad?.Invoke(); LoadOK?.Invoke(); if (config.Content is DrawerLoad idrawer) idrawer.LoadOK(); config.Content.SizeChanged += Content_SizeChanged; } bool isok = true; private void Content_SizeChanged(object? sender, EventArgs e) { if (form == null) return; if (isok) { isok = false; var size = config.Content.Size; switch (config.Align) { case TAlignMini.Top: end_H = (int)(size.Height * Config.Dpi) + padding * 2 + 20; if (config.Form is Window windowT) { start_W = end_W = windowT.Width; start_X = end_X = windowT.Left; start_Y = end_Y = windowT.Top; } else { start_W = end_W = config.Form.Width; start_X = end_X = config.Form.Left; start_Y = end_Y = config.Form.Top; } break; case TAlignMini.Bottom: end_H = (int)(size.Height * Config.Dpi) + padding * 2 + 20; if (config.Form is Window windowB) { start_W = end_W = windowB.Width; start_X = end_X = windowB.Left; start_Y = windowB.Top + windowB.Height; } else { start_W = end_W = config.Form.Width; start_X = end_X = config.Form.Left; start_Y = config.Form.Top + config.Form.Height; } end_Y = start_Y - end_H; break; case TAlignMini.Left: end_W = (int)(size.Width * Config.Dpi) + padding * 2 + 20; if (config.Form is Window windowL) { start_H = end_H = windowL.Height; start_X = end_X = windowL.Left; start_Y = end_Y = windowL.Top; } else { start_H = end_H = config.Form.Height; start_X = end_X = config.Form.Left; start_Y = end_Y = config.Form.Top; } break; case TAlignMini.Right: default: end_W = (int)(size.Width * Config.Dpi) + padding * 2 + 20; if (config.Form is Window windowR) { start_H = end_H = windowR.Height; start_X = windowR.Left + windowR.Width; start_Y = end_Y = windowR.Top; } else { start_H = end_H = config.Form.Height; start_X = config.Form.Left + config.Form.Width; start_Y = end_Y = config.Form.Top; } end_X = start_X - end_W; break; } SetLocation(end_X, end_Y); SetSize(end_W, end_H); var rect = Ang(); form.Location = rect.Location; form.Size = rect.Size; Print(); ITask.Run(() => { System.Threading.Thread.Sleep(500); isok = true; }); } } internal Action? LoadOK = null; Rectangle Ang() { switch (config.Align) { case TAlignMini.Top: return new Rectangle(end_X + padding, end_Y + padding, end_W - padding * 2, end_H - 20 - padding * 2); case TAlignMini.Bottom: return new Rectangle(end_X + padding, end_Y + padding + 20, end_W - padding * 2, end_H - 20 - padding * 2); case TAlignMini.Left: return new Rectangle(end_X + padding, end_Y + padding, end_W - 20 - padding * 2, end_H - padding * 2); case TAlignMini.Right: default: return new Rectangle(end_X + padding + 20, end_Y + padding, end_W - 20 - padding * 2, end_H - padding * 2); } } #endregion #region 设置动画参数 void SetAnimateValueX(int x, int w, byte _alpha) { if (TargetRect.X != x || TargetRect.Width != w || alpha != _alpha) { SetLocationX(x); SetSizeW(w); alpha = _alpha; Print(); } } void SetAnimateValueY(int y, int h, byte _alpha) { if (TargetRect.Y != y || TargetRect.Height != h || alpha != _alpha) { SetLocationY(y); SetSizeH(h); alpha = _alpha; Print(); } } void SetAnimateValue(int x, int y, int w, int h, byte _alpha) { if (TargetRect.X != x || TargetRect.Y != y || TargetRect.Width != w || TargetRect.Height != h || alpha != _alpha) { SetLocation(x, y); SetSize(w, h); alpha = _alpha; Print(); } } #endregion protected override void DestroyHandle() { base.DestroyHandle(); formMask?.IClose(); } protected override void OnClosing(CancelEventArgs e) { task_start?.Dispose(); if (!ok_end) { config.Form.LocationChanged -= Form_LocationChanged; config.Form.SizeChanged -= Form_SizeChanged; tempContent = new Bitmap(config.Content.Width, config.Content.Height); config.Content.DrawToBitmap(tempContent, new Rectangle(0, 0, tempContent.Width, tempContent.Height)); form?.Hide(); e.Cancel = true; if (Config.Animation) { if (!run_end) { run_end = true; var t = Animation.TotalFrames(10, 100); new ITask(vertical ? (i) => { var val = Animation.Animate(i, t, 1F, AnimationType.Ball); SetAnimateValueY(end_Y - (int)((end_Y - start_Y) * val), (int)(end_H * (1F - val)), (byte)(255 * (1F - val))); return true; } : (i) => { var val = Animation.Animate(i, t, 1F, AnimationType.Ball); SetAnimateValueX(end_X - (int)((end_X - start_X) * val), (int)(end_W * (1F - val)), (byte)(255 * (1F - val))); return true; }, 10, t, () => { ok_end = true; IClose(true); }); } } else { ok_end = true; IClose(true); } } base.OnClosing(e); } protected override void Dispose(bool disposing) { config.Form.LocationChanged -= Form_LocationChanged; config.Form.SizeChanged -= Form_SizeChanged; if (config.Dispose) config.Content.Dispose(); tempContent?.Dispose(); form?.Dispose(); task_start?.Dispose(); config.OnClose?.Invoke(); config.OnClose = null; base.Dispose(disposing); } public void IRClose() { ok_end = true; IClose(true); } #endregion #region 渲染 public override Bitmap PrintBit() { Rectangle rect; if (HasBor) rect = new Rectangle(FrmBor, 0, TargetRect.Width - FrmBor * 2, TargetRect.Height - FrmBor); else rect = TargetRectXY; var original_bmp = new Bitmap(TargetRect.Width, TargetRect.Height); using (var g = Graphics.FromImage(original_bmp).High()) { var rect_read = DrawShadow(g, rect); using (var path = rect_read.RoundPath(FrmRadius)) { using (var brush = new SolidBrush(Style.Db.BgElevated)) { g.FillPath(brush, path); } if (tempContent != null) g.DrawImage(tempContent, new Rectangle(rect_read.X + padding, rect_read.Y + padding, tempContent.Width, tempContent.Height)); } } return original_bmp; } Bitmap? shadow_temp = null; /// /// 绘制阴影 /// /// GDI /// 客户区域 Rectangle DrawShadow(Graphics g, Rectangle rect) { var matrix = new ColorMatrix { Matrix33 = 0.3F }; switch (config.Align) { case TAlignMini.Top: if (Config.ShadowEnabled) { if (shadow_temp == null || shadow_temp.Width != end_W) { shadow_temp?.Dispose(); using (var path = new Rectangle(rect.X, rect.Y + 20, end_W, 40).RoundPath(FrmRadius)) { shadow_temp = path.PaintShadow(end_W, 80, 20); } } using (var attributes = new ImageAttributes()) { attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); g.DrawImage(shadow_temp, new Rectangle(rect.Y, rect.Bottom - 80, rect.Width, 80), 0, 0, shadow_temp.Width, shadow_temp.Height, GraphicsUnit.Pixel, attributes); } } return new Rectangle(rect.X, rect.Y, rect.Width, rect.Height - 20); case TAlignMini.Bottom: if (Config.ShadowEnabled) { if (shadow_temp == null || shadow_temp.Width != end_W) { shadow_temp?.Dispose(); using (var path = new Rectangle(rect.X, rect.Y + 20, end_W, 40).RoundPath(FrmRadius)) { shadow_temp = path.PaintShadow(end_W, 80, 20); } } using (var attributes = new ImageAttributes()) { attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); g.DrawImage(shadow_temp, new Rectangle(rect.Y, rect.Y, rect.Width, 80), 0, 0, shadow_temp.Width, shadow_temp.Height, GraphicsUnit.Pixel, attributes); } } return new Rectangle(rect.X, rect.Y + 20, rect.Width, rect.Height - 20); case TAlignMini.Left: if (Config.ShadowEnabled) { if (shadow_temp == null || shadow_temp.Height != end_H) { shadow_temp?.Dispose(); using (var path = new Rectangle(rect.X + 20, rect.Y, 40, end_H).RoundPath(FrmRadius)) { shadow_temp = path.PaintShadow(80, end_H, 20); } } using (var attributes = new ImageAttributes()) { attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); g.DrawImage(shadow_temp, new Rectangle(rect.Right - 80, rect.Y, 80, rect.Height), 0, 0, shadow_temp.Width, shadow_temp.Height, GraphicsUnit.Pixel, attributes); } } return new Rectangle(rect.X, rect.Y, rect.Width - 20, rect.Height); case TAlignMini.Right: default: if (Config.ShadowEnabled) { if (shadow_temp == null || shadow_temp.Height != end_H) { shadow_temp?.Dispose(); using (var path = new Rectangle(rect.X + 20, rect.Y, 40, end_H).RoundPath(FrmRadius)) { shadow_temp = path.PaintShadow(80, end_H, 20); } } using (var attributes = new ImageAttributes()) { attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); g.DrawImage(shadow_temp, new Rectangle(rect.X, rect.Y, 80, rect.Height), 0, 0, shadow_temp.Width, shadow_temp.Height, GraphicsUnit.Pixel, attributes); } } return new Rectangle(rect.X + 20, rect.Y, rect.Width - 20, rect.Height); } } #endregion } public interface DrawerLoad { void LoadOK(); } }