// 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.Drawing.Imaging; using System.Threading; using System.Windows.Forms; namespace AntdUI { /// /// Avatar 头像 /// /// 用来代表用户或事物,支持图片、图标或字符展示。 [Description("Avatar 头像")] [ToolboxItem(true)] [DefaultProperty("Image")] [Designer(typeof(IControlDesigner))] public class Avatar : IControl, ShadowConfig { public Avatar() { base.BackColor = Color.Transparent; } #region 属性 Color back = Color.Transparent; /// /// 背景颜色 /// [Description("背景颜色"), Category("外观"), DefaultValue(typeof(Color), "Transparent")] public new Color BackColor { get => back; set { if (back == value) return; back = value; Invalidate(); } } string? text = null; /// /// 文本 /// [Description("文本"), Category("外观"), DefaultValue(null)] public override string? Text { get => text; set { if (text == value) return; if (value != null && value.Length > 1) value = value.Substring(0, 1); text = value; Invalidate(); OnTextChanged(EventArgs.Empty); } } int radius = 0; /// /// 圆角 /// [Description("圆角"), Category("外观"), DefaultValue(0)] 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(); } } #region 图片 Image? image = null; /// /// 图片 /// [Description("图片"), Category("外观"), DefaultValue(null)] public Image? Image { get => image; set { if (image == value) return; image = value; if (value != null && PlayGIF) { var fd = new FrameDimension(value.FrameDimensionsList[0]); int count = value.GetFrameCount(fd); if (count > 1) PlayGif(value, fd, count); else Invalidate(); } else Invalidate(); } } /// /// 播放GIF /// [Description("播放GIF"), Category("行为"), DefaultValue(true)] public bool PlayGIF { get; set; } = true; void PlayGif(Image value, FrameDimension fd, int count) { int[] delays = GifDelays(value, count); ITask.Run(() => { while (image == value) { for (int i = 0; i < count; i++) { if (image == value) { lock (_lock) { value.SelectActiveFrame(fd, i); } Invalidate(); Thread.Sleep(delays[i]); } else return; } } }); } object _lock = new object(); int[] GifDelays(Image value, int count) { int PropertyTagFrameDelay = 0x5100; var propItem = value.GetPropertyItem(PropertyTagFrameDelay); if (propItem != null) { var bytes = propItem.Value; if (bytes != null) { int[] delays = new int[count]; for (int i = 0; i < delays.Length; i++) delays[i] = BitConverter.ToInt32(bytes, i * 4) * 10; return delays; } } int[] delaysd = new int[count]; for (int i = 0; i < delaysd.Length; i++) delaysd[i] = 100; return delaysd; } string? imageSvg = null; /// /// 图片SVG /// [Description("图片SVG"), Category("外观"), DefaultValue(null)] public string? ImageSvg { get => imageSvg; set { if (imageSvg == value) return; imageSvg = value; Invalidate(); } } TFit imageFit = TFit.Cover; /// /// 图片布局 /// [Description("图片布局"), Category("外观"), DefaultValue(TFit.Cover)] public TFit ImageFit { get => imageFit; set { if (imageFit == value) return; imageFit = value; Invalidate(); } } #endregion #region 加载 bool loading = false; /// /// 加载状态 /// [Description("加载状态"), Category("外观"), DefaultValue(false)] public bool Loading { get => loading; set { if (loading == value) return; loading = value; Invalidate(); } } float _value = 0F; /// /// 加载进度 /// [Description("加载进度 0F-1F"), Category("数据"), DefaultValue(0F)] public float LoadingProgress { get => _value; set { if (_value == value) return; if (value < 0) value = 0; else if (value > 1) value = 1; _value = value; if (loading) Invalidate(); } } #endregion #region 边框 float borderWidth = 0F; /// /// 边框宽度 /// [Description("边框宽度"), Category("边框"), DefaultValue(0F)] public float BorderWidth { get => borderWidth; set { if (borderWidth == value) return; borderWidth = value; Invalidate(); } } Color borColor = Color.FromArgb(246, 248, 250); /// /// 边框颜色 /// [Description("边框颜色"), Category("边框"), DefaultValue(typeof(Color), "246, 248, 250")] public Color BorderColor { get => borColor; set { if (borColor == value) return; borColor = value; if (borderWidth > 0) Invalidate(); } } #endregion #region 阴影 int shadow = 0; [Description("阴影大小"), Category("阴影"), DefaultValue(0)] public int Shadow { get => shadow; set { if (shadow == value) return; shadow = value; Invalidate(); } } [Description("阴影颜色"), Category("阴影"), DefaultValue(null)] [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))] public Color? ShadowColor { get; set; } float shadowOpacity = 0.3F; [Description("阴影透明度"), Category("阴影"), DefaultValue(0.3F)] public float ShadowOpacity { get => shadowOpacity; set { if (shadowOpacity == value) return; if (value < 0) value = 0; else if (value > 1) value = 1; shadowOpacity = value; Invalidate(); } } int shadowOffsetX = 0; [Description("阴影偏移X"), Category("阴影"), DefaultValue(0)] public int ShadowOffsetX { get => shadowOffsetX; set { if (shadowOffsetX == value) return; shadowOffsetX = value; Invalidate(); } } int shadowOffsetY = 0; [Description("阴影偏移Y"), Category("阴影"), DefaultValue(0)] public int ShadowOffsetY { get => shadowOffsetY; set { if (shadowOffsetY == value) return; shadowOffsetY = value; Invalidate(); } } #endregion #endregion #region 渲染 protected override void OnPaint(PaintEventArgs e) { var _rect = ClientRectangle; if (_rect.Width == 0 || _rect.Height == 0) return; var g = e.Graphics.High(); float _radius = radius * Config.Dpi; var rect = ReadRectangle; if (shadow > 0 && shadowOpacity > 0) g.PaintShadow(this, _rect, rect, _radius, round); FillRect(g, rect, back, _radius, round); if (image != null) { lock (_lock) { g.PaintImg(rect, image, imageFit, _radius, round); } } else if (imageSvg != null) { using (var bmp = SvgExtend.GetImgExtend(imageSvg, rect, ForeColor)) { if (bmp == null) PaintText(g, text, rect, stringCenter, Enabled); else g.PaintImg(rect, bmp, imageFit, _radius, round); } } else PaintText(g, text, rect, stringCenter, Enabled); if (borderWidth > 0) DrawRect(g, rect, borColor, borderWidth * Config.Dpi, _radius, round); if (loading) { using (var pen = new Pen(Color.FromArgb(220, Style.Db.PrimaryColor), 6 * Config.Dpi)) using (var penpro = new Pen(Style.Db.Primary, pen.Width)) { int loading_size = (int)(40 * Config.Dpi); var rect_loading = new Rectangle(rect.X + (rect.Width - loading_size) / 2, rect.Y + (rect.Height - loading_size) / 2, loading_size, loading_size); g.DrawEllipse(pen, rect_loading); try { g.DrawArc(penpro, rect_loading, -90, 360F * _value); } catch { } } } this.PaintBadge(g); base.OnPaint(e); } #region 渲染帮助 readonly StringFormat stringCenter = Helper.SF_ALL(); void FillRect(Graphics g, Rectangle rect, Color color, float radius, bool round) { using (var brush = new SolidBrush(color)) { if (round) { g.FillEllipse(brush, rect); } else if (radius > 0) { using (var path = rect.RoundPath(radius)) { g.FillPath(brush, path); } } else { g.FillRectangle(brush, rect); } } } void DrawRect(Graphics g, Rectangle rect, Color color, float width, float radius, bool round) { using (var pen = new Pen(color, width)) { if (round) { g.DrawEllipse(pen, rect); } else if (radius > 0) { using (var path = rect.RoundPath(radius)) { g.DrawPath(pen, path); } } else { #if NET40 || NET46 || NET48 || NET6_0 g.DrawRectangles(pen, new RectangleF[] { rect }); #else g.DrawRectangle(pen, rect); #endif } } } #endregion public override Rectangle ReadRectangle { get { if (borderWidth > 0) return ClientRectangle.PaddingRect(Padding, borderWidth * Config.Dpi / 2F); else return ClientRectangle.PaddingRect(Padding); } } public override GraphicsPath RenderRegion { get { if (borderWidth > 0) { var rect = ReadRectangle; if (round) { var path = new GraphicsPath(); path.AddEllipse(rect); return path; } else if (radius > 0) return rect.RoundPath(radius * Config.Dpi); else { var path = new GraphicsPath(); path.AddRectangle(rect); return path; } } else { var rect = ReadRectangle; if (round) { var path = new GraphicsPath(); path.AddEllipse(rect); return path; } else if (radius > 0) return rect.RoundPath(radius * Config.Dpi); else { var path = new GraphicsPath(); path.AddRectangle(rect); return path; } } } } #endregion } }