// 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 { /// /// Badge 徽标数 /// /// 图标右上角的圆形徽标数字。 [Description("Badge 徽标数")] [ToolboxItem(true)] [DefaultProperty("Text")] public class Badge : 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(); } } TState state = TState.Default; /// /// 状态 /// [Description("状态"), Category("外观"), DefaultValue(TState.Default)] public TState State { get => state; set { if (state == value) return; state = value; StartAnimation(); Invalidate(); } } bool has_text = true; string? text = null; /// /// 文本 /// [Description("文本"), Category("外观"), DefaultValue(null)] public override string? Text { get => text; set { if (text == value) return; text = value; has_text = string.IsNullOrEmpty(text); if (BeforeAutoSize()) Invalidate(); OnTextChanged(EventArgs.Empty); } } StringFormat stringFormat = Helper.SF_ALL(lr: StringAlignment.Near); ContentAlignment textAlign = ContentAlignment.MiddleLeft; /// /// 文本位置 /// [Description("文本位置"), Category("外观"), DefaultValue(ContentAlignment.MiddleLeft)] public ContentAlignment TextAlign { get => textAlign; set { if (textAlign == value) return; textAlign = value; textAlign.SetAlignment(ref stringFormat); Invalidate(); } } Color? fill = null; /// /// 颜色 /// [Description("颜色"), Category("外观"), DefaultValue(null)] [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))] public Color? Fill { get => fill; set { if (fill == value) return; fill = value; Invalidate(); } } #region 动画 void StartAnimation() { StopAnimation(); if (Config.Animation && state == TState.Processing) { ThreadState = new ITask(this, i => { AnimationStateValue = i; Invalidate(); }, 50, 1F, 0.05F); } } void StopAnimation() { ThreadState?.Dispose(); } protected override void Dispose(bool disposing) { StopAnimation(); base.Dispose(disposing); } ITask? ThreadState = null; float AnimationStateValue = 0; #endregion #endregion #region 渲染 protected override void OnPaint(PaintEventArgs e) { var rect = ClientRectangle.PaddingRect(Padding); var g = e.Graphics.High(); if (has_text) { var size = g.MeasureString(Config.NullText, Font); float dot_size = size.Height / 2.5F; using (var brush = new SolidBrush(GetColor(fill, state))) { g.FillEllipse(brush, new RectangleF((rect.Width - dot_size) / 2F, (rect.Height - dot_size) / 2F, dot_size, dot_size)); if (state == TState.Processing) { float max = (size.Height - 6F) * AnimationStateValue, alpha = 255 * (1F - AnimationStateValue); using (var pen = new Pen(Helper.ToColor(alpha, brush.Color), 4F)) { g.DrawEllipse(pen, new RectangleF((rect.Width - max) / 2F, (rect.Height - max) / 2F, max, max)); } } } } else { var size = g.MeasureString(text, Font); float dot_size = size.Height / 2.5F; using (var brush = new SolidBrush(GetColor(fill, state))) { var rect_dot = new RectangleF(rect.X + (size.Height - dot_size) / 2, rect.Y + (rect.Height - dot_size) / 2, dot_size, dot_size); g.FillEllipse(brush, rect_dot); if (state == TState.Processing) { float max = (size.Height - 6F) * AnimationStateValue, alpha = 255 * (1F - AnimationStateValue); using (var pen = new Pen(Helper.ToColor(alpha, brush.Color), 4F)) { g.DrawEllipse(pen, new RectangleF(rect_dot.X + (rect_dot.Width - max) / 2F, rect_dot.Y + (rect_dot.Height - max) / 2F, max, max)); } } } using (var brush = fore.Brush(Style.Db.Text, Style.Db.TextQuaternary, Enabled)) { g.DrawStr(text, Font, brush, new RectangleF(rect.X + size.Height, rect.Y, rect.Width - size.Height, rect.Height), stringFormat); } } this.PaintBadge(g); base.OnPaint(e); } #region 渲染帮助 internal Color GetColor(Color? color, TState state) { if (color.HasValue) return color.Value; return GetColor(state); } internal Color GetColor(TState state) { switch (state) { case TState.Success: return Style.Db.Success; case TState.Error: return Style.Db.Error; case TState.Primary: case TState.Processing: return Style.Db.Primary; case TState.Warn: return Style.Db.Warning; default: return Style.Db.TextQuaternary; } } #endregion #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 => { if (has_text) { var font_size = g.MeasureString(Config.NullText, Font).Size(); font_size.Width = font_size.Height; return font_size; } else { var font_size = g.MeasureString(text ?? Config.NullText, Font).Size(); font_size.Width += font_size.Height; return font_size; } }); } } 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 } }