2016-11-23 50 views
0

我有一個Windows 10 UWP應用程序,可以在Windows 10 Mobile上運行。我的要求是從用戶那裏獲取簽名。到目前爲止,我只是在XAML中使用InkCanvas,並將它連接到我的代碼後面。使用WCF調用發送InkCanvas簽名

然後我有一個按鈕,點擊後,將在InkCanvas上簽名並通過WCF調用將其發送到服務器。服務器和WCF服務已經存在。它將簽名圖像作爲base64序列化字符串。

我知道如何獲得base64,一旦我有一個圖像或字節數組。但是,在我閱讀的許多小時內,我發現文章/示例是爲WPF或Windows 8.1編寫的,而不適用於Windows 10 UWP。另外,在我發現的例子中,我的唯一選擇是將簽名保存爲GIF文件。

我看到,我可以打電話GetStrokes()這樣的

var strokeCollection = cvsSignature.InkPresenter.StrokeContainer.GetStrokes(); 

將返回我InkStroke的只讀列表。我想我可以遍歷該列表並構建一個字節數組?我會怎麼做?看來這不是有效的?

否則,我想我可以只是將文件流中的流更改爲內存流,但我猜這是不可能的,或者我錯過了一些東西。我想這

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

但隨着這種類型的方法,我感到我無法從System.IO.MemoryStream轉換爲Windows.Storage.Streams.IOutputStream

感謝例外!

回答

1

我找到了一種方法,如果您不想將InkCanvas保存爲文件或GIF,請在內存中對其進行操作。我實際上有三個選項。請注意,對於其中的一些內容,您需要添加一個對Win2D.uwp的引用,它可以在NuGet上搜索確切的名稱。它由微軟提供。

  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. 如果你需要的是像素的字節數組,你做。但是,如果您現在想要生成圖像,則可以使用WriteableBitmap來完成此操作。以下是可以調用的同步和異步方法。

    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. 最後,這裏是我使用的是能夠做到位圖上的作物,如果你想這樣做異步方法。這種方法的關鍵是注意如何使用這種方法,您不必首先獲取像素字節作爲數組。只要讓從畫布的筆觸,然後將其直接保存到內存流

    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使用的通用時InkCanvas提供一些替代品。

0

看來我唯一的選擇是將簽名保存爲GIF文件。

GIF不是唯一的選擇。 official sample只顯示GIF,但您也可以將InkStokes集合保存到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.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集合保存爲流,您可以使用上面的代碼來使用它。

+0

感謝您的詳細示例。我會進一步研究。然而,作爲一個評論,我無法將我的頭圍繞在我必須保存到文件然後從文件中讀取以執行我想要做的事實上。這是一種糟糕且成本高昂的方法,對於不需要首先保存到文件的應用程序而言是不適合的。我很驚訝InkCanvas類省去了直接保存到內存的功能。 –