// 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.Drawing.Drawing2D; using System.Windows.Forms; namespace AntdUI { /// /// Tag 标签 /// /// 进行标记和分类的小标签。 [Description("Tag 标签")] [ToolboxItem(true)] [DefaultProperty("Text")] public class Tag : IControl { #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(); } } #region 背景 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(); } } Image? backImage = null; /// /// 背景图片 /// [Description("背景图片"), Category("外观"), DefaultValue(null)] public new Image? BackgroundImage { get => backImage; set { if (backImage == value) return; backImage = value; Invalidate(); } } TFit backFit = TFit.Fill; /// /// 背景图片布局 /// [Description("背景图片布局"), Category("外观"), DefaultValue(TFit.Fill)] public new TFit BackgroundImageLayout { get => backFit; set { if (backFit == value) return; backFit = value; Invalidate(); } } #endregion #region 边框 internal float borderWidth = 1; /// /// 边框宽度 /// [Description("边框宽度"), Category("边框"), DefaultValue(1F)] public float BorderWidth { get => borderWidth; set { if (borderWidth == value) return; borderWidth = value; Invalidate(); } } #endregion internal int radius = 6; /// /// 圆角 /// [Description("圆角"), Category("外观"), DefaultValue(6)] public int Radius { get => radius; set { if (radius == value) return; radius = value; if (BeforeAutoSize()) Invalidate(); } } internal TTypeMini type = TTypeMini.Default; /// /// 类型 /// [Description("类型"), Category("外观"), DefaultValue(TTypeMini.Default)] public TTypeMini Type { get => type; set { if (type == value) return; type = value; Invalidate(); } } bool closeIcon = false; /// /// 是否显示关闭图标 /// [Description("是否显示关闭图标"), Category("行为"), DefaultValue(false)] public bool CloseIcon { get => closeIcon; set { if (closeIcon == value) return; closeIcon = value; if (BeforeAutoSize()) Invalidate(); } } #region 文本 internal string? text = null; /// /// 文本 /// [Editor(typeof(System.ComponentModel.Design.MultilineStringEditor), typeof(UITypeEditor))] [Description("文本"), Category("外观"), DefaultValue(null)] public override string? Text { get => text; set { if (text == value) return; text = value; if (BeforeAutoSize()) Invalidate(); OnTextChanged(EventArgs.Empty); } } StringFormat stringFormat = Helper.SF_NoWrap(); ContentAlignment textAlign = ContentAlignment.MiddleCenter; /// /// 文本位置 /// [Description("文本位置"), Category("外观"), DefaultValue(ContentAlignment.MiddleCenter)] public ContentAlignment TextAlign { get => textAlign; set { if (textAlign == value) return; textAlign = value; textAlign.SetAlignment(ref stringFormat); Invalidate(); } } bool autoEllipsis = false; /// /// 文本超出自动处理 /// [Description("文本超出自动处理"), Category("行为"), DefaultValue(false)] public bool AutoEllipsis { get => autoEllipsis; set { if (autoEllipsis == value) return; autoEllipsis = value; stringFormat.Trimming = value ? StringTrimming.EllipsisCharacter : StringTrimming.None; } } bool textMultiLine = false; /// /// 是否多行 /// [Description("是否多行"), Category("行为"), DefaultValue(false)] public bool TextMultiLine { get => textMultiLine; set { if (textMultiLine == value) return; textMultiLine = value; stringFormat.FormatFlags = value ? 0 : StringFormatFlags.NoWrap; Invalidate(); } } #endregion #region 图片 Image? image = null; /// /// 图像 /// [Description("图像"), Category("外观"), DefaultValue(null)] public Image? Image { get => image; set { if (image == value) return; image = value; if (BeforeAutoSize()) Invalidate(); } } string? imageSvg = null; [Description("图像SVG"), Category("外观"), DefaultValue(null)] public string? ImageSvg { get => imageSvg; set { if (imageSvg == value) return; imageSvg = value; if (BeforeAutoSize()) Invalidate(); } } /// /// 是否包含图片 /// public bool HasImage { get => imageSvg != null || image != null; } /// /// 图像大小 /// [Description("图像大小"), Category("外观"), DefaultValue(typeof(Size), "0, 0")] public Size ImageSize { get; set; } = new Size(0, 0); #endregion #endregion #region 事件 /// /// Close时发生 /// [Description("Close时发生"), Category("行为")] public event RBoolEventHandler? CloseChanged = null; #endregion #region 渲染 protected override void OnPaint(PaintEventArgs e) { var g = e.Graphics.High(); var rect_read = ReadRectangle; if (backImage != null) g.PaintImg(rect_read, backImage, backFit, radius, false); float _radius = radius * Config.Dpi; Color _fore, _back, _bor; switch (type) { case TTypeMini.Default: _back = Style.Db.TagDefaultBg; _fore = Style.Db.TagDefaultColor; _bor = Style.Db.DefaultBorder; break; case TTypeMini.Error: _back = Style.Db.ErrorBg; _fore = Style.Db.Error; _bor = Style.Db.ErrorBorder; break; case TTypeMini.Success: _back = Style.Db.SuccessBg; _fore = Style.Db.Success; _bor = Style.Db.SuccessBorder; break; case TTypeMini.Info: _back = Style.Db.InfoBg; _fore = Style.Db.Info; _bor = Style.Db.InfoBorder; break; case TTypeMini.Warn: _back = Style.Db.WarningBg; _fore = Style.Db.Warning; _bor = Style.Db.WarningBorder; break; case TTypeMini.Primary: default: _back = Style.Db.PrimaryBg; _fore = Style.Db.Primary; _bor = Style.Db.Primary; break; } if (fore.HasValue) _fore = fore.Value; if (back.HasValue) _back = back.Value; if (backImage != null) g.PaintImg(rect_read, backImage, backFit, _radius, false); using (var path = rect_read.RoundPath(_radius)) { #region 绘制背景 using (var brush = new SolidBrush(_back)) { g.FillPath(brush, path); } if (borderWidth > 0) { float border = borderWidth * Config.Dpi; using (var brush = new Pen(_bor, border)) { g.DrawPath(brush, path); } } #endregion PaintText(g, text, _fore, rect_read); } this.PaintBadge(g); base.OnPaint(e); } #region 渲染帮助 internal void PaintText(Graphics g, string? text, Color color, Rectangle rect_read) { var font_size = g.MeasureString(text ?? Config.NullText, Font).Size(); if (text == null) { if (PaintImageNoText(g, color, font_size, rect_read)) { if (closeIcon) { int size = (int)(rect_read.Height * .4F); var rect_arrow = new Rectangle(rect_read.X + (rect_read.Width - size) / 2, rect_read.Y + (rect_read.Height - size) / 2, size, size); rect_close = rect_arrow; if (hover_close.Animation) g.PaintIconClose(rect_arrow, Helper.ToColor(hover_close.Value + Style.Db.TextQuaternary.A, Style.Db.Text)); else if (hover_close.Switch) g.PaintIconClose(rect_arrow, Style.Db.Text); else g.PaintIconClose(rect_arrow, Style.Db.TextQuaternary); } } } else { bool right = RightToLeft == RightToLeft.Yes; var rect = ReadRectangle.IconRect(font_size.Height, HasImage, closeIcon, right, false); rect_close = rect.r; if (closeIcon) { if (hover_close.Animation) g.PaintIconClose(rect.r, Helper.ToColor(hover_close.Value + Style.Db.TextQuaternary.A, Style.Db.Text), .8F); else if (hover_close.Switch) g.PaintIconClose(rect.r, Style.Db.Text, .8F); else g.PaintIconClose(rect.r, Style.Db.TextQuaternary, .8F); } PaintImage(g, color, rect.l); using (var brush = new SolidBrush(color)) { g.DrawStr(text, Font, brush, rect.text, stringFormat); } } } /// /// 渲染图片(没有文字) /// /// GDI /// 颜色 /// 字体大小 /// 客户区域 bool PaintImageNoText(Graphics g, Color? color, Size font_size, Rectangle rect_read) { if (imageSvg != null) { g.GetImgExtend(imageSvg, GetImageRectCenter(font_size, rect_read), color); return false; } else if (image != null) { g.DrawImage(image, GetImageRectCenter(font_size, rect_read)); return false; } return true; } /// /// 居中的图片绘制区域 /// /// 字体大小 /// 客户区域 Rectangle GetImageRectCenter(Size font_size, Rectangle rect_read) { if (ImageSize.Width > 0 && ImageSize.Height > 0) { int w = (int)(ImageSize.Width * Config.Dpi), h = (int)(ImageSize.Height * Config.Dpi); return new Rectangle(rect_read.X + (rect_read.Width - w) / 2, rect_read.Y + (rect_read.Height - h) / 2, w, h); } else { int w = (int)(font_size.Height * 0.8F); return new Rectangle(rect_read.X + (rect_read.Width - w) / 2, rect_read.Y + (rect_read.Height - w) / 2, w, w); } } /// /// 渲染图片 /// /// GDI /// 颜色 /// 图标区域 void PaintImage(Graphics g, Color? color, Rectangle rectl) { if (imageSvg != null) g.GetImgExtend(imageSvg, GetImageRect(rectl), color); else if (image != null) g.DrawImage(image, GetImageRect(rectl)); } /// /// 图片绘制区域 /// /// 图标区域 Rectangle GetImageRect(Rectangle rectl) { if (ImageSize.Width > 0 && ImageSize.Height > 0) { int w = (int)(ImageSize.Width * Config.Dpi), h = (int)(ImageSize.Height * Config.Dpi); return new Rectangle(rectl.X + (rectl.Width - w) / 2, rectl.Y + (rectl.Height - h) / 2, w, h); } else return rectl; } #endregion public override Rectangle ReadRectangle { get => ClientRectangle.PaddingRect(Padding, borderWidth * Config.Dpi); } public override GraphicsPath RenderRegion { get { var rect_read = ReadRectangle; float _radius = radius * Config.Dpi; return rect_read.RoundPath(_radius); } } #endregion #region 鼠标 ITaskOpacity hover_close; public Tag() { base.BackColor = Color.Transparent; hover_close = new ITaskOpacity(this); } RectangleF rect_close; protected override void OnMouseClick(MouseEventArgs e) { if (e.Button == MouseButtons.Left && closeIcon && rect_close.Contains(e.Location)) { bool isclose = false; if (CloseChanged == null || CloseChanged(this, EventArgs.Empty)) isclose = true; if (isclose) Dispose(); return; } base.OnMouseClick(e); } protected override void OnMouseMove(MouseEventArgs e) { if (closeIcon) { hover_close.MaxValue = Style.Db.Text.A - Style.Db.TextQuaternary.A; hover_close.Switch = rect_close.Contains(e.Location); SetCursor(hover_close.Switch); } else SetCursor(false); base.OnMouseMove(e); } #endregion #region 自动大小 /// /// 自动大小 /// [Browsable(true)] [Description("自动大小"), Category("外观"), DefaultValue(false)] public override bool AutoSize { get => base.AutoSize; set { if (base.AutoSize == value) return; base.AutoSize = value; if (value) { if (autoSize == TAutoSize.None) autoSize = TAutoSize.Auto; } else autoSize = TAutoSize.None; BeforeAutoSize(); } } TAutoSize autoSize = TAutoSize.None; /// /// 自动大小模式 /// [Description("自动大小模式"), Category("外观"), DefaultValue(TAutoSize.None)] public TAutoSize AutoSizeMode { get => autoSize; set { if (autoSize == value) return; autoSize = value; base.AutoSize = autoSize != TAutoSize.None; BeforeAutoSize(); } } protected override void OnFontChanged(EventArgs e) { BeforeAutoSize(); base.OnFontChanged(e); } public override Size GetPreferredSize(Size proposedSize) { if (autoSize == TAutoSize.None) return base.GetPreferredSize(proposedSize); else if (autoSize == TAutoSize.Width) return new Size(PSize.Width, base.GetPreferredSize(proposedSize).Height); else if (autoSize == TAutoSize.Height) return new Size(base.GetPreferredSize(proposedSize).Width, PSize.Height); return PSize; } internal Size PSize { get { return Helper.GDI(g => { var font_size = g.MeasureString(text ?? Config.NullText, Font).Size(); int count = 0; if (HasImage) count++; if (closeIcon) count++; return new Size(font_size.Width + (int)(14F * Config.Dpi) + (font_size.Height * count), font_size.Height + (int)(8F * Config.Dpi)); }); } } protected override void OnResize(EventArgs e) { BeforeAutoSize(); base.OnResize(e); } internal bool BeforeAutoSize() { if (autoSize == TAutoSize.None) return true; if (InvokeRequired) { bool flag = false; Invoke(new Action(() => { flag = BeforeAutoSize(); })); return flag; } var PS = PSize; switch (autoSize) { case TAutoSize.Width: if (Width == PS.Width) return true; Width = PS.Width; break; case TAutoSize.Height: if (Height == PS.Height) return true; Height = PS.Height; break; case TAutoSize.Auto: default: if (Width == PS.Width && Height == PS.Height) return true; Size = PS; break; } return false; } #endregion } }