// 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.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Design; using System.Windows.Forms; namespace AntdUI { /// /// Tabs 标签页 /// /// 选项卡切换组件。 [Description("Tabs 标签页")] [ToolboxItem(true)] [DefaultEvent("SelectedIndexChanged")] [DefaultProperty("Pages")] [Designer(typeof(TabControlDesigner))] public partial class Tabs : IControl { public Tabs() { style = SetType(type); } #region 属性 Color? fore; /// /// 文字颜色 /// [Description("文字颜色"), Category("外观"), DefaultValue(null)] [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))] public new Color? ForeColor { get => fore; set { if (fore == value) fore = value; fore = value; Invalidate(); } } Color? fill; /// /// 颜色 /// [Description("颜色"), Category("外观"), DefaultValue(null)] [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))] public Color? Fill { get => fill; set { if (fill == value) return; fill = value; Invalidate(); } } /// /// 悬停颜色 /// [Description("悬停颜色"), Category("外观"), DefaultValue(null)] [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))] public Color? FillHover { get; set; } /// /// 激活颜色 /// [Description("激活颜色"), Category("外观"), DefaultValue(null)] [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))] public Color? FillActive { get; set; } TabAlignment alignment = TabAlignment.Top; /// /// 位置 /// [Description("位置"), Category("外观"), DefaultValue(TabAlignment.Top)] public TabAlignment Alignment { get => alignment; set { if (alignment == value) return; alignment = value; LoadLayout(); } } bool centered = false; /// /// 标签居中展示 /// [Description("标签居中展示"), Category("外观"), DefaultValue(false)] public bool Centered { get => centered; set { if (centered == value) return; centered = value; LoadLayout(); } } TabTypExceed typExceed = TabTypExceed.Button; /// /// 超出UI类型 /// [Description("超出UI类型"), Category("外观"), DefaultValue(TabTypExceed.Button)] public TabTypExceed TypExceed { get => typExceed; set { if (typExceed == value) return; typExceed = value; bitblock_l?.Dispose(); bitblock_r?.Dispose(); bitblock_l = bitblock_r = null; LoadLayout(); } } Color? scrollback; /// /// 滚动条颜色 /// [Description("滚动条颜色"), Category("外观"), DefaultValue(null)] [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))] public Color? ScrollBack { get => scrollback; set { if (scrollback == value) return; scrollback = value; bitblock_l?.Dispose(); bitblock_r?.Dispose(); bitblock_l = bitblock_r = null; Invalidate(); } } Color? scrollfore; /// /// 滚动条文本颜色 /// [Description("滚动条文本颜色"), Category("外观"), DefaultValue(null)] [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))] public Color? ScrollFore { get => scrollfore; set { if (scrollfore == value) return; scrollfore = value; Invalidate(); } } /// /// 滚动条悬停文本颜色 /// [Description("滚动条悬停文本颜色"), Category("外观"), DefaultValue(null)] [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))] public Color? ScrollForeHover { get; set; } /// /// 滚动条悬停颜色 /// [Description("滚动条悬停颜色"), Category("外观"), DefaultValue(null)] [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))] public Color? ScrollBackHover { get; set; } #region 样式 IStyle style; /// /// 样式 /// [Description("样式"), Category("外观")] public IStyle Style { get => style; set => style = value; } TabType type = TabType.Line; /// /// 样式类型 /// [Description("样式类型"), Category("外观"), DefaultValue(TabType.Line)] public TabType Type { get => type; set { if (type == value) return; type = value; style = SetType(value); LoadLayout(); } } IStyle SetType(TabType type) { switch (type) { case TabType.Card: if (style is StyleCard stylec) return stylec; return new StyleCard(this); case TabType.Card2: if (style is StyleCard2 stylec2) return stylec2; return new StyleCard2(this); case TabType.Line: default: if (style is StyleLine stylel) return stylel; return new StyleLine(this); } } #endregion int _gap = 8; /// /// 间距 /// [Description("间距"), Category("外观"), DefaultValue(8)] public int Gap { get => _gap; set { if (_gap == value) return; _gap = value; LoadLayout(); } } float iconratio = .7F; /// /// 图标比例 /// [Description("图标比例"), Category("外观"), DefaultValue(.7F)] public float IconRatio { get => iconratio; set { if (iconratio == value) return; iconratio = value; LoadLayout(); } } bool _tabMenuVisible = true; [Description("是否显示头"), Category("外观"), DefaultValue(true)] public bool TabMenuVisible { get => _tabMenuVisible; set { _tabMenuVisible = value; LoadLayout(); } } int? _itemSize = null; /// /// 自定义项宽度 /// [Description("自定义项大小"), Category("外观"), DefaultValue(null)] public int? ItemSize { get => _itemSize; set { if (_itemSize == value) return; _itemSize = value; LoadLayout(); } } internal Dictionary HandItemSize(Dictionary rect_dir, ref int sizewh) { if (_itemSize.HasValue) { int Size = (int)Math.Ceiling(_itemSize.Value * Config.Dpi); var rect_dirtmp = new Dictionary(rect_dir.Count); foreach (var it in rect_dir) rect_dirtmp.Add(it.Key, new Size(Size, it.Value.Height)); if (alignment == TabAlignment.Left || alignment == TabAlignment.Right) sizewh = Size; return rect_dirtmp; } return rect_dir; } public override Rectangle DisplayRectangle { get => ClientRectangle.PaddingRect(Margin, Padding, _padding); } #region 数据 TabCollection? items; /// /// 数据 /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] [Description("集合"), Category("数据")] public TabCollection Pages { get { items ??= new TabCollection(this); return items; } set => items = value.BindData(this); } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public TabPage? SelectedTab { get { if (items == null || items.Count <= _select || _select < 0) return null; return items[_select]; } set { if (items == null || value == null) return; var index = items.IndexOf(value); SelectedIndex = index; } } public void SelectTab(string tabPageName) { if (items == null) return; foreach (var it in items) { if (it.Text == tabPageName) { SelectedTab = it; return; } } } public void SelectTab(TabPage tabPage) { SelectedTab = tabPage; } public void SelectTab(int index) { SelectedIndex = index; } #region 动画 int _select = 0; [Description("选中序号"), Category("数据"), DefaultValue(0)] public int SelectedIndex { get => _select; set { if (_select == value) return; int old = _select; _select = value; style.SelectedIndexChanged(value, old); SelectedIndexChanged?.Invoke(this, new IntEventArgs(value)); Invalidate(); ShowPage(); } } internal void ShowPage() { if (showok) { BeginInvoke(new Action(() => { Controls.Clear(); if (items == null) return; if (items.Count <= _select || _select < 0) return; var item = items[_select]; item.DpiAuto(); Controls.Add(item); })); } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new ControlCollection Controls => base.Controls; protected override void Dispose(bool disposing) { style.Dispose(); base.Dispose(disposing); } #endregion #endregion #endregion #region 布局 bool showok = false; protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); LoadLayout(false); showok = true; ShowPage(); } protected override void OnMarginChanged(EventArgs e) { LoadLayout(true); base.OnMarginChanged(e); } protected override void OnFontChanged(EventArgs e) { LoadLayout(false); base.OnFontChanged(e); } protected override void OnSizeChanged(EventArgs e) { LoadLayout(false); base.OnSizeChanged(e); } Padding _padding = new Padding(0); bool SetPadding(int x, int y, int r, int b) { if (_padding.Left == x && _padding.Top == y && _padding.Right == r && _padding.Bottom == b) return true; _padding.Left = x; _padding.Top = y; _padding.Right = r; _padding.Bottom = b; base.OnSizeChanged(EventArgs.Empty); return false; } internal void LoadLayout(bool r = true) { if (IsHandleCreated) { if (items == null) return; if (_tabMenuVisible) { var rect = ClientRectangle; if (rect.Width > 0 && rect.Height > 0) { var rect_t = rect.DeflateRect(Margin); style.LoadLayout(this, rect_t, items); if (r) Invalidate(); } } else SetPadding(0, 0, 0, 0); } } #endregion #region 渲染 readonly StringFormat s_c = Helper.SF_ALL(), s_f = Helper.SF_NoWrap(); protected override void OnPaint(PaintEventArgs e) { if (items == null || items.Count == 0 || !_tabMenuVisible) return; var g = e.Graphics.High(); style.Paint(this, g, items); base.OnPaint(e); } void PaintBadge(Graphics g, TabPage page, Rectangle rect) { if (page.Badge != null) { var color = page.BadgeBack ?? AntdUI.Style.Db.Error; using (var brush_fore = new SolidBrush(AntdUI.Style.Db.ErrorColor)) { float borsize = 1F * Config.Dpi; using (var font = new Font(Font.FontFamily, Font.Size * page.BadgeSize)) { if (string.IsNullOrEmpty(page.Badge) || page.Badge == "" || page.Badge == " ") { var size = (int)Math.Floor(g.MeasureString(Config.NullText, font).Width / 2); var rect_badge = new RectangleF(rect.Right - size - page.BadgeOffsetX * Config.Dpi, rect.Y + page.BadgeOffsetY * Config.Dpi, size, size); using (var brush = new SolidBrush(color)) { g.FillEllipse(brush, rect_badge); using (var pen = new Pen(brush_fore.Color, borsize)) { g.DrawEllipse(pen, rect_badge); } } } else { var size = g.MeasureString(page.Badge, font); var size_badge = size.Height * 1.2F; if (size.Height > size.Width) { var rect_badge = new RectangleF(rect.Right - size_badge - page.BadgeOffsetX * Config.Dpi, rect.Y + page.BadgeOffsetY * Config.Dpi, size_badge, size_badge); using (var brush = new SolidBrush(color)) { g.FillEllipse(brush, rect_badge); using (var pen = new Pen(brush_fore.Color, borsize)) { g.DrawEllipse(pen, rect_badge); } } g.DrawStr(page.Badge, font, brush_fore, rect_badge, s_f); } else { var w_badge = size.Width * 1.2F; var rect_badge = new RectangleF(rect.Right - w_badge - page.BadgeOffsetX * Config.Dpi, rect.Y + page.BadgeOffsetY * Config.Dpi, w_badge, size_badge); using (var brush = new SolidBrush(color)) { using (var path = rect_badge.RoundPath(rect_badge.Height)) { g.FillPath(brush, path); using (var pen = new Pen(brush_fore.Color, borsize)) { g.DrawPath(pen, path); } } } g.DrawStr(page.Badge, font, brush_fore, rect_badge, s_f); } } } } } } #endregion #region 鼠标 protected override void OnMouseDown(MouseEventArgs e) { if (items == null) { base.OnMouseDown(e); return; } if (MouseDownPre(e.X, e.Y)) return; if (_tabMenuVisible) { int i = 0, x = e.X + scroll_x, y = e.Y + scroll_y; foreach (var item in items) { if (item.Visible && item.Contains(x, y)) { item.MDown = true; Invalidate(); return; } i++; } } base.OnMouseDown(e); } protected override void OnMouseUp(MouseEventArgs e) { if (items == null) { base.OnMouseUp(e); return; } if (_tabMenuVisible) { int i = 0, x = e.X + scroll_x, y = e.Y + scroll_y; foreach (var item in items) { if (item.MDown) { item.MDown = false; if (item.Contains(x, y)) { if (style.MouseClick(item, i, x, y)) return; SelectedIndex = i; } else Invalidate(); return; } i++; } } base.OnMouseUp(e); } int hover_i = -1; int Hover_i { get => hover_i; set { if (hover_i == value) return; hover_i = value; Invalidate(); } } protected override void OnMouseMove(MouseEventArgs e) { if (items == null) return; if (MouseMovePre(e.X, e.Y)) { Hover_i = -1; SetCursor(true); base.OnMouseMove(e); return; } int i = 0, x = e.X + scroll_x, y = e.Y + scroll_y; foreach (var item in items) { if (item.Visible && item.Contains(x, y)) { SetCursor(true); Hover_i = i; style.MouseMove(x, y); return; } i++; } style.MouseMove(x, y); SetCursor(false); base.OnMouseMove(e); } protected override void OnMouseLeave(EventArgs e) { style.MouseLeave(); Hover_l = Hover_r = false; Hover_i = -1; SetCursor(false); base.OnMouseLeave(e); } protected override void OnMouseWheel(MouseEventArgs e) { style.MouseWheel(e.Delta); base.OnMouseWheel(e); } #endregion #region 滚动条 internal bool scroll_show = false; int _scroll_x = 0, _scroll_y = 0, scroll_max = 0; int scroll_x { get => _scroll_x; set { if (value < 0) value = 0; else if (scroll_max > 0 && value > scroll_max) value = scroll_max; if (value == _scroll_x) return; _scroll_x = value; Invalidate(); } } int scroll_y { get => _scroll_y; set { if (value < 0) value = 0; else if (scroll_max > 0 && value > scroll_max) value = scroll_max; if (value == _scroll_y) return; _scroll_y = value; Invalidate(); } } #region 超出 LayeredFormSelectDown? subForm = null; bool MouseMovePre(int x, int y) { if (items == null) return false; switch (typExceed) { case TabTypExceed.Button: if (scroll_show && rect_r.Contains(x, y)) { if (subForm == null) { var objs = new List(items.Count); foreach (var item in items) objs.Add(new SelectItem(item.Text, item)); subForm = new LayeredFormSelectDown(this, 6, objs.ToArray(), SelectedTab, rect_r); subForm.Disposed += (a, b) => { subForm = null; }; subForm.MouseEnter += (a, b) => { if (a is LayeredFormSelectDown form) form.tag1 = false; }; subForm.MouseLeave += (a, b) => { if (a is LayeredFormSelectDown form) form.IClose(); }; subForm.Leave += (a, b) => { if (a is LayeredFormSelectDown form) form.IClose(); }; subForm.Show(this); } return true; } else { subForm?.IClose(); subForm = null; } break; case TabTypExceed.LR: case TabTypExceed.LR_Shadow: if (scroll_show) { if (MouseDownLRL(x, y, out bool lr)) { Hover_l = true; return true; } else Hover_l = false; if (MouseDownLRR(x, y, out _)) { Hover_r = true; return true; } else Hover_r = false; } break; } return false; } bool MouseDownPre(int x, int y) { if (items == null) return false; if (scroll_show && (typExceed == TabTypExceed.LR || typExceed == TabTypExceed.LR_Shadow)) { if (MouseDownLRL(x, y, out bool lr)) { if (lr) scroll_y -= 120; else scroll_x -= 120; return true; } else if (MouseDownLRR(x, y, out bool lr2)) { if (lr2) scroll_y += 120; else scroll_x += 120; return true; } } return false; } bool MouseDownLRL(int x, int y, out bool lr) { switch (alignment) { case TabAlignment.Left: case TabAlignment.Right: lr = true; if (scroll_y > 0 && rect_l.Contains(x, y)) return true; return false; case TabAlignment.Top: case TabAlignment.Bottom: default: lr = false; if (scroll_x > 0 && rect_l.Contains(x, y)) return true; return false; } } bool MouseDownLRR(int x, int y, out bool lr) { switch (alignment) { case TabAlignment.Left: case TabAlignment.Right: lr = true; if (scroll_max != scroll_y && rect_r.Contains(x, y)) return true; return false; case TabAlignment.Top: case TabAlignment.Bottom: default: lr = false; if (scroll_max != scroll_x && rect_r.Contains(x, y)) return true; return false; } } #region 绘制超出部分 public virtual int SizeExceed(Rectangle rect, Rectangle first, Rectangle last) { switch (typExceed) { case TabTypExceed.Button: int size = last.Height; switch (alignment) { case TabAlignment.Left: case TabAlignment.Right: rect_r = new Rectangle(last.X, rect.Bottom - size, last.Width, size); break; case TabAlignment.Top: case TabAlignment.Bottom: default: rect_r = new Rectangle(rect.Right - size, last.Y, size, size); break; } return size; case TabTypExceed.None: default: rect_r = Rectangle.Empty; return 0; } } public virtual Rectangle PaintExceedPre(Rectangle rect, int size) { switch (typExceed) { case TabTypExceed.Button: switch (alignment) { case TabAlignment.Left: case TabAlignment.Right: if (scroll_max != scroll_y) return new Rectangle(rect.X, rect.Y, rect.Width, rect.Height - size); else return rect; case TabAlignment.Top: case TabAlignment.Bottom: default: if (scroll_max != scroll_x) return new Rectangle(rect.X, rect.Y, rect.Width - size, rect.Height); else return rect; } case TabTypExceed.LR: case TabTypExceed.LR_Shadow: switch (alignment) { case TabAlignment.Left: case TabAlignment.Right: int sizeLR2 = (int)(size * .6F); if (scroll_max != scroll_y) { if (scroll_y > 0) return new Rectangle(rect.X, rect.Y + sizeLR2, rect.Width, rect.Height - sizeLR2 * 2); else return new Rectangle(rect.X, rect.Y, rect.Width, rect.Height - sizeLR2); } else if (scroll_y > 0) return new Rectangle(rect.X, rect.Y + sizeLR2, rect.Width, rect.Height - sizeLR2); else return rect; case TabAlignment.Top: case TabAlignment.Bottom: default: int sizeLR = (int)(size * .6F); if (scroll_max != scroll_x) { if (scroll_x > 0) return new Rectangle(rect.X + sizeLR, rect.Y, rect.Width - sizeLR * 2, rect.Height); else return new Rectangle(rect.X, rect.Y, rect.Width - sizeLR, rect.Height); } else if (scroll_x > 0) return new Rectangle(rect.X + sizeLR, rect.Y, rect.Width - sizeLR, rect.Height); else return rect; } case TabTypExceed.None: default: return rect; } } Bitmap? bitblock_l = null, bitblock_r = null; public virtual void PaintExceed(Graphics g, Color color, int radius, Rectangle rect, Rectangle first, Rectangle last, bool full) { switch (typExceed) { case TabTypExceed.Button: PaintExceedButton(g, color, radius, rect, first, last, full); break; case TabTypExceed.LR: PaintExceedLR(g, color, radius, rect, first, last, full); break; case TabTypExceed.LR_Shadow: PaintExceedLR_Shadow(g, color, radius, rect, first, last, full); break; case TabTypExceed.None: default: break; } } public virtual void PaintExceedButton(Graphics g, Color color, int radius, Rectangle rect, Rectangle first, Rectangle last, bool full) { g.ResetClip(); g.ResetTransform(); switch (alignment) { case TabAlignment.Left: case TabAlignment.Right: if (scroll_y > 0 || scroll_max != scroll_y) { int gap = (int)(_gap * Config.Dpi), gap2 = gap * 2; int size = last.Height, icosize = (int)(size * 0.4F); var rect_cr = new Rectangle(last.X, rect.Bottom - size, last.Width, size); if (scroll_y > 0) { var rect_l = new Rectangle(first.X, first.Y, rect_cr.Width, gap2); if (full) rect_l.Y = 0; if (bitblock_l == null || bitblock_l.Width != rect_l.Width || bitblock_l.Height != rect_l.Height) { bitblock_l?.Dispose(); bitblock_l = new Bitmap(rect_l.Width, rect_l.Height); using (var g_bmp = Graphics.FromImage(bitblock_l).HighLay()) { using (var brush = new SolidBrush(color)) { using (var path = new Rectangle(0, 0, bitblock_l.Width, gap).RoundPath(gap, false, false, true, true)) { g_bmp.FillPath(brush, path); } } } Helper.Blur(bitblock_l, gap); } g.DrawImage(bitblock_l, rect_l, .1F); } if (scroll_max != scroll_y) { var rect_r = new Rectangle(rect_cr.X, rect_cr.Y - gap2, rect_cr.Width, gap2); if (bitblock_r == null || bitblock_r.Width != rect_r.Width || bitblock_r.Height != rect_r.Height) { bitblock_r?.Dispose(); bitblock_r = new Bitmap(rect_r.Width, rect_r.Height); using (var g_bmp = Graphics.FromImage(bitblock_r).HighLay()) { using (var brush = new SolidBrush(color)) { using (var path = new Rectangle(0, gap, bitblock_r.Width, gap).RoundPath(gap, true, true, false, false)) { g_bmp.FillPath(brush, path); } } } Helper.Blur(bitblock_r, gap); } g.DrawImage(bitblock_r, rect_r, .1F); } var rect_ico = new Rectangle(rect_cr.X + (rect_cr.Width - icosize) / 2, rect_cr.Y + (rect_cr.Height - icosize) / 2, icosize, icosize); g.GetImgExtend(SvgDb.IcoMore, rect_ico, color); } break; case TabAlignment.Top: case TabAlignment.Bottom: default: if (scroll_x > 0 || scroll_max != scroll_x) { int gap = (int)(_gap * Config.Dpi), gap2 = gap * 2; int size = last.Height, icosize = (int)(size * 0.4F); var rect_cr = new Rectangle(rect.Right - size, last.Y, size, size); if (scroll_x > 0) { var rect_l = new Rectangle(first.X, first.Y, gap2, size); if (full) rect_l.X = 0; if (bitblock_l == null || bitblock_l.Width != rect_l.Width || bitblock_l.Height != rect_l.Height) { bitblock_l?.Dispose(); bitblock_l = new Bitmap(rect_l.Width, rect_l.Height); using (var g_bmp = Graphics.FromImage(bitblock_l).HighLay()) { using (var brush = new SolidBrush(color)) { using (var path = new Rectangle(0, 0, gap, bitblock_l.Height).RoundPath(gap, false, true, true, false)) { g_bmp.FillPath(brush, path); } } } Helper.Blur(bitblock_l, gap); } g.DrawImage(bitblock_l, rect_l, .1F); } if (scroll_max != scroll_x) { var rect_r = new Rectangle(rect_cr.X - gap2, rect_cr.Y, gap2, size); if (bitblock_r == null || bitblock_r.Width != rect_r.Width || bitblock_r.Height != rect_r.Height) { bitblock_r?.Dispose(); bitblock_r = new Bitmap(rect_r.Width, rect_r.Height); using (var g_bmp = Graphics.FromImage(bitblock_r).HighLay()) { using (var brush = new SolidBrush(color)) { using (var path = new Rectangle(gap, 0, gap, bitblock_r.Height).RoundPath(gap, true, false, false, true)) { g_bmp.FillPath(brush, path); } } } Helper.Blur(bitblock_r, gap); } g.DrawImage(bitblock_r, rect_r, .1F); } var rect_ico = new Rectangle(rect_cr.X + (rect_cr.Width - icosize) / 2, rect_cr.Y + (rect_cr.Height - icosize) / 2, icosize, icosize); g.GetImgExtend(SvgDb.IcoMore, rect_ico, color); } break; } } public virtual void PaintExceedLR(Graphics g, Color color, int radius, Rectangle rect, Rectangle first, Rectangle last, bool full) { g.ResetClip(); g.ResetTransform(); switch (alignment) { case TabAlignment.Left: case TabAlignment.Right: if (scroll_y > 0 || scroll_max != scroll_y) { int size = (int)(last.Height * .6F); using (var brush = new SolidBrush(scrollback ?? AntdUI.Style.Db.FillSecondary)) using (var brush_hover = new SolidBrush(ScrollBackHover ?? AntdUI.Style.Db.Primary)) using (var pen = new Pen(scrollfore ?? color, 2F * Config.Dpi)) using (var pen_hover = new Pen(ScrollForeHover ?? AntdUI.Style.Db.PrimaryColor, 2F * Config.Dpi)) { if (scroll_y > 0) { rect_l = new Rectangle(last.X, rect.Y, last.Width, size); using (var path = Helper.RoundPath(rect_l, radius)) { if (hover_l) { g.FillPath(brush_hover, path); g.DrawLines(pen_hover, TAlignMini.Top.TriangleLines(rect_l, .5F)); } else { g.FillPath(brush, path); g.DrawLines(pen, TAlignMini.Top.TriangleLines(rect_l, .5F)); } } } if (scroll_max != scroll_y) { rect_r = new Rectangle(last.X, rect.Y + rect.Height - size, last.Width, size); using (var path = Helper.RoundPath(rect_r, radius)) { if (hover_r) { g.FillPath(brush_hover, path); g.DrawLines(pen_hover, TAlignMini.Bottom.TriangleLines(rect_r, .5F)); } else { g.FillPath(brush, path); g.DrawLines(pen, TAlignMini.Bottom.TriangleLines(rect_r, .5F)); } } } } } break; case TabAlignment.Top: case TabAlignment.Bottom: default: if (scroll_x > 0 || scroll_max != scroll_x) { int size = (int)(last.Height * .6F); using (var brush = new SolidBrush(scrollback ?? AntdUI.Style.Db.FillSecondary)) using (var brush_hover = new SolidBrush(ScrollBackHover ?? AntdUI.Style.Db.Primary)) using (var pen = new Pen(scrollfore ?? color, 2F * Config.Dpi)) using (var pen_hover = new Pen(ScrollForeHover ?? AntdUI.Style.Db.PrimaryColor, 2F * Config.Dpi)) { if (scroll_x > 0) { rect_l = new Rectangle(rect.X, last.Y, size, last.Height); using (var path = Helper.RoundPath(rect_l, radius)) { if (hover_l) { g.FillPath(brush_hover, path); g.DrawLines(pen_hover, TAlignMini.Left.TriangleLines(rect_l, .5F)); } else { g.FillPath(brush, path); g.DrawLines(pen, TAlignMini.Left.TriangleLines(rect_l, .5F)); } } } if (scroll_max != scroll_x) { rect_r = new Rectangle(rect.X + rect.Width - size, last.Y, size, last.Height); using (var path = Helper.RoundPath(rect_r, radius)) { if (hover_r) { g.FillPath(brush_hover, path); g.DrawLines(pen_hover, TAlignMini.Right.TriangleLines(rect_r, .5F)); } else { g.FillPath(brush, path); g.DrawLines(pen, TAlignMini.Right.TriangleLines(rect_r, .5F)); } } } } } break; } } public virtual void PaintExceedLR_Shadow(Graphics g, Color color, int radius, Rectangle rect, Rectangle first, Rectangle last, bool full) { g.ResetClip(); g.ResetTransform(); switch (alignment) { case TabAlignment.Left: case TabAlignment.Right: if (scroll_y > 0 || scroll_max != scroll_y) { int gap = (int)(_gap * Config.Dpi), gap2 = gap * 2, size = (int)(last.Height * .6F); using (var brush = new SolidBrush(scrollback ?? AntdUI.Style.Db.FillSecondary)) using (var brush_hover = new SolidBrush(ScrollBackHover ?? AntdUI.Style.Db.Primary)) using (var pen = new Pen(scrollfore ?? color, 2F * Config.Dpi)) using (var pen_hover = new Pen(ScrollForeHover ?? AntdUI.Style.Db.PrimaryColor, 2F * Config.Dpi)) { if (scroll_y > 0) { rect_l = new Rectangle(last.X, rect.Y, last.Width, size); var Rect_l = new Rectangle(rect_l.X, rect_l.Bottom, rect_l.Width, gap2); if (bitblock_l == null || bitblock_l.Width != Rect_l.Width || bitblock_l.Height != Rect_l.Height) { bitblock_l?.Dispose(); bitblock_l = new Bitmap(Rect_l.Width, Rect_l.Height); using (var g_bmp = Graphics.FromImage(bitblock_l).HighLay()) { using (var path = new Rectangle(0, 0, bitblock_l.Width, gap).RoundPath(gap, false, false, true, true)) { g_bmp.FillPath(brush, path); } } Helper.Blur(bitblock_l, gap); } g.DrawImage(bitblock_l, Rect_l, .1F); using (var path = Helper.RoundPath(rect_l, radius, true, true, false, false)) { if (hover_l) { g.FillPath(brush_hover, path); g.DrawLines(pen_hover, TAlignMini.Top.TriangleLines(rect_l, .5F)); } else { g.FillPath(brush, path); g.DrawLines(pen, TAlignMini.Top.TriangleLines(rect_l, .5F)); } } } if (scroll_max != scroll_y) { rect_r = new Rectangle(last.X, rect.Y + rect.Height - size, last.Width, size); var Rect_r = new Rectangle(rect_r.X, rect_r.Y - gap2, rect_r.Width, gap2); if (bitblock_r == null || bitblock_r.Width != Rect_r.Width || bitblock_r.Height != Rect_r.Height) { bitblock_r?.Dispose(); bitblock_r = new Bitmap(Rect_r.Width, Rect_r.Height); using (var g_bmp = Graphics.FromImage(bitblock_r).HighLay()) { using (var path = new Rectangle(0, gap, bitblock_r.Width, gap).RoundPath(gap, true, true, false, false)) { g_bmp.FillPath(brush, path); } } Helper.Blur(bitblock_r, gap); } g.DrawImage(bitblock_r, Rect_r, .1F); using (var path = Helper.RoundPath(rect_r, radius, false, false, true, true)) { if (hover_r) { g.FillPath(brush_hover, path); g.DrawLines(pen_hover, TAlignMini.Bottom.TriangleLines(rect_r, .5F)); } else { g.FillPath(brush, path); g.DrawLines(pen, TAlignMini.Bottom.TriangleLines(rect_r, .5F)); } } } } } break; case TabAlignment.Top: case TabAlignment.Bottom: default: if (scroll_x > 0 || scroll_max != scroll_x) { int gap = (int)(_gap * Config.Dpi), gap2 = gap * 2, size = (int)(last.Height * .6F); using (var brush = new SolidBrush(scrollback ?? AntdUI.Style.Db.FillSecondary)) using (var brush_hover = new SolidBrush(ScrollBackHover ?? AntdUI.Style.Db.Primary)) using (var pen = new Pen(scrollfore ?? color, 2F * Config.Dpi)) using (var pen_hover = new Pen(ScrollForeHover ?? AntdUI.Style.Db.PrimaryColor, 2F * Config.Dpi)) { if (scroll_x > 0) { rect_l = new Rectangle(rect.X, last.Y, size, last.Height); var Rect_l = new Rectangle(rect_l.Right, rect_l.Y, gap2, rect_l.Height); if (bitblock_l == null || bitblock_l.Width != Rect_l.Width || bitblock_l.Height != Rect_l.Height) { bitblock_l?.Dispose(); bitblock_l = new Bitmap(Rect_l.Width, Rect_l.Height); using (var g_bmp = Graphics.FromImage(bitblock_l).HighLay()) { using (var path = new Rectangle(0, 0, gap, bitblock_l.Height).RoundPath(gap, false, true, true, false)) { g_bmp.FillPath(brush, path); } } Helper.Blur(bitblock_l, gap); } g.DrawImage(bitblock_l, Rect_l, .1F); using (var path = Helper.RoundPath(rect_l, radius, true, false, false, true)) { if (hover_l) { g.FillPath(brush_hover, path); g.DrawLines(pen_hover, TAlignMini.Left.TriangleLines(rect_l, .5F)); } else { g.FillPath(brush, path); g.DrawLines(pen, TAlignMini.Left.TriangleLines(rect_l, .5F)); } } } if (scroll_max != scroll_x) { rect_r = new Rectangle(rect.X + rect.Width - size, last.Y, size, last.Height); var Rect_r = new Rectangle(rect_r.X - gap2, rect_r.Y, gap2, rect_r.Height); if (bitblock_r == null || bitblock_r.Width != Rect_r.Width || bitblock_r.Height != Rect_r.Height) { bitblock_r?.Dispose(); bitblock_r = new Bitmap(Rect_r.Width, Rect_r.Height); using (var g_bmp = Graphics.FromImage(bitblock_r).HighLay()) { using (var path = new Rectangle(gap, 0, gap, bitblock_r.Height).RoundPath(gap, true, false, false, true)) { g_bmp.FillPath(brush, path); } } Helper.Blur(bitblock_r, gap); } g.DrawImage(bitblock_r, Rect_r, .1F); using (var path = Helper.RoundPath(rect_r, radius, false, true, true, false)) { if (hover_r) { g.FillPath(brush_hover, path); g.DrawLines(pen_hover, TAlignMini.Right.TriangleLines(rect_r, .5F)); } else { g.FillPath(brush, path); g.DrawLines(pen, TAlignMini.Right.TriangleLines(rect_r, .5F)); } } } } } break; } } Rectangle rect_l, rect_r; bool hover_l, hover_r = false; bool Hover_l { get => hover_l; set { if (hover_l == value) return; hover_l = value; Invalidate(); } } bool Hover_r { get => hover_r; set { if (hover_r == value) return; hover_r = value; Invalidate(); } } #endregion #endregion #endregion #region 事件 /// /// SelectedIndex 属性值更改时发生 /// [Description("SelectedIndex 属性值更改时发生"), Category("行为")] public event IntEventHandler? SelectedIndexChanged = null; /// /// 关闭页面前发生 /// [Description("关闭页面前发生"), Category("行为")] public event ClosingPageEventHandler? ClosingPage; #endregion } public class TabCollection : iCollection { public TabCollection(Tabs it) { BindData(it); } internal TabCollection BindData(Tabs it) { action = render => { if (render) it.LoadLayout(false); it.Invalidate(); }; action_add = item => { item.PARENT = it; item.Dock = DockStyle.Fill; it.ShowPage(); }; action_del = item => { int old = it.SelectedIndex; int index = IndexOf(item); if (old == index) { it.Controls.Remove(item); int _new = index - 1; if (_new > -1) it.SelectedIndex = _new; else it.ShowPage(); } else { it.Controls.Remove(item); it.SelectedIndex = old - 1; } }; return this; } } [ToolboxItem(false)] [Designer(typeof(IControlDesigner))] public class TabPage : ScrollableControl { public TabPage() { SetStyle( ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.DoubleBuffer | ControlStyles.SupportsTransparentBackColor | ControlStyles.ContainerControl | ControlStyles.UserPaint, true); UpdateStyles(); } #region 属性 Image? icon = null; /// /// 图标 /// [Category("外观"), Description("图标"), DefaultValue(null)] public Image? Icon { get => icon; set { if (icon == value) return; icon = value; PARENT?.LoadLayout(); } } string? iconSvg = null; /// /// 图标 /// [Category("外观"), Description("图标SVG"), DefaultValue(null)] public string? IconSvg { get => iconSvg; set { if (iconSvg == value) return; iconSvg = value; PARENT?.LoadLayout(); } } /// /// 是否包含图标 /// public bool HasIcon { get => iconSvg != null || icon != null; } bool readOnly = false; /// /// 只读 /// [Description("只读"), Category("行为"), DefaultValue(false)] public bool ReadOnly { get => readOnly; set { if (readOnly == value) return; readOnly = value; PARENT?.LoadLayout(); } } #region 徽标 string? badge = null; [Description("徽标内容"), Category("徽标"), DefaultValue(null)] public string? Badge { get => badge; set { if (badge == value) return; badge = value; Invalidate(); } } float badgeSize = .6F; [Description("徽标比例"), Category("徽标"), DefaultValue(.6F)] public float BadgeSize { get => badgeSize; set { if (badgeSize != value) { badgeSize = value; if (badge != null) PARENT?.Invalidate(); } } } Color? badgeback = null; [Description("徽标背景颜色"), Category("徽标"), DefaultValue(null)] public Color? BadgeBack { get => badgeback; set { if (badgeback == value) return; if (badgeback != value) { badgeback = value; if (badge != null) PARENT?.Invalidate(); } } } /// /// 徽标偏移X /// [Description("徽标偏移X"), Category("徽标"), DefaultValue(1)] public int BadgeOffsetX { get; set; } = 1; /// /// 徽标偏移Y /// [Description("徽标偏移Y"), Category("徽标"), DefaultValue(1)] public int BadgeOffsetY { get; set; } = 1; #endregion #endregion #region 坐标 internal bool MDown = false; internal Rectangle Rect = new Rectangle(-10, -10, 0, 0); internal bool Contains(int x, int y) { return Rect.Contains(x, y); } internal Rectangle SetRect(Rectangle rect) { Rect = rect; return Rect; } internal Rectangle SetOffset(int x, int y) { Rect.Offset(x, y); return Rect; } #endregion #region 变更 internal Tabs? PARENT; protected override void OnTextChanged(EventArgs e) { PARENT?.LoadLayout(); base.OnTextChanged(e); } protected override void OnVisibleChanged(EventArgs e) { PARENT?.LoadLayout(); base.OnVisibleChanged(e); } bool isdpi = false; public void DpiAuto() { if (isdpi) return; isdpi = true; if (DesignMode) return; if (Config.Dpi != 1F) { SuspendLayout(); Helper.DpiAuto(Config.Dpi, this); ResumeLayout(); } } #endregion public override string ToString() => Text; } }