在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;
}
}
}
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);
}
}
}
Invalidate()
SizeMode
using
语句确保及时释放GDI+资源这个自定义控件为WinForms应用程序提供了一个美观的圆形图片显示解决方案,特别适合用于显示用户头像、圆形标志等场景。通过调整各种属性,可以实现丰富的视觉效果。
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!