using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics.Skia; using System.Collections.Generic; namespace LSFE.MobApp.Views.CoveragePlan { public class SignaturePadView : GraphicsView { private readonly List> _strokes = new(); private List _currentStroke; private readonly SignatureDrawable _drawable; public SignaturePadView() { _drawable = new SignatureDrawable(_strokes, () => _currentStroke); Drawable = _drawable; BackgroundColor = Colors.White; // Set minimum size to ensure it's touchable MinimumHeightRequest = 200; MinimumWidthRequest = 200; StartInteraction += OnStartInteraction; DragInteraction += OnDragInteraction; EndInteraction += OnEndInteraction; } private void OnStartInteraction(object sender, TouchEventArgs e) { if (e.Touches.Length > 0) { _currentStroke = new List { e.Touches[0] }; Invalidate(); } } private void OnDragInteraction(object sender, TouchEventArgs e) { if (_currentStroke != null && e.Touches.Length > 0) { foreach (var touch in e.Touches) { _currentStroke.Add(touch); } Invalidate(); } } private void OnEndInteraction(object sender, TouchEventArgs e) { if (_currentStroke != null && _currentStroke.Count > 0) { _strokes.Add(new List(_currentStroke)); _currentStroke = null; Invalidate(); } } public void Clear() { _strokes.Clear(); _currentStroke = null; Invalidate(); } public bool IsEmpty() { return _strokes.Count == 0 && (_currentStroke == null || _currentStroke.Count == 0); } public async Task GetSignatureImageAsync() { // if nothing drawn, return null if (IsEmpty()) return null; // Determine size (fall back to default if not laid out yet) var width = (int)Width; var height = (int)Height; if (width <= 0 || height <= 0) { width = 800; height = 400; } // Choose display scale (1.0f is fine; you can use DeviceDisplay.MainDisplayInfo.Density if needed) float displayScale = 1.0f; try { // Create Skia bitmap export context using var bmp = new SkiaBitmapExportContext(width, height, displayScale, dpi: 72, disposeBitmap: true, transparent: false); var canvas = bmp.Canvas; // Draw white background canvas.FillColor = Colors.White; canvas.FillRectangle(0, 0, width, height); // Draw stored strokes _drawable.Draw(canvas, new RectF(0, 0, width, height)); // Write PNG to memory stream using var ms = new MemoryStream(); bmp.WriteToStream(ms); return await Task.FromResult(ms.ToArray()); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Error creating signature image: {ex.Message}"); return null; } } private class SignatureDrawable : IDrawable { private readonly List> _strokes; private readonly Func> _getCurrentStroke; public SignatureDrawable(List> strokes, Func> getCurrentStroke) { _strokes = strokes; _getCurrentStroke = getCurrentStroke; } public void Draw(ICanvas canvas, RectF dirtyRect) { canvas.StrokeColor = Colors.Black; canvas.StrokeSize = 3; canvas.StrokeLineCap = LineCap.Round; canvas.StrokeLineJoin = LineJoin.Round; // Draw completed strokes foreach (var stroke in _strokes) { DrawStroke(canvas, stroke); } // Draw current stroke being drawn in real-time var currentStroke = _getCurrentStroke(); if (currentStroke != null && currentStroke.Count > 0) { DrawStroke(canvas, currentStroke); } } private void DrawStroke(ICanvas canvas, List stroke) { if (stroke.Count > 1) { var path = new PathF(); path.MoveTo(stroke[0]); for (int i = 1; i < stroke.Count; i++) { path.LineTo(stroke[i]); } canvas.DrawPath(path); } else if (stroke.Count == 1) { // Draw single point as small dot var p = stroke[0]; canvas.FillRectangle(p.X - 1.5f, p.Y - 1.5f, 3, 3); } } } } }