// 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 { /// /// Checkbox 多选框 /// /// 多选框。 [Description("Checkbox 多选框")] [ToolboxItem(true)] [DefaultProperty("Checked")] [DefaultEvent("CheckedChanged")] public class Checkbox : 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(); } } 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(); } } string? text = null; /// /// 文本 /// [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_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(); } } bool AnimationCheck = false; float AnimationCheckValue = 0; bool _checked = false; /// /// 选中状态 /// [Description("选中状态"), Category("数据"), DefaultValue(false)] public bool Checked { get => _checked; set { if (_checked == value) return; _checked = value; CheckedChanged?.Invoke(this, new BoolEventArgs(value)); ThreadCheck?.Dispose(); if (IsHandleCreated && Config.Animation) { AnimationCheck = true; if (value) { ThreadCheck = new ITask(this, () => { AnimationCheckValue = AnimationCheckValue.Calculate(0.2F); if (AnimationCheckValue > 1) { AnimationCheckValue = 1F; return false; } Invalidate(); return true; }, 20, () => { AnimationCheck = false; Invalidate(); }); } else { ThreadCheck = new ITask(this, () => { AnimationCheckValue = AnimationCheckValue.Calculate(-0.2F); if (AnimationCheckValue <= 0) { AnimationCheckValue = 0F; return false; } Invalidate(); return true; }, 20, () => { AnimationCheck = false; Invalidate(); }); } } else AnimationCheckValue = value ? 1F : 0F; Invalidate(); } } /// /// 点击时自动改变选中状态 /// [Description("点击时自动改变选中状态"), Category("行为"), DefaultValue(false)] public bool AutoCheck { get; set; } = true; RightToLeft rightToLeft = RightToLeft.No; [Description("反向"), Category("外观"), DefaultValue(RightToLeft.No)] public override RightToLeft RightToLeft { get => rightToLeft; set { if (rightToLeft == value) return; rightToLeft = value; stringFormat.Alignment = RightToLeft == RightToLeft.Yes ? StringAlignment.Far : StringAlignment.Near; Invalidate(); } } #endregion #region 事件 /// /// Checked 属性值更改时发生 /// [Description("Checked 属性值更改时发生"), Category("行为")] public event BoolEventHandler? CheckedChanged = null; #endregion #region 渲染 protected override void OnPaint(PaintEventArgs e) { var rect = ClientRectangle.DeflateRect(Padding); var g = e.Graphics.High(); bool enabled = Enabled; var font_size = g.MeasureString(text ?? Config.NullText, Font).Size(); rect.IconRectL(font_size.Height, out var icon_rect, out var text_rect); bool right = rightToLeft == RightToLeft.Yes; PaintChecked(g, rect, enabled, icon_rect, right); if (right) text_rect.X = rect.Width - text_rect.X - text_rect.Width; using (var brush = fore.Brush(Style.Db.Text, Style.Db.TextQuaternary, enabled)) { g.DrawStr(text, Font, brush, text_rect, stringFormat); } this.PaintBadge(g); base.OnPaint(e); } #region 渲染帮助 internal void PaintChecked(Graphics g, Rectangle rect, bool enabled, RectangleF icon_rect, bool right) { float dot_size = icon_rect.Height; float radius = dot_size * .2F; if (right) icon_rect.X = rect.Width - icon_rect.X - icon_rect.Width; using (var path = icon_rect.RoundPath(radius)) { if (enabled) { var color = fill ?? Style.Db.Primary; if (AnimationCheck) { float dot = dot_size * 0.3F, alpha = 255 * AnimationCheckValue; using (var brush = new SolidBrush(Helper.ToColor(alpha, color))) { g.FillPath(brush, path); } using (var brush = new Pen(Helper.ToColor(alpha, Style.Db.BgBase), 3F)) { g.DrawLines(brush, PaintArrow(icon_rect)); } if (_checked) { float max = icon_rect.Height + ((rect.Height - icon_rect.Height) * AnimationCheckValue), alpha2 = 100 * (1F - AnimationCheckValue); using (var brush = new SolidBrush(Helper.ToColor(alpha2, color))) { g.FillEllipse(brush, new RectangleF(icon_rect.X + (icon_rect.Width - max) / 2F, icon_rect.Y + (icon_rect.Height - max) / 2F, max, max)); } } using (var brush = new Pen(color, 2F)) { g.DrawPath(brush, path); } } else if (_checked) { using (var brush = new SolidBrush(color)) { g.FillPath(brush, path); } using (var brush = new Pen(Style.Db.BgBase, 3F)) { g.DrawLines(brush, PaintArrow(icon_rect)); } } else { if (AnimationHover) { using (var brush = new Pen(Style.Db.BorderColor, 2F)) { g.DrawPath(brush, path); } using (var brush = new Pen(Helper.ToColor(AnimationHoverValue, color), 2F)) { g.DrawPath(brush, path); } } else if (ExtraMouseHover) { using (var brush = new Pen(color, 2F)) { g.DrawPath(brush, path); } } else { using (var brush = new Pen(Style.Db.BorderColor, 2F)) { g.DrawPath(brush, path); } } } } else { using (var brush = new SolidBrush(Style.Db.FillQuaternary)) { g.FillPath(brush, path); } if (_checked) { using (var brush = new Pen(Style.Db.TextQuaternary, 3F)) { g.DrawLines(brush, PaintArrow(icon_rect)); } } using (var brush = new Pen(Style.Db.BorderColorDisable, 2F)) { g.DrawPath(brush, path); } } } } internal PointF[] PaintArrow(RectangleF rect) { float size = rect.Height * 0.15F, size2 = rect.Height * 0.2F, size3 = rect.Height * 0.26F; return new PointF[] { new PointF(rect.X+size,rect.Y+rect.Height/2), new PointF(rect.X+rect.Width*0.4F,rect.Y+(rect.Height-size3)), new PointF(rect.X+rect.Width-size2,rect.Y+size2), }; } #endregion #endregion #region 鼠标 protected override void OnClick(EventArgs e) { if (AutoCheck) Checked = !_checked; base.OnClick(e); } protected override void OnMouseDown(MouseEventArgs e) { Focus(); base.OnMouseDown(e); } int AnimationHoverValue = 0; bool AnimationHover = false; bool _mouseHover = false; bool ExtraMouseHover { get => _mouseHover; set { if (_mouseHover == value) return; _mouseHover = value; var enabled = Enabled; SetCursor(value && enabled); if (enabled) { if (Config.Animation) { ThreadHover?.Dispose(); AnimationHover = true; if (value) { ThreadHover = new ITask(this, () => { AnimationHoverValue += 20; if (AnimationHoverValue > 255) { AnimationHoverValue = 255; return false; } Invalidate(); return true; }, 10, () => { AnimationHover = false; Invalidate(); }); } else { ThreadHover = new ITask(this, () => { AnimationHoverValue -= 20; if (AnimationHoverValue < 1) { AnimationHoverValue = 0; return false; } Invalidate(); return true; }, 10, () => { AnimationHover = false; Invalidate(); }); } } else AnimationHoverValue = 255; Invalidate(); } } } #region 动画 protected override void Dispose(bool disposing) { ThreadCheck?.Dispose(); ThreadHover?.Dispose(); base.Dispose(disposing); } ITask? ThreadHover = null; ITask? ThreadCheck = null; #endregion protected override void OnMouseEnter(EventArgs e) { base.OnMouseEnter(e); ExtraMouseHover = true; } protected override void OnMouseLeave(EventArgs e) { base.OnMouseLeave(e); ExtraMouseHover = false; } protected override void OnLeave(EventArgs e) { base.OnLeave(e); ExtraMouseHover = false; } #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) { if (BeforeAutoSize()) Invalidate(); 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); float gap = 20 * Config.Dpi; return new Size((int)Math.Ceiling(font_size.Width + font_size.Height + gap), (int)Math.Ceiling(font_size.Height + gap)); }); } } 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 } }