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

.NET MAUI DrawingView获取完整绘图点及服务端签名渲染问题

解决.NET MAUI DrawingView签名点不足、服务端渲染不完整的问题

问题描述

将旧Xamarin Forms应用迁移至.NET MAUI,使用DrawingView替代原SignaturePad控件后,发现DrawingView生成的线条和点数量远少于原控件,导致服务端渲染的签名极不完整且严重像素化,需要解决如何获取足够的点/线,让服务端能渲染出完整清晰的签名。

签名对比示例:左侧为原SignaturePad生成的完整清晰签名,右侧为DrawingView生成的不完整且像素化的签名

当前实现代码:

XAML代码

<toolkit:DrawingView
x:Name="MySignatureView"
BackgroundColor="DarkGray"  
DrawingLineCompletedCommand="{Binding ExecuteGetSignatureCommand}"
HorizontalOptions="FillAndExpand"
IsEnabled="{Binding IsLocked}"
IsMultiLineModeEnabled="True"
LineColor="Black"
LineWidth="1"
Lines="{Binding DrawingLines, Mode=TwoWay}"
ShouldClearOnFinish="False"
VerticalOptions="FillAndExpand"/>

后台代码

MySignatureView.DrawAction = (ICanvas canvas, RectF rect) =>
{
    canvas.DrawString("Signature", 0, 0, (int)MySignatureView.Width, (int)MySignatureView.Height, HorizontalAlignment.Center, VerticalAlignment.Center);
};

视图模型代码

[ObservableProperty]
ObservableCollection<IDrawingLine> drawingLines = new();
public async void ExecuteGetSignature(IDrawingLine drawingLines)
{
    try
    {
        List<Point>? currentPoints = drawingLines.Points.Select(pointF => new Point(pointF.X, pointF.Y)).ToList();

        UpdatePoints(currentPoints);

        var gestureImage = new Image();
        using var stream = await drawingLines.GetImageStream(gestureImage.Width, gestureImage.Height, Colors.Gray.AsPaint());

        using var memoryStream = new MemoryStream();
        await stream.CopyToAsync(memoryStream);

        stream.Position = 0;
        memoryStream.Position = 0;

        SignatureAsBase64String = Convert.ToBase64String(memoryStream.ToArray());
    }
    catch (Exception ex)
    {
        ex.Tag();
    }
}


public double ZeroTolerance { get; set; }
private void UpdatePoints(List<Point> points)
{
    List<Point[]> lines = new();

    List<Point> currentLine = new();

    ZeroTolerance = 0;

    foreach (Point point in points)
    {
        if ((Math.Abs(point.X) < ZeroTolerance && (Math.Abs(point.Y) < ZeroTolerance)) || (Math.Abs(point.X - (-10000)) < ZeroTolerance && Math.Abs(point.Y - (-10000)) < ZeroTolerance))
        {
            lines.Add(currentLine.ToArray());
            currentLine = new List<Point>();
        }
        else
        {
            currentLine.Add(point);
        }
    }

    lines.Add(currentLine.ToArray());

    Lines = lines;
}

解决步骤

1. 调整DrawingView的点采样参数

MAUI Toolkit的DrawingView默认会对采集的点进行精简,通过PointsCollectionThreshold和PointsCollectionInterval控制采样密度。将这两个值调小,可获取更密集的点:

<toolkit:DrawingView
    ...
    PointsCollectionThreshold="1"
    PointsCollectionInterval="1"
    .../>
  • PointsCollectionThreshold:移动超过指定像素才记录新点,设为1表示每移动1像素就记录
  • PointsCollectionInterval:间隔指定毫秒记录新点,设为1表示每1毫秒记录一次

2. 修正点过滤逻辑

UpdatePoints方法中ZeroTolerance=0的过滤条件会误删有效点,要么删除该过滤逻辑,要么调整阈值为合理值(比如5):

private void UpdatePoints(List<Point> points)
{
    List<Point[]> lines = new();
    List<Point> currentLine = new();
    // 调整阈值为合理值,或直接移除过滤逻辑
    ZeroTolerance = 5;

    foreach (Point point in points)
    {
        if ((Math.Abs(point.X) < ZeroTolerance && Math.Abs(point.Y) < ZeroTolerance) 
            || (Math.Abs(point.X + 10000) < ZeroTolerance && Math.Abs(point.Y + 10000) < ZeroTolerance))
        {
            if(currentLine.Any()) // 避免添加空数组
            {
                lines.Add(currentLine.ToArray());
                currentLine = new List<Point>();
            }
        }
        else
        {
            currentLine.Add(point);
        }
    }

    if(currentLine.Any()) // 避免添加空数组
    {
        lines.Add(currentLine.ToArray());
    }

    Lines = lines;
}

3. 生成高清签名图片

当前获取图片流时使用新建Image的默认尺寸(大概率极小),导致图片模糊。需传入DrawingView的实际宽高:

// 替换原尺寸获取逻辑
int width = (int)MySignatureView.Width;
int height = (int)MySignatureView.Height;
using var stream = await drawingLines.GetImageStream(width, height, Colors.Gray.AsPaint());

若ViewModel无法直接获取DrawingView尺寸,可通过绑定将宽高传递到ViewModel中。

4. 优先上传原始点数据(推荐)

如果服务端支持,直接上传所有点的坐标数据(而非图片),服务端用SVG或Canvas进行矢量渲染,可彻底避免像素化问题:

// 在ExecuteGetSignature中序列化点数据
var signatureData = new 
{
    Lines = DrawingLines.Select(line => new 
    {
        Points = line.Points.Select(p => new { X = p.X, Y = p.Y }),
        LineWidth = line.LineWidth,
        LineColor = line.LineColor.ToHex()
    })
};
string jsonData = JsonSerializer.Serialize(signatureData);
// 上传jsonData到服务端,服务端用SVG/Canvas渲染

内容的提问来源于stack exchange,提问作者Edward Whitehead

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 13:45:16