使用WCF调用发送Inkcanvas签名



我有一个Windows 10 UWP应用程序,该应用程序将在Windows 10移动设备上运行。我的要求是捕获用户的签名。到目前为止,我只是在XAML中使用Inkcanvas,并将其连接到我的代码后面。

i然后有一个按钮,当单击时,将在Inkcanvas上进行签名,并通过WCF调用将其发送到服务器。服务器和WCF服务已经存在。它以base64序列化字符串为单位。

我知道如何获得base64,一旦我拥有图像或字节数组。但是,在我的数小时的阅读中,我发现文章/示例是为WPF或Windows 8.1编写的,并且在Windows 10 UWP上不起作用。另外,在我发现将有效的示例中,我唯一的选择是将签名保存为gif。

我看到我可以像这个

一样调用getTrokes()
 var strokeCollection = cvsSignature.InkPresenter.StrokeContainer.GetStrokes();

这将使我仅读取inkstroke的读取列表。我想我可以迭代该列表并构建字节数组吗?我该怎么做?看来这不是有效的?

否则,我认为我可以将流从文件流更改为内存流,但我想这是不可能的,或者我缺少一些东西。我正在尝试这个

 using (var inkMemStream = new MemoryStream())
 {
     await cvsSignature.InkPresenter.StrokeContainer.SaveAsync(inkMemStream);
 }

但是,使用这种方法,我会得到一个例外,我无法从System.io.memorystream转换为Windows.storage.storage.Streams.ioutputStream

谢谢!

我找到了一种方法,如果您不想将Inkcanvas保存到文件或GIF,则在内存中操纵它。我实际上有三个选择。请注意,对于其中的某些情况,您需要添加对Win2D.UWP的引用,可以通过搜索确切的名称在Nuget上找到。它由Microsoft提供。

  1. 将Inkcanvas转换为字节数组:

    private byte[] ConvertInkCanvasToByteArray()
    {
        //First, we need to get all of the strokes in the canvas
        var canvasStrokes = myCanvas.InkPresenter.StrokeContainer.GetStrokes();
        //Just as a check, make sure to only do work if there are actually strokes (ie not empty)
        if (canvasStrokes.Count > 0)
        {
            var width = (int)myCanvas.ActualWidth;
            var height = (int)myCanvas.ActualHeight;
            var device = CanvasDevice.GetSharedDevice();
            //Create a new renderTarget with the same width and height as myCanvas at 96dpi
            var renderTarget = new CanvasRenderTarget(device, width,
                height, 96);
            using (var ds = renderTarget.CreateDrawingSession())
            {
                //This will clear the renderTarget with a clean slate of a white background.
                ds.Clear(Windows.UI.Colors.White);
                //Here is where we actually take the strokes from the canvas and draw them on the render target.
                ds.DrawInk(myCanvas.InkPresenter.StrokeContainer.GetStrokes());
            }
            //Finally, this will return the render target as a byte array.
            return renderTarget.GetPixelBytes();
        }
        else
        {
            return null;
        }
    }
    
  2. 如果您需要的只是像素的字节数组,则完成。但是,如果您现在想生成图像,则使用WritableBitMap进行此操作。这是可以称为执行此操作的同步和异步方法。

    private WriteableBitmap GetSignatureBitmapFull()
    {
        var bytes = ConvertInkCanvasToByteArray();
        if (bytes != null)
        {
            var width = (int)cvsSignature.ActualWidth;
            var height = (int)cvsSignature.ActualHeight;
            var bmp = new WriteableBitmap(width, height);
            using (var stream = bmp.PixelBuffer.AsStream())
            {
                stream.Write(bytes, 0, bytes.Length);
                return bmp;
            }
        }
        else
            return null;
    }
    private async Task<WriteableBitmap> GetSignatureBitmapFullAsync()
    {
        var bytes = ConvertInkCanvasToByteArray();
        if (bytes != null)
        {
            var width = (int)cvsSignature.ActualWidth;
            var height = (int)cvsSignature.ActualHeight;
            var bmp = new WriteableBitmap(width, height);
            using (var stream = bmp.PixelBuffer.AsStream())
            {
                await stream.WriteAsync(bytes, 0, bytes.Length);
                return bmp;
            }
        }
        else
            return null;
    }
    
  3. 最后,这是一种异步方法,如果您想这样做,我用来在位图上进行农作物。这种方法的关键是注意如何使用这种方法,您不必先将Pixel字节作为数组。您只需从画布中获得笔触,然后将其直接保存到内存流中

    private async Task<WriteableBitmap> GetSignatureBitmapCropped()
    {
        try
        {
            var canvasStrokes = cvsSignature.InkPresenter.StrokeContainer.GetStrokes();
            if (canvasStrokes.Count > 0)
            {
                var bounds = cvsSignature.InkPresenter.StrokeContainer.BoundingRect;
                var xOffset = (uint)Math.Round(bounds.X);
                var yOffset = (uint)Math.Round(bounds.Y);
                var pixelWidth = (int)Math.Round(bounds.Width);
                var pixelHeight = (int)Math.Round(bounds.Height);
                using (var memStream = new InMemoryRandomAccessStream())
                {
                    await cvsSignature.InkPresenter.StrokeContainer.SaveAsync(memStream);
                    var decoder = await BitmapDecoder.CreateAsync(memStream);
                    var transform = new BitmapTransform();
                    var newBounds = new BitmapBounds();
                    newBounds.X = 0;
                    newBounds.Y = 0;
                    newBounds.Width = (uint)pixelWidth;
                    newBounds.Height = (uint)pixelHeight;
                    transform.Bounds = newBounds;
                    var pdp = await decoder.GetPixelDataAsync(
                        BitmapPixelFormat.Bgra8,
                        BitmapAlphaMode.Straight,
                        transform,
                        ExifOrientationMode.IgnoreExifOrientation,
                        ColorManagementMode.DoNotColorManage);
                    var pixels = pdp.DetachPixelData();
                    var cropBmp = new WriteableBitmap(pixelWidth, pixelHeight);
                    using (var stream = cropBmp.PixelBuffer.AsStream())
                    {
                        await stream.WriteAsync(pixels, 0, pixels.Length);
                    }
                    return cropBmp;
                }
            }
            else
            {
                return null;
            }
        }
        catch (Exception ex)
        {
            return null;
        }
    }
    

希望这有助于在Windows 10 Universal中使用InkCanvas时提供一些替代方案。

似乎我唯一的选择是将签名保存为gif。

GIF不是唯一的选择。官方样本只显示GIF,但您也可以将Inkstokes Collection保存到JPGPNG。代码如下:

 var savePicker = new Windows.Storage.Pickers.FileSavePicker();
 savePicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
 savePicker.FileTypeChoices.Add("Gif,JPG,PNG", new System.Collections.Generic.List<string> { ".jpg" ,".gif",".png"}); 
 Windows.Storage.StorageFile file = await savePicker.PickSaveFileAsync();

但是,使用这种方法,我会得到一个例外,我无法从System.io.memorystream转换为Windows.storage.storage.Streams.ioutputStream

如您所见,它不能直接从System.IO.MemoryStream转换为Windows.Storage.Streams.IOutputStream。您需要读取刚刚保存为ImageSource的图像文件。如您所知,一旦拥有图像或字节数组,如何获得base64。因此,我们只需要从图像文件中获取图像或字节数组。

将图像文件读为字节数组,然后将内存流读取如下

if (inkCanvas.InkPresenter.StrokeContainer.GetStrokes().Count > 0)
{
    var savePicker = new Windows.Storage.Pickers.FileSavePicker();
    savePicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
    savePicker.FileTypeChoices.Add("Gif,JPG,PNG", new System.Collections.Generic.List<string> { ".jpg", ".gif", ".png" });
    Windows.Storage.StorageFile file = await savePicker.PickSaveFileAsync();
    if (null != file)
    {
        using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
        {
            await inkCanvas.InkPresenter.StrokeContainer.SaveAsync(stream);
        }
        using (IRandomAccessStream streamforread = await file.OpenAsync(FileAccessMode.Read))
        {
            WriteableBitmap inkimagesource = new WriteableBitmap(50, 50);
            inkimagesource.SetSource(streamforread);
            byte[] imageBuffer = inkimagesource.PixelBuffer.ToArray();
            MemoryStream ms = new MemoryStream(imageBuffer);
        }
    }
}

将文件读取为图像源,只需更改文件读取代码,如下所示:

using (IRandomAccessStream streamforread = await file.OpenAsync(FileAccessMode.Read)
{ 
   var bitmap = new BitmapImage();
   bitmap.SetSource(streamforread); 
}

我想我可以迭代该列表并构建字节数组吗?我该怎么做?看来这不是有效的?

目前似乎没有API可以直接将InkStroke集合构建为字节数组。saveasync()方法已经帮助您将InkStroke集合保存到流中,您可以使用上面的代码来使用它。