Search code examples
c#pdfuwp-xamlsyncfusionpdf-viewer

How to convert a xaml page to pdf file and show it in a pdf viewer inside a UWP application?


I am trying to use Syncfusion PDF viewer but in manner to use that I have to eventually save a xaml page as a pdf file. How do I do that?

I followed this link but it doesn't give me what I want. XAML to PDF Conversion

I'm trying below code to save a xaml page [new GenericManifestPDF(_manifestPDFDataModel);] from my view model class and getting this error: 'Value does not fall within the expected range' in this line await renderTargetBitmap.RenderAsync(new GenericManifestPDF(_manifestPDFDataModel));

   PdfDocument document = new PdfDocument();
   //Add a new page
   PdfPage page = document.Pages.Add();
   //Initialize render to bitmap
   var logicalDpi = DisplayInformation.GetForCurrentView().LogicalDpi;
   var renderTargetBitmap = new RenderTargetBitmap();
   //Create a Bitmap from XAML page
   await renderTargetBitmap.RenderAsync(new GenericManifestPDF(_manifestPDFDataModel));
   var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
   //Save the XAML in bitmap image
   using (var stream = new Windows.Storage.Streams.InMemoryRandomAccessStream())
   {
   var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
   encoder.SetPixelData(
   BitmapPixelFormat.Bgra8,
   BitmapAlphaMode.Ignore,
   (uint)renderTargetBitmap.PixelWidth,
   (uint)renderTargetBitmap.PixelHeight,
   logicalDpi,
   logicalDpi,
   pixelBuffer.ToArray());
   await encoder.FlushAsync();
   //Load and draw the bitmap image in PDF
   PdfImage img = PdfImage.FromStream(stream.AsStream());
   if (img.Width > img.Height)
      document.PageSettings.Orientation = PdfPageOrientation.Portrait;
          else                                                                                      document.PageSettings.Orientation = PdfPageOrientation.Landscape;
       var section = document.Sections.Add();
       section.PageSettings.Size = new SizeF(img.Width, img.Height);
       page = section.Pages.Add();
       page.Graphics.DrawImage(img, new RectangleF(0, 0, img.Width, img.Height));
                                        }
        //Save the document
        MemoryStream docStream = new MemoryStream();
        document.Save(docStream);
        //Close the document
        document.Close(true);
        Save(docStream, "SampleManifest.pdf");

This is Save method:

 public async void Save(Stream stream, string filename)
    {
        stream.Position = 0;
        StorageFile stFile;
        if (!(Windows.Foundation.Metadata.ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons")))
        {
            FileSavePicker savePicker = new FileSavePicker();
            savePicker.DefaultFileExtension = ".pdf";
            savePicker.SuggestedFileName = "Output";
            savePicker.FileTypeChoices.Add("Adobe PDF Document", new List<string>() { ".pdf" });
            stFile = await savePicker.PickSaveFileAsync();
        }
        else
        {
            StorageFolder local = Windows.Storage.ApplicationData.Current.LocalFolder;
            stFile = await local.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
        }
        if (stFile != null)
        {
            Windows.Storage.Streams.IRandomAccessStream fileStream = await stFile.OpenAsync(FileAccessMode.ReadWrite);
            Stream st = fileStream.AsStreamForWrite();
            st.Write((stream as MemoryStream).ToArray(), 0, (int)stream.Length);
            st.Flush();
            st.Dispose();
            fileStream.Dispose();
            MessageDialog msgDialog = new MessageDialog("Do you want to view the Document?", "File created.");
            UICommand yesCmd = new UICommand("Yes");
            msgDialog.Commands.Add(yesCmd);
            UICommand noCmd = new UICommand("No");
            msgDialog.Commands.Add(noCmd);
            IUICommand cmd = await msgDialog.ShowAsync();
            if (cmd == yesCmd)
            {
                // Launch the retrieved file
                bool success = await Windows.System.Launcher.LaunchFileAsync(stFile);
            }
        }
    }

This is how I'm binding the data in the upper mentioned page constructor:

public GenericManifestPDF(ManifestPDFDataModel PDFDataModel)
    {
        this.InitializeComponent();
        Function.Text = "Function :" + PDFDataModel.Function;
        Counts.Text = "Count :" + PDFDataModel.Counts;
        PrintedValue.Text = "Printed :" + PDFDataModel.CreationDate;
        RouteValue.Text = "Route :" + PDFDataModel.Route;
        BatchIDValue.Text = "Batch :" + PDFDataModel.BatchID;
        BatchBarcodeText.Text = "Batch Barcode :" + PDFDataModel.Barcode;
        IBarcodeWriter writer = new BarcodeWriter
        {
            Format = BarcodeFormat.CODE_39,
            Options = new ZXing.Common.EncodingOptions
            {
                Height = 100,
                Width = 450
            }
        };

        var result = writer.Write(PDFDataModel.Barcode);
        BarcodeImage.Source = result;
        PDFItemsList.ItemsSource = PDFDataModel.ItemsPDFList;
    }

This is the xaml code of the page:

<Grid x:Name="ManifestPDFGrid">
    <Grid.RowDefinitions>
        <RowDefinition Height="30*"/>
        <RowDefinition Height="70*"/>
    </Grid.RowDefinitions>
    <Grid BorderBrush="Black" BorderThickness="2" Margin="30">
        <Grid.RowDefinitions>
            <RowDefinition Height="80*"/>
            <RowDefinition Height="20*"/>
        </Grid.RowDefinitions>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                <TextBlock Text="Manifest PDF Report" Foreground="Black" FontWeight="Bold" Margin="20,20,0,0"/>
                <TextBlock x:Name="Function" Foreground="Black" Grid.Row="1" FontWeight="Bold" Margin="20,20,0,0"/>
                <TextBlock x:Name="Counts" Grid.Row="2" Foreground="Black" FontWeight="Bold" Margin="20,20,0,0"/>
            </Grid>
            <Grid Grid.Column="1">
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                <TextBlock x:Name="PrintedValue" Foreground="Black" FontWeight="Bold" Margin="20,20,0,0"/>
                <TextBlock x:Name="RouteValue" Foreground="Black" Grid.Row="1" FontWeight="Bold" Margin="20,20,0,0"/>
                <TextBlock x:Name="BatchIDValue" Grid.Row="2" Foreground="Black" FontWeight="Bold" Margin="20,20,0,0"/>
            </Grid>
            <Grid Grid.Column="2">
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                <Image x:Name="BarcodeImage" Margin="20,0,0,0" HorizontalAlignment="Center"/>
                <TextBlock x:Name="BatchBarcodeText" Grid.Row="1" Foreground="Black" FontWeight="Bold" Margin="20,20,0,0"/>
            </Grid>
        </Grid>
        <Grid Grid.Row="2">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <TextBlock Text="Delivered By :" Foreground="Black" Margin="20,0,0,0" VerticalAlignment="Center"/>
            <TextBlock Text="Batch Delivery Time :" Foreground="Black" Grid.Column="1" Margin="20,0,0,0" VerticalAlignment="Center"/>
        </Grid>
    </Grid>
    <Grid Grid.Row="2">
        <Grid.RowDefinitions>
            <RowDefinition Height="10*"/>
            <RowDefinition Height="90*"/>
        </Grid.RowDefinitions>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <TextBlock FontWeight="Bold" Text="Carrier/PKG/BILL#" Foreground="Black" TextDecorations="Underline" Margin="20,20,0,0"/>
            <TextBlock FontWeight="Bold" Grid.Column="1" Foreground="Black" TextDecorations="Underline" Text="Location/Item Type" Margin="20,20,0,0"/>
            <TextBlock FontWeight="Bold" Grid.Column="2" Foreground="Black" TextDecorations="Underline" Text="Deliver To/ Sender" Margin="20,20,0,0"/>
            <TextBlock FontWeight="Bold" Grid.Column="3" Foreground="Black" TextDecorations="Underline" Text="DATE/TIME/PO/Control#" Margin="20,20,0,0"/>
        </Grid>
        <ListView Grid.Row="1" x:Name="PDFItemsList" IsItemClickEnabled="False">
            <ListView.ItemContainerStyle>
                <Style TargetType="ListViewItem">
                    <Setter Property="BorderThickness" Value="0,0,0,1" />
                    <Setter Property="BorderBrush" Value="Black"/>
                    <Setter Property="Width" Value="Auto"/>
                </Style>
            </ListView.ItemContainerStyle>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ContentControl Style="{StaticResource EmptyContentControlStyle}">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="1*"/>
                                <ColumnDefinition Width="1*"/>
                                <ColumnDefinition Width="1*"/>
                                <ColumnDefinition Width="1*"/>
                            </Grid.ColumnDefinitions>
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition/>
                                    <RowDefinition/>
                                    <RowDefinition/>
                                </Grid.RowDefinitions>
                                <TextBlock Text="{Binding CarrierName}" Foreground="Black" Margin="20,20,0,0"/>
                                <TextBlock Text="{Binding PackageID}" Foreground="Black" Grid.Row="1" Margin="20,20,0,0"/>
                                <TextBlock Text="{Binding Bill}" Foreground="Black" Grid.Row="2" Margin="20,20,0,0"/>
                            </Grid>
                            <Grid Grid.Column="1">
                                <Grid.RowDefinitions>
                                    <RowDefinition/>
                                    <RowDefinition/>
                                    <RowDefinition/>
                                </Grid.RowDefinitions>
                                <TextBlock Text="{Binding Location}" Foreground="Black" Margin="20,20,0,0"/>
                                <TextBlock Text="{Binding ItemType}" Foreground="Black" Grid.Row="1" Margin="20,20,0,0"/>
                                <TextBlock Text="{Binding Quantity}" Foreground="Black" Grid.Row="2" Margin="20,20,0,0"/>
                            </Grid>
                            <Grid Grid.Column="2" HorizontalAlignment="Right">
                                <Grid.RowDefinitions>
                                    <RowDefinition/>
                                    <RowDefinition/>
                                    <RowDefinition/>
                                    <RowDefinition/>
                                </Grid.RowDefinitions>
                                <TextBlock Text="{Binding DeliverTo}" HorizontalAlignment="Right" Foreground="Black" Margin="20,20,0,0"/>
                                <TextBlock Text="{Binding Sender}" HorizontalAlignment="Right" Foreground="Black" Grid.Row="1" Margin="20,20,0,0"/>
                                <TextBlock Text="" Foreground="Black" HorizontalAlignment="Right" TextDecorations="Underline" Grid.Row="2" Margin="20,20,0,0"/>
                                <TextBlock Text="Signature" HorizontalAlignment="Right" Foreground="Black" FontWeight="ExtraLight" Grid.Row="3" Margin="20,20,0,0"/>
                            </Grid>
                            <Grid Grid.Column="3" HorizontalAlignment="Right">
                                <Grid.RowDefinitions>
                                    <RowDefinition/>
                                    <RowDefinition/>
                                    <RowDefinition/>
                                    <RowDefinition/>
                                    <RowDefinition/>
                                </Grid.RowDefinitions>
                                <TextBlock Text="{Binding CreationDate}" HorizontalAlignment="Right" Foreground="Black" Margin="20,20,0,0"/>
                                <TextBlock Text="{Binding PONumber}" HorizontalAlignment="Right" Grid.Row="1" Foreground="Black" Margin="20,20,0,0"/>
                                <TextBlock Text="{Binding ControlNumber}" HorizontalAlignment="Right" Grid.Row="2" Foreground="Black" Margin="20,20,0,0"/>
                                <TextBlock Text="" Foreground="Black" TextDecorations="Underline" Grid.Row="3" Margin="20,20,0,0"/>
                                <TextBlock Text="Print Name Here" HorizontalAlignment="Right" Foreground="Black" FontWeight="ExtraLight" Grid.Row="4" Margin="20,20,0,0"/>
                            </Grid>
                        </Grid>
                    </ContentControl>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</Grid>

I'm able to save it in a pdf format but it's not showing properly, I think I need to change something here

  using (var stream = new Windows.Storage.Streams.InMemoryRandomAccessStream())
                                        {
                                            var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
                                            encoder.SetPixelData(
                                            BitmapPixelFormat.Bgra8,
                                            BitmapAlphaMode.Ignore,
                                            (uint)renderTargetBitmap.PixelWidth,
                                            (uint)renderTargetBitmap.PixelHeight,
                                            logicalDpi,
                                            logicalDpi,
                                            pixelBuffer.ToArray());
                                            await encoder.FlushAsync();
                                            //Load and draw the bitmap image in PDF
                                            PdfImage img = PdfImage.FromStream(stream.AsStream());
                                            if (img.Width > img.Height)
                                                document.PageSettings.Orientation = PdfPageOrientation.Portrait;
                                            else
                                                document.PageSettings.Orientation = PdfPageOrientation.Landscape;
                                            var section = document.Sections.Add();
                                            section.PageSettings.Size = new SizeF(img.Width, img.Height);
                                            PdfPage page = section.Pages.Add();
                                            page.Graphics.DrawImage(img, new RectangleF(0, 0, img.Width, img.Height));
                                        }

The only problem I'm facing now is how do I generate pdf pages according to the list view items. The code given above generates only 1 page every time so the list won't be visible fully.

Thanks in advance.


Solution

  • You could directly save the pdf file in the Windows.Storage.ApplicationData.Current.LocalFolder and use Windows.Data.Pdf Namespace APIs to show pdf in app, instead of calling Windows.System.Launcher.LaunchFileAsync(stFile) method to launch other app to open the pdf file.

    See UWP PDF document sample for more details.