-1

I am working on converting a UIelement into stream using RenderTargetBitmap in uwp application. But I dont know how to make the thread wait till the conversion of stream. I have tried using the suggestions but I could not the find the suitable solution. Any help is much appreciated.

   {
      for (int count = 0; count <= Textpage.Children.Count; count++)
      {
         if (Textpage.Children[count] is Viewbox)
         {
            CustomView customView = (Textpage.Children[count] as Viewbox).Child as CustomView;
            UIElement frameworkElement = customView as UIElement;
            (Textpage.Children[count] as Viewbox).Child = null;
            var element = (PdfDocumentPanel.Children[count] as Border).Child as Canvas;
            Textpage.Children.Remove((Textpage.Children[count] as Viewbox));

            SaveCustomAnnotation(frameworkElement, pageIndex, documentToBeSaved);

         }
      }

   }

   Stream savedStream = new MemoryStream();
}

internal async void SaveCustomAnnotation(UIElement element, int pageIndex, PdfLoadedDocument loadedDocument)
{

   Type pageChild = null;

   IRandomAccessStream randomStream;

   randomStream = await Task.Run(() => ConvertToImage(element));

}

public async Task<IRandomAccessStream> ConvertToImage(UIElement element)
{

   InMemoryRandomAccessStream randomAccessStream = null;
   IBuffer pixelBuffer = null;
   RenderTargetBitmap bitmap = new RenderTargetBitmap();


   await bitmap.RenderAsync(element);

   pixelBuffer = await bitmap.GetPixelsAsync();

   var logicalDpi = DisplayInformation.GetForCurrentView().LogicalDpi;
   using (randomAccessStream = new InMemoryRandomAccessStream())
   {
      var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, randomAccessStream);
      encoder.SetPixelData(
      BitmapPixelFormat.Bgra8,
      BitmapAlphaMode.Ignore,
      (uint)bitmap.PixelWidth,
      (uint)bitmap.PixelHeight,
      logicalDpi,
      logicalDpi,
      pixelBuffer.ToArray());
      await encoder.FlushAsync();

   }
   return randomAccessStream;
}

I want the SaveCustomannotation method to be entirely completed before moving initializing new memory stream

TheGeneral
  • 79,002
  • 9
  • 103
  • 141
CoderRJP
  • 29
  • 5
  • 1
    Relevant: [Async: When to return a task vs. void](https://stackoverflow.com/questions/12144077/async-await-when-to-return-a-task-vs-void). Async void is very different not just because it can't be awaited but because any unhandled exception in an async void method will likely cause your program to crash. – John Wu Apr 12 '19 at 05:24
  • See also https://stackoverflow.com/questions/14455293/how-and-when-to-use-async-and-await/44204614#44204614, which has extensive information about the C# `async`/`await` feature. You are hardly the first person to ask a question on SO about not being able to wait for `async void` methods. – Peter Duniho Apr 12 '19 at 05:58
  • @Jonathon: _"we're going to see a lot of different variations of"_ -- puts it in the same category of `NullReferenceException`. I might have been a bit short, but the fact is, people are posting these kinds of questions without doing any real research. Every reasonable reference discussing the C# `async`/`await` feature, including Microsoft's own documentation, numerous articles on Stack Overflow (including the de facto canonical one I provided the link for), and various experts' blog articles, all explain the hazards of `async void`. It's fundamental. – Peter Duniho Apr 12 '19 at 06:10

1 Answers1

8

The problem is here is the misuse of async void, this prevented you from awaiting it. A good rule when you are starting out is, if you type the words async void stop... take a step back from your computer, and tell your self there is probably a better way... They should primarily be used for event handlers.

Basically SaveCustomAnnotation should return a Task

internal async Task SaveCustomAnnotation(UIElement element, int pageIndex, PdfLoadedDocument loadedDocument)

Then you would need to await it

await SaveCustomAnnotation(frameworkElement, pageIndex, documentToBeSaved);

As such, its calling method should be async Task as well (all the way up the call chain), only then should should their be an async void and only if it's an event handler.

Last note

Async void methods have different error-handling semantics. When an exception is thrown out of an async Task or async Task<T> method, that exception is captured and placed on the Task. With async void methods, there is no Task object (they run unobserved), so any exceptions thrown out of an async void method will be raised directly on the SynchronizationContext that was active when it started.

In short always catch and handle exceptions from with in such methods


As Jonathon Chase eluded to (and rightly so).

If this is all it does, you should probably just call it from the main method and await it, as it returns a task anyway and marked async

internal async void SaveCustomAnnotation(UIElement element, int pageIndex, PdfLoadedDocument loadedDocument)
{
   Type pageChild = null;
   IRandomAccessStream randomStream;
   randomStream = await Task.Run(() => ConvertToImage(element));
}

You could remove the entire method and just replace

await SaveCustomAnnotation(frameworkElement, pageIndex, documentToBeSaved);

with

var readstream = await ConvertToImage(frameworkElement); 

There is more that is iffy about this code, however I think they should probably fall under another question

halfer
  • 19,824
  • 17
  • 99
  • 186
TheGeneral
  • 79,002
  • 9
  • 103
  • 141