You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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类型,默认值设为2
  • CrossPaddingRatio:叉号距离圆形边缘的内边距占比,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事件,所有绘制逻辑写在事件回调里,绘制顺序严格按从底到顶来:

  1. 计算基础布局参数:取当前画布的宽高短边为基准计算圆半径,圆心固定在画布中心,避免控件非等比拉伸时圆形变形
  2. 绘制底部填充圆形:创建SKPaint对象设置Style = SKPaintStyle.Fill,颜色从Avalonia的Color转成SKColor,调用canvas.DrawCircle()绘制圆形底
  3. 绘制上层叉号:新建描边用的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 18:33:31