如何在Avalonia中使用SkiaSharp绘制带填充色的叉形圆形控件
Avalonia 基于SkiaSharp实现填充叉号圆形自定义控件方案
核心实现逻辑
参考WPF UserControl封装的思路完全可行,整体流程和WPF自定义绘制控件差异很小,不需要额外适配特殊逻辑,分三步实现即可:
1. 前置准备
首先给项目安装对应Avalonia版本的SkiaSharp.Views.Avalonia Nuget包,这是Skia在Avalonia下的渲染承载组件。
2. 自定义控件结构与属性定义
新建继承自UserControl的自定义控件CrossCircleControl,对照WPF依赖属性的写法,用Avalonia的StyledProperty定义所有可配置参数,保证支持XAML赋值、绑定、样式设置:
CircleFill:圆形填充色,类型为Avalonia.Media.Color,默认值可设为浅灰色CrossStroke:叉号线条颜色,同Color类型CrossThickness:叉号线条粗细,double类型,默认值设为2CrossPaddingRatio:叉号距离圆形边缘的内边距占比,double类型,默认0.2(即叉号长度为圆直径的60%)
属性定义示例:
public static readonly StyledProperty<Color> CircleFillProperty = AvaloniaProperty.Register<CrossCircleControl, Color>(nameof(CircleFill), defaultValue: Colors.LightGray); public Color CircleFill { get => GetValue(CircleFillProperty); set => SetValue(CircleFillProperty, value); }
所有属性注册时添加值变更回调,属性修改时调用内部承载的Skia画布的InvalidateSurface()方法触发重绘,逻辑和WPF中重写属性元数据调用InvalidateVisual()完全一致。
3. Skia渲染逻辑实现
在自定义控件的XAML中放一个撑满整个控件的SKCanvasView,订阅它的PaintSurface事件,所有绘制逻辑写在事件回调里,绘制顺序严格按从底到顶来:
- 计算基础布局参数:取当前画布的宽高短边为基准计算圆半径,圆心固定在画布中心,避免控件非等比拉伸时圆形变形
- 绘制底部填充圆形:创建
SKPaint对象设置Style = SKPaintStyle.Fill,颜色从Avalonia的Color转成SKColor,调用canvas.DrawCircle()绘制圆形底 - 绘制上层叉号:新建描边用的
SKPaint对象,设置Style = SKPaintStyle.Stroke、StrokeCap = SKStrokeCap.Round(圆角端点更美观),按设置的内边距比例计算两条交叉线的四个端点坐标,调用两次canvas.DrawLine()画出X形叉号即可
核心绘制代码示例:
private void OnPaintSurface(object? sender, SKPaintSurfaceEventArgs e) { var canvas = e.Surface.Canvas; canvas.Clear(); // 清空画布避免残影 // 计算居中布局 var width = e.Info.Width; var height = e.Info.Height; var radius = Math.Min(width, height) / 2f * 0.95f; // 留5%边距避免圆形边缘被裁切 var centerX = width / 2f; var centerY = height / 2f; // 画填充圆 using var circlePaint = new SKPaint { Style = SKPaintStyle.Fill, Color = CircleFill.ToSKColor(), IsAntialias = true // 开抗锯齿 }; canvas.DrawCircle(centerX, centerY, radius, circlePaint); // 画叉号 var crossOffset = radius * (1 - CrossPaddingRatio); using var crossPaint = new SKPaint { Style = SKPaintStyle.Stroke, Color = CrossStroke.ToSKColor(), StrokeWidth = CrossThickness, StrokeCap = SKStrokeCap.Round, IsAntialias = true }; // 左上到右下 canvas.DrawLine(centerX - crossOffset, centerY - crossOffset, centerX + crossOffset, centerY + crossOffset, crossPaint); // 右上到左下 canvas.DrawLine(centerX + crossOffset, centerY - crossOffset, centerX - crossOffset, centerY + crossOffset, crossPaint); }
开发注意点
- 所有SKPaint对象用完记得用
using包裹释放,避免内存泄漏,这和WPF的DrawingContext使用逻辑有区别,Skia的绘制对象是非托管资源需要手动释放 - 不要硬编码像素值,所有尺寸基于当前画布的实际宽高计算,自动适配不同DPI的屏幕
- 如果需要给控件加交互、命令、模板,逻辑和WPF的UserControl完全一致,没有额外学习成本
内容的提问来源于stack exchange,提问作者oknevermind
相关产品推荐
相关产品推荐

