// 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.Design; using System.Windows.Forms; namespace AntdUI { /// /// Segmented 分段控制器 /// /// 分段控制器。 [Description("Segmented 分段控制器")] [ToolboxItem(true)] [DefaultProperty("Items")] [DefaultEvent("SelectIndexChanged")] public class Segmented : IControl { public Segmented() { base.BackColor = Color.Transparent; } #region 属性 #region 线条 bool barstyle = false; [Description("线条样式"), Category("外观"), DefaultValue(false)] public bool BarStyle { get => barstyle; set { if (barstyle == value) return; barstyle = value; Invalidate(); } } float barsize = 3F; /// /// 条大小 /// [Description("条大小"), Category("条"), DefaultValue(3F)] public float BarSize { get => barsize; set { if (barsize == value) return; barsize = value; if (barstyle) Invalidate(); } } int barpadding = 0; /// /// 条边距 /// [Description("条边距"), Category("条"), DefaultValue(0)] public int BarPadding { get => barpadding; set { if (barpadding == value) return; barpadding = value; if (barstyle) Invalidate(); } } /// /// 条圆角 /// [Description("条圆角"), Category("条"), DefaultValue(0)] public int BarRadius { get; set; } #endregion bool vertical = false; /// /// 是否竖向 /// [Description("是否竖向"), Category("外观"), DefaultValue(false)] public bool Vertical { get => vertical; set { if (vertical == value) return; vertical = value; ChangeItems(); Invalidate(); } } bool full = false; /// /// 是否铺满 /// [Description("是否铺满"), Category("外观"), DefaultValue(false)] public bool Full { get => full; set { if (full == value) return; full = value; ChangeItems(); Invalidate(); } } int radius = 6; /// /// 圆角 /// [Description("圆角"), Category("外观"), DefaultValue(6)] public int Radius { get => radius; set { if (radius == value) return; radius = value; Invalidate(); } } bool round = false; /// /// 圆角样式 /// [Description("圆角样式"), Category("外观"), DefaultValue(false)] public bool Round { get => round; set { if (round == value) return; round = value; Invalidate(); } } TAlignMini iconalign = TAlignMini.Top; [Description("图标对齐方向"), Category("外观"), DefaultValue(TAlignMini.Top)] public TAlignMini IconAlign { get => iconalign; set { if (iconalign == value) return; iconalign = value; ChangeItems(); Invalidate(); } } int igap = 0; [Description("间距"), Category("外观"), DefaultValue(0)] public int Gap { get => igap; set { if (igap == value) return; igap = value; ChangeItems(); Invalidate(); } } Color? back; /// /// 背景颜色 /// [Description("背景颜色"), Category("外观"), DefaultValue(null)] [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))] public new Color? BackColor { get => back; set { if (back == value) return; back = value; Invalidate(); } } /// /// 悬停背景颜色 /// [Description("悬停背景颜色"), Category("外观"), DefaultValue(null)] [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))] public Color? BackHover { get; set; } Color? backactive; /// /// 激活背景颜色 /// [Description("激活背景颜色"), Category("外观"), DefaultValue(null)] [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))] public Color? BackActive { get => backactive; set { if (backactive == value) return; backactive = value; Invalidate(); } } 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(); } } /// /// 悬停文字颜色 /// [Description("悬停文字颜色"), Category("外观"), DefaultValue(null)] [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))] public Color? ForeHover { get; set; } Color? foreactive; /// /// 激活文字颜色 /// [Description("激活文字颜色"), Category("外观"), DefaultValue(null)] [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))] public Color? ForeActive { get => foreactive; set { if (foreactive == value) return; foreactive = value; Invalidate(); } } SegmentedItemCollection? items; /// /// 获取列表中所有列表项的集合 /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] [Description("集合"), Category("数据"), DefaultValue(null)] public SegmentedItemCollection Items { get { items ??= new SegmentedItemCollection(this); return items; } set => items = value.BindData(this); } int _select = -1; /// /// 选择序号 /// [Description("选择序号"), Category("数据"), DefaultValue(-1)] public int SelectIndex { get => _select; set { if (_select == value) return; var old = _select; _select = value; SelectIndexChanged?.Invoke(this, new IntEventArgs(value)); SetRect(old, _select); } } protected override void Dispose(bool disposing) { ThreadBar?.Dispose(); base.Dispose(disposing); } bool AnimationBar = false; RectangleF AnimationBarValue; ITask? ThreadBar = null; RectangleF TabSelectRect; void SetRect(int old, int value) { if (items == null || items.Count == 0) return; if (items.ListExceed(value)) { Invalidate(); return; } var _new = items[value]; if (items.ListExceed(old)) { AnimationBarValue = TabSelectRect = _new.Rect; Invalidate(); return; } var _old = items[old]; ThreadBar?.Dispose(); RectangleF OldValue = _old.Rect, NewValue = _new.Rect; if (Config.Animation) { if (vertical) { if (OldValue.X == NewValue.X) { AnimationBar = true; TabSelectRect = NewValue; float p_val = Math.Abs(NewValue.Y - AnimationBarValue.Y) * 0.09F, p_w_val = Math.Abs(NewValue.Height - AnimationBarValue.Height) * 0.1F, p_val2 = (NewValue.Y - AnimationBarValue.Y) * 0.5F; ThreadBar = new ITask(this, () => { if (AnimationBarValue.Height != NewValue.Height) { if (NewValue.Height > OldValue.Height) { AnimationBarValue.Height += p_w_val; if (AnimationBarValue.Height > NewValue.Height) AnimationBarValue.Height = NewValue.Height; } else { AnimationBarValue.Height -= p_w_val; if (AnimationBarValue.Height < NewValue.Height) AnimationBarValue.Height = NewValue.Height; } } if (NewValue.Y > OldValue.Y) { if (AnimationBarValue.Y > p_val2) AnimationBarValue.Y += p_val / 2F; else AnimationBarValue.Y += p_val; if (AnimationBarValue.Y > NewValue.Y) { AnimationBarValue.Y = NewValue.Y; Invalidate(); return false; } } else { AnimationBarValue.Y -= p_val; if (AnimationBarValue.Y < NewValue.Y) { AnimationBarValue.Y = NewValue.Y; Invalidate(); return false; } } Invalidate(); return true; }, 10, () => { AnimationBarValue = NewValue; AnimationBar = false; Invalidate(); }); } } else { if (OldValue.Y == NewValue.Y) { AnimationBar = true; TabSelectRect = NewValue; float p_val = Math.Abs(NewValue.X - AnimationBarValue.X) * 0.09F, p_w_val = Math.Abs(NewValue.Width - AnimationBarValue.Width) * 0.1F, p_val2 = (NewValue.X - AnimationBarValue.X) * 0.5F; ThreadBar = new ITask(this, () => { if (AnimationBarValue.Width != NewValue.Width) { if (NewValue.Width > OldValue.Width) { AnimationBarValue.Width += p_w_val; if (AnimationBarValue.Width > NewValue.Width) AnimationBarValue.Width = NewValue.Width; } else { AnimationBarValue.Width -= p_w_val; if (AnimationBarValue.Width < NewValue.Width) AnimationBarValue.Width = NewValue.Width; } } if (NewValue.X > OldValue.X) { if (AnimationBarValue.X > p_val2) AnimationBarValue.X += p_val / 2F; else AnimationBarValue.X += p_val; if (AnimationBarValue.X > NewValue.X) { AnimationBarValue.X = NewValue.X; Invalidate(); return false; } } else { AnimationBarValue.X -= p_val; if (AnimationBarValue.X < NewValue.X) { AnimationBarValue.X = NewValue.X; Invalidate(); return false; } } Invalidate(); return true; }, 10, () => { AnimationBarValue = NewValue; AnimationBar = false; Invalidate(); }); } } return; } else { TabSelectRect = AnimationBarValue = NewValue; Invalidate(); return; } } /// /// SelectIndex 属性值更改时发生 /// [Description("SelectIndex 属性值更改时发生"), Category("行为")] public event IntEventHandler? SelectIndexChanged = null; /// /// 点击项时发生 /// [Description("点击项时发生"), Category("行为")] public event SegmentedItemEventHandler? ItemClick = null; #region Change protected override void OnSizeChanged(EventArgs e) { ChangeItems(); base.OnSizeChanged(e); } protected override void OnMarginChanged(EventArgs e) { ChangeItems(); base.OnMarginChanged(e); } protected override void OnPaddingChanged(EventArgs e) { ChangeItems(); base.OnPaddingChanged(e); } protected override void OnFontChanged(EventArgs e) { ChangeItems(); base.OnFontChanged(e); } #endregion #region 布局 bool pauseLayout = false; [Browsable(false), Description("暂停布局"), Category("行为"), DefaultValue(false)] public bool PauseLayout { get => pauseLayout; set { if (pauseLayout == value) return; pauseLayout = value; if (!value) { ChangeItems(); Invalidate(); } } } internal void ChangeItems() { if (items == null || items.Count == 0) { _select = -1; return; } else if (_select >= items.Count) _select = items.Count - 1; if (pauseLayout) return; var _rect = ClientRectangle.PaddingRect(Padding); if (_rect.Width == 0 || _rect.Height == 0) return; var rect = _rect.PaddingRect(Margin); Helper.GDI(g => { var size_t = g.MeasureString(Config.NullText, Font); int text_heigth = (int)Math.Ceiling(size_t.Height), sp = (int)(4 * Config.Dpi), _igap = (int)(igap * Config.Dpi), gap = (int)(size_t.Height * 0.6F), gap2 = gap * 2; if (Full) { int imgsize = (int)(size_t.Height * 1.8F); int len = items.Count; if (Vertical) { int heightone = (rect.Height * 1 - (_igap * (len - 1))) / len, y = 0; switch (iconalign) { case TAlignMini.Top: int imgsize_t = (int)(size_t.Height * 1.8F); foreach (var it in items) { it.PARENT = this; if (it.HasIcon && it.HasEmptyText) it.SetIconNoText(new Rectangle(rect.X, rect.Y + y, rect.Width, heightone), imgsize_t); else it.SetRectTop(new Rectangle(rect.X, rect.Y + y, rect.Width, heightone), imgsize_t, text_heigth, sp); y += heightone + _igap; } break; case TAlignMini.Bottom: int imgsize_b = (int)(size_t.Height * 1.8F); foreach (var it in items) { it.PARENT = this; if (it.HasIcon && it.HasEmptyText) it.SetIconNoText(new Rectangle(rect.X, rect.Y + y, rect.Width, heightone), imgsize_b); else it.SetRectBottom(new Rectangle(rect.X, rect.Y + y, rect.Width, heightone), imgsize_b, text_heigth, sp); y += heightone + _igap; } break; case TAlignMini.Left: int imgsize_l = (int)(size_t.Height * 1.2F); foreach (var it in items) { it.PARENT = this; if (it.HasIcon && it.HasEmptyText) it.SetIconNoText(new Rectangle(rect.X, rect.Y + y, rect.Width, heightone), imgsize_l); else it.SetRectLeft(new Rectangle(rect.X, rect.Y + y, rect.Width, heightone), imgsize_l, sp, gap); y += heightone + _igap; } break; case TAlignMini.Right: int imgsize_r = (int)(size_t.Height * 1.2F); foreach (var it in items) { it.PARENT = this; if (it.HasIcon && it.HasEmptyText) it.SetIconNoText(new Rectangle(rect.X, rect.Y + y, rect.Width, heightone), imgsize_r); else it.SetRectRight(new Rectangle(rect.X, rect.Y + y, rect.Width, heightone), imgsize_r, sp, gap); y += heightone + _igap; } break; default: foreach (var it in items) { it.PARENT = this; it.SetRectNone(new Rectangle(rect.X, rect.Y + y, rect.Width, heightone)); y += heightone + _igap; } break; } } else { int widthone = (rect.Width * 1 - (_igap * (len - 1))) / len, x = 0; switch (iconalign) { case TAlignMini.Top: int imgsize_t = (int)(size_t.Height * 1.8F); foreach (var it in items) { it.PARENT = this; if (it.HasIcon && it.HasEmptyText) it.SetIconNoText(new Rectangle(rect.X + x, rect.Y, widthone, rect.Height), imgsize_t); else it.SetRectTop(new Rectangle(rect.X + x, rect.Y, widthone, rect.Height), imgsize_t, text_heigth, sp); x += widthone + _igap; } break; case TAlignMini.Bottom: int imgsize_b = (int)(size_t.Height * 1.8F); foreach (var it in items) { it.PARENT = this; if (it.HasIcon && it.HasEmptyText) it.SetIconNoText(new Rectangle(rect.X + x, rect.Y, widthone, rect.Height), imgsize_b); else it.SetRectBottom(new Rectangle(rect.X + x, rect.Y, widthone, rect.Height), imgsize_b, text_heigth, sp); x += widthone + _igap; } break; case TAlignMini.Left: int imgsize_l = (int)(size_t.Height * 1.2F); foreach (var it in items) { it.PARENT = this; if (it.HasIcon && it.HasEmptyText) it.SetIconNoText(new Rectangle(rect.X + x, rect.Y, widthone, rect.Height), imgsize_l); else it.SetRectLeft(new Rectangle(rect.X + x, rect.Y, widthone, rect.Height), imgsize_l, sp, gap); x += widthone + _igap; } break; case TAlignMini.Right: int imgsize_r = (int)(size_t.Height * 1.2F); foreach (var it in items) { it.PARENT = this; if (it.HasIcon && it.HasEmptyText) it.SetIconNoText(new Rectangle(rect.X + x, rect.Y, widthone, rect.Height), imgsize_r); else it.SetRectRight(new Rectangle(rect.X + x, rect.Y, widthone, rect.Height), imgsize_r, sp, gap); x += widthone + _igap; } break; default: foreach (var it in items) { it.PARENT = this; it.SetRectNone(new Rectangle(rect.X + x, rect.Y, widthone, rect.Height)); x += widthone + _igap; } break; } } Rect = _rect; } else { if (Vertical) { int y = 0; switch (iconalign) { case TAlignMini.Top: int imgsize_t = (int)(size_t.Height * 1.8F), heigth_t = (int)Math.Ceiling(size_t.Height * 2.4F + gap2); foreach (var it in items) { it.PARENT = this; if (it.HasIcon && it.HasEmptyText) it.SetIconNoText(new Rectangle(rect.X, rect.Y + y, rect.Width, heigth_t), imgsize_t); else it.SetRectTop(new Rectangle(rect.X, rect.Y + y, rect.Width, heigth_t), imgsize_t, text_heigth, sp); y += it.Rect.Height + _igap; } break; case TAlignMini.Bottom: int imgsize_b = (int)(size_t.Height * 1.8F), heigth_b = (int)Math.Ceiling(size_t.Height * 2.4F + gap2); foreach (var it in items) { it.PARENT = this; if (it.HasIcon && it.HasEmptyText) it.SetIconNoText(new Rectangle(rect.X, rect.Y + y, rect.Width, heigth_b), imgsize_b); else it.SetRectBottom(new Rectangle(rect.X, rect.Y + y, rect.Width, heigth_b), imgsize_b, text_heigth, sp); y += it.Rect.Height + _igap; } break; case TAlignMini.Left: int imgsize_l = (int)(size_t.Height * 1.2F), heigth_l = (int)Math.Ceiling(size_t.Height + gap2); foreach (var it in items) { it.PARENT = this; if (it.HasIcon && it.HasEmptyText) it.SetIconNoText(new Rectangle(rect.X, rect.Y + y, rect.Width, heigth_l), imgsize_l); else it.SetRectLeft(new Rectangle(rect.X, rect.Y + y, rect.Width, heigth_l), imgsize_l, sp, gap); y += it.Rect.Height + _igap; } break; case TAlignMini.Right: int imgsize_r = (int)(size_t.Height * 1.2F), heigth_r = (int)Math.Ceiling(size_t.Height + gap2); foreach (var it in items) { it.PARENT = this; if (it.HasIcon && it.HasEmptyText) it.SetIconNoText(new Rectangle(rect.X, rect.Y + y, rect.Width, heigth_r), imgsize_r); else it.SetRectRight(new Rectangle(rect.X, rect.Y + y, rect.Width, heigth_r), imgsize_r, sp, gap); y += it.Rect.Height + _igap; } break; default: int heigth = (int)Math.Ceiling(size_t.Height + gap); foreach (var it in items) { it.PARENT = this; it.SetRectNone(new Rectangle(rect.X, rect.Y + y, rect.Width, heigth)); y += it.Rect.Height + _igap; } break; } Rect = new RectangleF(_rect.X, _rect.Y, _rect.Width, y - _igap + Margin.Vertical); } else { int x = 0; switch (iconalign) { case TAlignMini.Top: int imgsize_t = (int)(size_t.Height * 1.8F); foreach (var it in items) { it.PARENT = this; if (it.HasIcon && it.HasEmptyText) it.SetIconNoText(new Rectangle(rect.X + x, rect.Y, imgsize_t + gap2, rect.Height), imgsize_t); else { var size = g.MeasureString(it.Text, Font).Size(); it.SetRectTop(new Rectangle(rect.X + x, rect.Y, size.Width + gap2, rect.Height), imgsize_t, text_heigth, sp); } x += it.Rect.Width + _igap; } break; case TAlignMini.Bottom: int imgsize_b = (int)(size_t.Height * 1.8F); foreach (var it in items) { it.PARENT = this; if (it.HasIcon && it.HasEmptyText) it.SetIconNoText(new Rectangle(rect.X + x, rect.Y, imgsize_b + gap2, rect.Height), imgsize_b); else { var size = g.MeasureString(it.Text, Font).Size(); it.SetRectBottom(new Rectangle(rect.X + x, rect.Y, size.Width + gap2, rect.Height), imgsize_b, text_heigth, sp); } x += it.Rect.Width + _igap; } break; case TAlignMini.Left: int imgsize_l = (int)(size_t.Height * 1.2F); foreach (var it in items) { it.PARENT = this; if (it.HasIcon && it.HasEmptyText) it.SetIconNoText(new Rectangle(rect.X + x, rect.Y, imgsize_l + gap2, rect.Height), imgsize_l); else { var size = g.MeasureString(it.Text, Font).Size(); it.SetRectLeft(new Rectangle(rect.X + x, rect.Y, size.Width + imgsize_l + sp + gap2, rect.Height), imgsize_l, sp, gap); } x += it.Rect.Width + _igap; } break; case TAlignMini.Right: int imgsize_r = (int)(size_t.Height * 1.2F); foreach (var it in items) { it.PARENT = this; if (it.HasIcon && it.HasEmptyText) it.SetIconNoText(new Rectangle(rect.X + x, rect.Y, imgsize_r + gap2, rect.Height), imgsize_r); else { var size = g.MeasureString(it.Text, Font).Size(); it.SetRectRight(new Rectangle(rect.X + x, rect.Y, size.Width + imgsize_r + sp + gap2, rect.Height), imgsize_r, sp, gap); } x += it.Rect.Width + _igap; } break; default: foreach (var it in items) { it.PARENT = this; var size = g.MeasureString(it.Text, Font).Size(); it.SetRectNone(new Rectangle(rect.X + x, rect.Y, size.Width + gap2, rect.Height)); x += it.Rect.Width + _igap; } break; } Rect = new RectangleF(_rect.X, _rect.Y, x - _igap + Margin.Horizontal, _rect.Height); } } }); if (_select > -1) { var _new = items[_select]; AnimationBarValue = TabSelectRect = _new.Rect; } } #endregion RectangleF Rect; #endregion #region 渲染 readonly StringFormat s_f = Helper.SF_ALL(); protected override void OnPaint(PaintEventArgs e) { if (items == null || items.Count == 0) return; var g = e.Graphics.High(); float _radius = radius * Config.Dpi; bool enabled = Enabled; using (var path = Rect.RoundPath(_radius, Round)) { using (var brush = new SolidBrush(back ?? Style.Db.BgLayout)) { g.FillPath(brush, path); } } var item_text = new System.Collections.Generic.List(items.Count); int _hover = -1; for (int i = 0; i < items.Count; i++) { var it = items[i]; if (it == null) continue; if (i == _select && !AnimationBar) { if (BarStyle) { float barSize = BarSize * Config.Dpi, barPadding = BarPadding * Config.Dpi, barPadding2 = barPadding * 2; var rect = new RectangleF(TabSelectRect.X + barPadding, TabSelectRect.Bottom - barSize, TabSelectRect.Width - barPadding2, barSize); if (BarRadius > 0) { using (var path = rect.RoundPath(BarRadius * Config.Dpi)) { using (var brush = new SolidBrush(backactive ?? Style.Db.BgElevated)) { g.FillPath(brush, path); } } } else { using (var brush = new SolidBrush(backactive ?? Style.Db.BgElevated)) { g.FillRectangle(brush, rect); } } } else { using (var path = TabSelectRect.RoundPath(_radius, Round)) { using (var brush = new SolidBrush(backactive ?? Style.Db.BgElevated)) { g.FillPath(brush, path); } } } } else if (it.Hover) { _hover = i; using (var path = it.Rect.RoundPath(_radius, Round)) { using (var brush = new SolidBrush(BackHover ?? Style.Db.HoverBg)) { g.FillPath(brush, path); } } } item_text.Add(it); } if (AnimationBar) { if (BarStyle) { float barSize = BarSize * Config.Dpi, barPadding = BarPadding * Config.Dpi, barPadding2 = barPadding * 2; var rect = new RectangleF(AnimationBarValue.X + barPadding, AnimationBarValue.Bottom - barSize, AnimationBarValue.Width - barPadding2, barSize); if (BarRadius > 0) { using (var path = rect.RoundPath(BarRadius * Config.Dpi)) { using (var brush = new SolidBrush(backactive ?? Style.Db.BgElevated)) { g.FillPath(brush, path); } } } else { using (var brush = new SolidBrush(backactive ?? Style.Db.BgElevated)) { g.FillRectangle(brush, rect); } } } else { using (var path = AnimationBarValue.RoundPath(_radius, Round)) { using (var brush = new SolidBrush(backactive ?? Style.Db.BgElevated)) { g.FillPath(brush, path); } } } } using (var brush = new SolidBrush(enabled ? (fore ?? Style.Db.TextSecondary) : Style.Db.TextQuaternary)) { for (int i = 0; i < item_text.Count; i++) { var it = item_text[i]; if (i == _select) { using (var brush_active = new SolidBrush(enabled ? (foreactive ?? Style.Db.Text) : Style.Db.TextQuaternary)) { if (PaintImg(g, it, brush_active.Color, it.IconActiveSvg, it.IconActive)) PaintImg(g, it, brush_active.Color, it.IconSvg, it.Icon); g.DrawStr(it.Text, Font, brush_active, it.RectText, s_f); } } else { if (i == _hover) { using (var brush_active = new SolidBrush(ForeHover ?? Style.Db.HoverColor)) { PaintImg(g, it, brush_active.Color, it.IconSvg, it.Icon); g.DrawStr(it.Text, Font, brush_active, it.RectText, s_f); } } else { PaintImg(g, it, brush.Color, it.IconSvg, it.Icon); g.DrawStr(it.Text, Font, brush, it.RectText, s_f); } } } } this.PaintBadge(g); base.OnPaint(e); } bool PaintImg(Graphics g, SegmentedItem it, Color color, string? svg, Image? bmp) { if (svg != null) { if (g.GetImgExtend(svg, it.RectImg, color)) return false; } else if (bmp != null) { g.DrawImage(bmp, it.RectImg); return false; } return true; } #endregion #region 鼠标 protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (items == null || items.Count == 0) return; int hand = 0, change = 0; foreach (var it in items) { bool hover = it.Rect.Contains(e.Location); if (it.Hover != hover) { it.Hover = hover; change++; } if (it.Hover) hand++; } SetCursor(hand > 0); if (change > 0) Invalidate(); } protected override void OnMouseLeave(EventArgs e) { base.OnMouseLeave(e); SetCursor(false); if (items == null || items.Count == 0) return; int change = 0; foreach (var it in items) { if (it.Hover) { it.Hover = false; change++; } } if (change > 0) Invalidate(); } protected override void OnLeave(EventArgs e) { base.OnLeave(e); SetCursor(false); if (items == null || items.Count == 0) return; int change = 0; foreach (var it in items) { if (it.Hover) { it.Hover = false; change++; } } if (change > 0) Invalidate(); } protected override void OnMouseClick(MouseEventArgs e) { base.OnMouseClick(e); if (items == null || items.Count == 0) return; for (int i = 0; i < items.Count; i++) { var it = items[i]; if (it != null && it.Rect.Contains(e.Location)) { SelectIndex = i; ItemClick?.Invoke(this, new SegmentedItemEventArgs(it, e)); return; } } } #endregion } public class SegmentedItemCollection : iCollection { public SegmentedItemCollection(Segmented it) { BindData(it); } internal SegmentedItemCollection BindData(Segmented it) { action = render => { if (render) it.ChangeItems(); it.Invalidate(); }; return this; } } public class SegmentedItem { Image? icon = null; /// /// 图标 /// [Description("图标"), Category("外观"), DefaultValue(null)] public Image? Icon { get => icon; set { if (icon == value) return; icon = value; Invalidates(); } } string? iconsvg = null; /// /// 图标SVG /// [Description("图标SVG"), Category("外观"), DefaultValue(null)] public string? IconSvg { get => iconsvg; set { if (iconsvg == value) return; iconsvg = value; Invalidates(); } } /// /// 是否包含图标 /// public bool HasIcon { get => IconSvg != null || Icon != null; } /// /// 图标激活 /// [Description("图标激活"), Category("外观"), DefaultValue(null)] public Image? IconActive { get; set; } /// /// 图标激活SVG /// [Description("图标激活SVG"), Category("外观"), DefaultValue(null)] public string? IconActiveSvg { get; set; } string? text = null; /// /// 文本 /// [Description("文本"), Category("外观"), DefaultValue(null)] public string? Text { get => text; set { if (text == value) return; text = value; Invalidates(); } } /// /// 用户定义数据 /// [Description("用户定义数据"), Category("数据"), DefaultValue(null)] public object? Tag { get; set; } internal bool Hover { get; set; } internal bool HasEmptyText => text == null || string.IsNullOrEmpty(text); internal void SetIconNoText(Rectangle rect, int imgsize) { Rect = rect; RectImg = RectText = new Rectangle(rect.X + (rect.Width - imgsize) / 2, rect.Y + (rect.Height - imgsize) / 2, imgsize, imgsize); } internal void SetRectTop(Rectangle rect, int imgsize, int text_heigth, int gap) { Rect = rect; if (HasIcon) { int y = (rect.Height - (imgsize + text_heigth + gap)) / 2; RectImg = new Rectangle(rect.X + (rect.Width - imgsize) / 2, rect.Y + y, imgsize, imgsize); RectText = new Rectangle(rect.X, RectImg.Bottom + gap, rect.Width, text_heigth); } else RectText = rect; } internal void SetRectBottom(Rectangle rect, int imgsize, int text_heigth, int gap) { Rect = rect; if (HasIcon) { int y = (rect.Height - (imgsize + text_heigth + gap)) / 2; RectText = new Rectangle(rect.X, rect.Y + y, rect.Width, text_heigth); RectImg = new Rectangle(rect.X + (rect.Width - imgsize) / 2, RectText.Bottom + gap, imgsize, imgsize); } else RectText = rect; } internal void SetRectLeft(Rectangle rect, int imgsize, int gap, int sp) { Rect = rect; if (HasIcon) { RectImg = new Rectangle(rect.X + sp, rect.Y + (rect.Height - imgsize) / 2, imgsize, imgsize); RectText = new Rectangle(RectImg.Right + gap, rect.Y, rect.Width - sp - imgsize - gap, rect.Height); } else RectText = rect; } internal void SetRectRight(Rectangle rect, int imgsize, int gap, int sp) { Rect = rect; if (HasIcon) { RectText = new Rectangle(rect.X, rect.Y, rect.Width - sp - imgsize - gap, rect.Height); RectImg = new Rectangle(RectText.Right + gap, rect.Y + (rect.Height - imgsize) / 2, imgsize, imgsize); } else RectText = rect; } internal void SetRectNone(Rectangle rect) { Rect = rect; RectText = rect; } internal Rectangle Rect { get; set; } internal Rectangle RectImg { get; set; } internal Rectangle RectText { get; set; } internal Segmented? PARENT { get; set; } void Invalidates() { if (PARENT == null) return; PARENT.ChangeItems(); PARENT.Invalidate(); } public override string? ToString() => text; } }