.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
相关产品推荐
相关产品推荐

