编辑
2025-10-14
C#
00

目录

控件特性
完整代码实现
增加悬停动画
性能优化建议

在Windows窗体应用程序开发中,我们经常需要展示圆形的图片,比如用户头像等。本文将详细介绍如何通过继承PictureBox控件,使用GDI+技术来实现一个支持渐变边框的圆形图片控件。

控件特性

  • 支持圆形显示图片
  • 可自定义边框粗细
  • 支持双色渐变边框
  • 可调整渐变角度
  • 支持多种边框线型
  • 具有抗锯齿效果

完整代码实现

C#
using System; using System.Collections.Generic; using System.Drawing.Drawing2D; using System.Linq; using System.Text; using System.Threading.Tasks; namespace AppControls { public class CircularPictureBox : PictureBox { private int borderSize = 2; private Color borderColor = Color.RoyalBlue; private Color borderColor2 = Color.HotPink; private DashStyle borderLineStyle = DashStyle.Solid; private DashCap borderCapStyle = DashCap.Flat; private float gradientAngle = 50F; public CircularPictureBox() { this.Size = new Size(100, 100); this.SizeMode = PictureBoxSizeMode.StretchImage; this.BackColor = Color.Transparent; } // 优化双缓冲设置 protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true); } public int BorderSize { get { return borderSize; } set { borderSize = value; this.Invalidate(); } } public Color BorderColor { get { return borderColor; } set { borderColor = value; this.Invalidate(); } } public Color BorderColor2 { get { return borderColor2; } set { borderColor2 = value; this.Invalidate(); } } public DashStyle BorderLineStyle { get { return borderLineStyle; } set { borderLineStyle = value; this.Invalidate(); } } public DashCap BorderCapStyle { get { return borderCapStyle; } set { borderCapStyle = value; this.Invalidate(); } } public float GradientAngle { get { return gradientAngle; } set { gradientAngle = value; this.Invalidate(); } } protected override void OnResize(EventArgs e) { base.OnResize(e); this.Size = new Size(this.Width, this.Height); } protected override void OnPaint(PaintEventArgs pe) { base.OnPaint(pe); Rectangle rectSurface = this.ClientRectangle; Rectangle rectBorder = Rectangle.Inflate(rectSurface, -borderSize, -borderSize); int smoothSize = 2; if (borderSize > 0) smoothSize = borderSize; using (GraphicsPath pathSurface = GetCirclePath(rectSurface)) using (GraphicsPath pathBorder = GetCirclePath(rectBorder)) using (Pen penSurface = new Pen(this.Parent.BackColor, smoothSize)) { // 设置绘图品质 pe.Graphics.SmoothingMode = SmoothingMode.AntiAlias; // 设置区域 this.Region = new Region(pathSurface); // 绘制图片 if (this.Image != null) { using (TextureBrush textureBrush = new TextureBrush(this.Image)) { textureBrush.WrapMode = WrapMode.Clamp; // 配置图片缩放 Matrix matrix = new Matrix(); if (this.SizeMode == PictureBoxSizeMode.Zoom) { float scale = Math.Min((float)this.Width / this.Image.Width, (float)this.Height / this.Image.Height); float x = (this.Width - (this.Image.Width * scale)) / 2; float y = (this.Height - (this.Image.Height * scale)) / 2; matrix.Translate(x, y); matrix.Scale(scale, scale); } else { matrix.Scale((float)this.Width / this.Image.Width, (float)this.Height / this.Image.Height); } textureBrush.Transform = matrix; pe.Graphics.FillPath(textureBrush, pathSurface); } } // 绘制表面边缘 pe.Graphics.DrawPath(penSurface, pathSurface); // 绘制边框 if (borderSize >= 1) { using (LinearGradientBrush borderGColor = new LinearGradientBrush( rectBorder, borderColor, borderColor2, gradientAngle)) using (Pen penBorder = new Pen(borderGColor, borderSize)) { penBorder.DashStyle = borderLineStyle; penBorder.DashCap = borderCapStyle; penBorder.Alignment = PenAlignment.Center; pe.Graphics.DrawPath(penBorder, pathBorder); } } } } private GraphicsPath GetCirclePath(Rectangle rect) { GraphicsPath path = new GraphicsPath(); path.AddEllipse(rect); return path; } } }

image.png

增加悬停动画

C#
using System; using System.Collections.Generic; using System.Drawing.Drawing2D; using System.Linq; using System.Text; using System.Threading.Tasks; namespace AppControls { public class CircularPictureBox : PictureBox { private int borderSize = 2; private Color borderColor = Color.RoyalBlue; private Color borderColor2 = Color.HotPink; private DashStyle borderLineStyle = DashStyle.Solid; private DashCap borderCapStyle = DashCap.Flat; private float gradientAngle = 50F; // 添加动画相关字段 private bool isHovered = false; private float currentScale = 1.0f; private float targetScale = 1.0f; private float hoverScale = 1.1f; // 悬停时的放大比例 private Color hoverBorderColor; private Color hoverBorderColor2; private System.Windows.Forms.Timer animationTimer; private float glowOpacity = 0f; private bool enableHoverEffect = true; public CircularPictureBox() { this.Size = new Size(100, 100); this.SizeMode = PictureBoxSizeMode.StretchImage; this.BackColor = Color.Transparent; // 初始化悬停颜色 hoverBorderColor = Color.FromArgb(255, 0, 174, 255); // 明亮的蓝色 hoverBorderColor2 = Color.FromArgb(255, 255, 0, 128); // 明亮的粉色 // 初始化动画计时器 animationTimer = new System.Windows.Forms.Timer(); animationTimer.Interval = 16; // 约60fps animationTimer.Tick += AnimationTimer_Tick; // 添加鼠标事件 this.MouseEnter += CircularPictureBox_MouseEnter; this.MouseLeave += CircularPictureBox_MouseLeave; } private void CircularPictureBox_MouseEnter(object sender, EventArgs e) { if (!enableHoverEffect) return; isHovered = true; targetScale = hoverScale; if (!animationTimer.Enabled) animationTimer.Start(); } private void CircularPictureBox_MouseLeave(object sender, EventArgs e) { if (!enableHoverEffect) return; isHovered = false; targetScale = 1.0f; if (!animationTimer.Enabled) animationTimer.Start(); } private void AnimationTimer_Tick(object sender, EventArgs e) { bool needsUpdate = false; // 更新缩放动画 if (Math.Abs(currentScale - targetScale) > 0.001f) { currentScale += (targetScale - currentScale) * 0.2f; needsUpdate = true; } // 更新发光效果 float targetGlow = isHovered ? 1f : 0f; if (Math.Abs(glowOpacity - targetGlow) > 0.001f) { glowOpacity += (targetGlow - glowOpacity) * 0.2f; needsUpdate = true; } if (needsUpdate) { this.Invalidate(); } else { animationTimer.Stop(); } } // 优化双缓冲设置 protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true); } public bool EnableHoverEffect { get => enableHoverEffect; set => enableHoverEffect = value; } public float HoverScale { get => hoverScale; set => hoverScale = value; } public int BorderSize { get { return borderSize; } set { borderSize = value; this.Invalidate(); } } public Color BorderColor { get { return borderColor; } set { borderColor = value; this.Invalidate(); } } public Color BorderColor2 { get { return borderColor2; } set { borderColor2 = value; this.Invalidate(); } } public DashStyle BorderLineStyle { get { return borderLineStyle; } set { borderLineStyle = value; this.Invalidate(); } } public DashCap BorderCapStyle { get { return borderCapStyle; } set { borderCapStyle = value; this.Invalidate(); } } public float GradientAngle { get { return gradientAngle; } set { gradientAngle = value; this.Invalidate(); } } protected override void OnResize(EventArgs e) { base.OnResize(e); this.Size = new Size(this.Width, this.Height); } protected override void OnPaint(PaintEventArgs pe) { base.OnPaint(pe); // 计算缩放后的矩形 float scaleFactor = currentScale; int widthDiff = (int)((this.Width * scaleFactor) - this.Width) / 2; int heightDiff = (int)((this.Height * scaleFactor) - this.Height) / 2; Rectangle scaledRect = new Rectangle( -widthDiff, -heightDiff, (int)(this.Width * scaleFactor), (int)(this.Height * scaleFactor) ); Rectangle rectSurface = scaledRect; Rectangle rectBorder = Rectangle.Inflate(rectSurface, -borderSize, -borderSize); int smoothSize = 2; if (borderSize > 0) smoothSize = borderSize; using (GraphicsPath pathSurface = GetCirclePath(rectSurface)) using (GraphicsPath pathBorder = GetCirclePath(rectBorder)) using (Pen penSurface = new Pen(this.Parent.BackColor, smoothSize)) { pe.Graphics.SmoothingMode = SmoothingMode.AntiAlias; this.Region = new Region(pathSurface); // 绘制发光效果 if (glowOpacity > 0) { using (GraphicsPath glowPath = GetCirclePath(Rectangle.Inflate(rectSurface, 5, 5))) using (PathGradientBrush glowBrush = new PathGradientBrush(glowPath)) { Color glowColor = Color.FromArgb( (int)(100 * glowOpacity), hoverBorderColor ); glowBrush.CenterColor = glowColor; glowBrush.SurroundColors = new Color[] { Color.FromArgb(0, glowColor) }; pe.Graphics.FillPath(glowBrush, glowPath); } } // 绘制图片 if (this.Image != null) { using (TextureBrush textureBrush = new TextureBrush(this.Image)) { textureBrush.WrapMode = WrapMode.Clamp; Matrix matrix = new Matrix(); if (this.SizeMode == PictureBoxSizeMode.Zoom) { float scale = Math.Min((float)scaledRect.Width / this.Image.Width, (float)scaledRect.Height / this.Image.Height); float x = (scaledRect.Width - (this.Image.Width * scale)) / 2 + scaledRect.X; float y = (scaledRect.Height - (this.Image.Height * scale)) / 2 + scaledRect.Y; matrix.Translate(x, y); matrix.Scale(scale, scale); } else { matrix.Scale((float)scaledRect.Width / this.Image.Width, (float)scaledRect.Height / this.Image.Height); matrix.Translate(scaledRect.X, scaledRect.Y); } textureBrush.Transform = matrix; pe.Graphics.FillPath(textureBrush, pathSurface); } } // 绘制表面边缘 pe.Graphics.DrawPath(penSurface, pathSurface); // 绘制边框 if (borderSize >= 1) { Color currentBorderColor = isHovered ? hoverBorderColor : borderColor; Color currentBorderColor2 = isHovered ? hoverBorderColor2 : borderColor2; using (LinearGradientBrush borderGColor = new LinearGradientBrush( rectBorder, currentBorderColor, currentBorderColor2, gradientAngle)) using (Pen penBorder = new Pen(borderGColor, borderSize)) { penBorder.DashStyle = borderLineStyle; penBorder.DashCap = borderCapStyle; penBorder.Alignment = PenAlignment.Center; pe.Graphics.DrawPath(penBorder, pathBorder); } } } } private GraphicsPath GetCirclePath(Rectangle rect) { GraphicsPath path = new GraphicsPath(); path.AddEllipse(rect); return path; } // 确保在控件销毁时释放资源 protected override void Dispose(bool disposing) { if (disposing) { animationTimer.Dispose(); } base.Dispose(disposing); } } }

性能优化建议

  1. 在不需要重绘时避免调用Invalidate()
  2. 合理设置图片的SizeMode
  3. 使用using语句确保及时释放GDI+资源
  4. 可以考虑添加双缓冲以提升显示效果

这个自定义控件为WinForms应用程序提供了一个美观的圆形图片显示解决方案,特别适合用于显示用户头像、圆形标志等场景。通过调整各种属性,可以实现丰富的视觉效果。

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!