locked
How use Paginate Event (Async problem)

    Question

  • The problem is PrintDocument.Paginate is a synchronous event handler and its called from UI thread.

    Due to WinRT ASYNC VIRUS some of my methods have asynchronous nature. And some of them using UI elements as it prepare pages for printing. This means that MyPreparePageAsync for example must be called also from UI thread, so directly from synchronous Paginate handler. The question is - how i must WAIT async methods in current thread without stoped this thread from execution ?!

    1. I cant set async keyword on handler because WinRT start displaying pages before it prepared

    2. I cant change void to Task as it dont match delegate signature

    3. I cant use AsTask().Wait() because this stop thread and await continues never executed

    4. I cant run my method on other thread because use many of DependencyObjects that needs UI thread.

    Будь проклят тот день, когда я сел за руль этого пылесоса (c)


    Wednesday, April 02, 2014 4:23 PM

All replies

  • I'm not sure what exactly you're asking here. Can you clarify what you're doing? Minimal sample code to demonstrate may help, as will posting with less emotion. You can likely perform the async operations in your PrintTaskSourceRequested event handler, which supports deferrals. Pagination should then be fairly straightforward since you already have the data.

    Also see the Printing guidelines, Quickstart: Printing from your appand the Print Sample sample app

    Thursday, April 03, 2014 1:48 AM
    Owner
  • >> You can likely perform the async operations in your PrintTaskSourceRequested event handler

    No, i can't. In PrintTaskSourceRequested stage i have not info about page descripton. MediaSize, orientation, fields and so on. All this info i have in Paginate event.

    Guidlines very simple and not helped. I make sample code later, but question is simple. How call Async methods in Paginate event handler so that would not have jumped out or hang before the page is prepared.

    Thursday, April 03, 2014 10:12 AM
  • I make sample.

    using System;
    using System.IO;
    using System.Runtime.InteropServices.WindowsRuntime;
    using System.Threading.Tasks;
    using Windows.Foundation;
    using Windows.Graphics.Imaging;
    using Windows.Graphics.Printing;
    using Windows.Storage;
    using Windows.Storage.Streams;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Media.Imaging;
    using Windows.UI.Xaml.Printing;
    
    // The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
    
    namespace PrintTest
    {
        /// <summary>
        /// An empty page that can be used on its own or navigated to within a Frame.
        /// </summary>
        public sealed partial class MainPage : Page
        {
            bool TestAsync = true;
            double SizeMultiple = 1;
            
            StorageFolder Folder = ApplicationData.Current.LocalFolder;
            String FileName = "print-test.png";
            PrintDocument _PrintDocument;
            IPrintDocumentSource _PrintDocumentSource;
    
            public MainPage()
            {
                this.InitializeComponent();
                PrintManager printManager = PrintManager.GetForCurrentView();
                printManager.PrintTaskRequested += printManager_PrintTaskRequested;
    
            }
    
            private void printManager_PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs args)
            {
                // I cannot prepare print content here because i dont have page info on this stage.
                
                // Create document. Marshall to UI thread.
                Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                {
                    _PrintDocument = new PrintDocument();
                    _PrintDocumentSource = _PrintDocument.DocumentSource;
                    _PrintDocument.Paginate += printDocument_Paginate;
                    _PrintDocument.GetPreviewPage += printDocument_GetPreviewPage;
                }).AsTask().Wait();
    
                PrintTask printTask = args.Request.CreatePrintTask("Print test", src =>
                {
                    src.SetSource(_PrintDocumentSource); 
                });
            }
    
            private void printDocument_Paginate(object sender, PaginateEventArgs e)
            {
                _PrintDocument.SetPreviewPageCount(1, PreviewPageCountType.Final);
                PrintPageDescription pageDescr = e.PrintTaskOptions.GetPageDescription(0);
    
                if (!TestAsync) PrepareContent(pageDescr);  // Synchronous method work properly
                else
                {
                    //In my real app due to async virus my method can only be asynchronous
                    //For demonstration async problem has to work long enough to return from the method on await. Play with SizeMultiple
                    var t = PrepareContentAsync(pageDescr);
                    t.Wait(); // Set Breakpoint 1 here. With proper multiplier this breakpoint raise before breakpoint 2 and app hangs because UI thread locked and await continues never executed.
                }
    
            }
    
            private void printDocument_GetPreviewPage(object sender, GetPreviewPageEventArgs e) { }
    
            private void PrepareContent(PrintPageDescription pageDescr)
            {
                BitmapSource bs = PayloadSync(pageDescr.ImageableRect);
                _PrintDocument.SetPreviewPage(1, GetPage(bs));
            }
    
            private async Task PrepareContentAsync(PrintPageDescription pageDescr)
            {
                BitmapSource bs = await PayloadAsync(pageDescr.ImageableRect);
                _PrintDocument.SetPreviewPage(1, GetPage(bs));
            }
    
            private BitmapSource PayloadSync(Rect rect)
            {
                return MakeSampleBitmap(Convert.ToInt32(rect.Width * SizeMultiple), Convert.ToInt32(rect.Height * SizeMultiple));
            }
    
            private async Task<BitmapSource> PayloadAsync(Rect rect)
            {
                await SaveSample(rect);
                BitmapSource bs = await LoadImage();
                return bs; // Set breakpoint 2 here.
    
            }
    
            private async Task SaveSample(Rect rect)
            {
                WriteableBitmap wb = MakeSampleBitmap(Convert.ToInt32(rect.Width * SizeMultiple), Convert.ToInt32(rect.Height * SizeMultiple));
                StorageFile file = await Folder.CreateFileAsync(FileName, CreationCollisionOption.ReplaceExisting);
                using (IRandomAccessStream stream = (await file.OpenStreamForWriteAsync()).AsRandomAccessStream())
                {
                    BitmapEncoder enc = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
                    enc.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)wb.PixelWidth, (uint)wb.PixelHeight, 600, 600, wb.PixelBuffer.ToArray());
                    await enc.FlushAsync();
                    await stream.FlushAsync();
                }
            }
    
            private async Task<BitmapSource> LoadImage()
            {
                BitmapImage bi = new BitmapImage();
                using (IRandomAccessStream stream = (await Folder.OpenStreamForReadAsync(FileName)).AsRandomAccessStream())
                {
                    await bi.SetSourceAsync(stream);
                }
                return bi;
            }
            
            private WriteableBitmap MakeSampleBitmap(int width, int height)
            {
                WriteableBitmap wb = new WriteableBitmap(width, height);
                Stream stream = wb.PixelBuffer.AsStream();
                byte[] buffer = new byte[wb.PixelWidth * 4];
                Random rnd = new Random();
    
                for (int i = 0; i < wb.PixelHeight; i++)
                {
                    rnd.NextBytes(buffer);
                    stream.Write(buffer, 0, buffer.Length);
                }
                stream.Flush();
                return wb;
            }
    
            private UIElement GetPage(BitmapSource imageContent)
            {
                Grid root = new Grid();
                Image img = new Image() { HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center, Stretch= Stretch.None };
                img.Source = imageContent;
                root.Children.Add(img);
                return root;
            }
    
            private async void AppBarButton_Tapped(object sender, TappedRoutedEventArgs e)
            {
                await PrintManager.ShowPrintUIAsync();
            }
    
        }
    }
    

    Thursday, April 03, 2014 1:21 PM
  • Is there a solution to this problem? If not, it would be better for all if Microsoft representative confirmed that problems exists.

    1. In general way synchronously executing asynchronous code.

    2. Printning interfaces don't support asynchronous execution.

    Monday, April 07, 2014 1:08 PM