How to print

amirvenusamirvenus USMember ✭✭✭
I have decided to migrate an existing WinForm application to XF.

It’s basically a very simple invoicing software for a shop and I was really excited about the performance when I started the project and managed to create iOS, Android, UWP, WPF and macOS all using the same code base (98% of the code is shared)

My project is a shared project (NOT PCL) and all I want to be able to do now is to print the invoices (not necessarily silently like POS)

I have to say that I am baffled that I can’t find a single working example after spending 12 hours on it...

I had been thinking using Shared Code would be giving me more flexibility to access native functions thanks to the #if #endif compiler directives but now I have minimised my expectations and will be the happiest man alive if I can at least print an invoice in the UWP/WPF only ...

I decided to create a PDF first to make things easier and consistent in terms of invoicing layout and even that was a big issue (using iTextSharp failed) so I ended up posting data to a WebService and then leaving PDF generation to the backend

Now, my question is how can I print that PDF using the device’s installed printer? (For now only in UWP/WPF and if I succeed I will be looking for other platforms)

Answers

  • BushbertBushbert Member ✭✭✭

    Each platform has its own implementation for printing. If you are talking UWP, then I guess you can use PrintManager and PrintDocument. I don't believe you can print pdf's using this method but i'm not sure you need to be doing that as you shifted the pdf generation to the backend.

    https://docs.microsoft.com/en-us/windows/uwp/devices-sensors/print-from-your-app

  • amirvenusamirvenus USMember ✭✭✭
    I tried using that guide, I don’t know if it’s only me but I find it very confusing and complicated.

    For example:

    await Windows.Graphics.Printing.PrintManager.ShowPrintUIAsync();


    It does not resolve and keeps asking me to add a dll (Windows.Foundation.Foundation...) but I don’t know how to add it and where it is
  • BushbertBushbert Member ✭✭✭
    edited July 2018

    Are you doing it in the UWP project? I achieve printing by using DependancyService which allows you to make one call in your shared code and that will automatically initiate the platform specific code. I'll try and knock you up an example.

  • amirvenusamirvenus USMember ✭✭✭
    > @Bushbert said:
    > Are you doing it in the UWP project? I achieve printing by using DependancyService which allows you to make one call in your shared code and that will automatically initiate the platform specific code. I'll try and knock you up an example.

    Could you please provide an example?

    Thanks!
  • BushbertBushbert Member ✭✭✭
    edited July 2018

    Here's an example solution which downloads and prints an image. I've only had time to implement the UWP solution for now. I will update shortly to include IOS and Android implantation.

    https://github.com/bushbert/XamarinPCLPrinting

    I should say it’s not a pcl project, it’s .net standard to avoid confusion.

  • amirvenusamirvenus USMember ✭✭✭

    Thanks for that!

    Could you please tell me if it is possible to implement printing in Xamarin.Forms.WPF part of the project as well?

  • BushbertBushbert Member ✭✭✭
    edited July 2018

    Yeah, its actually really simple. I've added a WPF project to the solution. I've also added IOS too.

  • amirvenusamirvenus USMember ✭✭✭

    Wow thanks a lot!

    Would it be possible to select the printer as well? The machine has two printers installed (one thermal printer for printing receipts and the other one for printing A4 invoices)

    Now I guess I will have to find a way for Xamarin.Forms.macOS.

    BTW, is it a must to use DependecyService as you did in your code? Isn't it that if we use Shared Project (in contrast to the PCL) we can access platform-specific code using compiler directives? I am a bit confused here

  • BushbertBushbert Member ✭✭✭
    edited July 2018

    The print dialogue should allow you to select available printers. What do you mean by machine? Are the printers connected directly to the network?

    Each platform supports certain types of printers by default i.e. In IOS the printer must support airprint.

    If the printer supports certain features and has an IP address, you could always connect directly to the printer using a socket connection in the native platform. So this may be something you need to look at.

    https://medium.com/@frankiefoo/xamarin-forms-how-to-print-receipts-using-ecs-pos-printers-488da61e3c84

    I only used DependancyService as I was using .net standard. If you are using shared code, you could something like the following.

    #if __IOS__
                var printInfo = UIKit.UIPrintInfo.PrintInfo;
                printInfo.Duplex = UIKit.UIPrintInfoDuplex.LongEdge;
                printInfo.OutputType = UIKit.UIPrintInfoOutputType.General;
                printInfo.JobName = "AppPrint";
                var printer = UIKit.UIPrintInteractionController.SharedPrintController;
                printer.PrintInfo = printInfo;
                printer.PrintingItem = Foundation.NSData.FromFile(pdfPath);
                printer.ShowsPageRange = true;
                printer.Present(true, (handler, completed, err) =>
                {
                    if (!completed && err != null)
                    {
                        System.Diagnostics.Debug.WriteLine("Printer Error");
                    }
                });
    #endif
    
  • amirvenusamirvenus USMember ✭✭✭
    Wow that’s great!

    Can I use the same technique for UWP and WPF so I won’t have to create Dependency?

    Although this project is being used for different platforms including iOS and Android, it will mostly be used on Desktop computers running Windows and macOS so I need to find a way to detect list of printers in UWP, macOS and WPF.
  • BushbertBushbert Member ✭✭✭

    I have never used Shared Projects nor intend to but I guess this code should work for WPF.

    //Check your conditional compilation symbol to configure what this is
    
    #if WINDOWS_WPF
    
    
                System.Windows.Controls.PrintDialog printDialog = new System.Windows.Controls.PrintDialog();
                if (printDialog.ShowDialog() == true)
                {
                var image = new System.Windows.Media.Imaging.BitmapImage();
                using (var ms = new System.IO.MemoryStream(imageContent))
                {
    
                    image.BeginInit();
                    image.CacheOption = System.Windows.Media.Imaging.BitmapCacheOption.OnLoad; // here
                    image.StreamSource = ms;
                    image.EndInit();
                }
    
    
    
                    var vis = new System.Windows.Media.DrawingVisual();
                    var dc = vis.RenderOpen();
                    dc.DrawImage(image, new System.Windows.Rect { Width = image.Width, Height = image.Height });
                    dc.Close();
    
                    var pdialog = new System.Windows.Controls.PrintDialog();
                    if (pdialog.ShowDialog() == true)
                    {
                        pdialog.PrintVisual(vis, "My Image");
                    }
    
                }
    
    
    
    
    #endif
    
  • amirvenusamirvenus USMember ✭✭✭

    Thanks a lot @Bushbert

    You have been a life saver!

    May I please ask if you could please share the same code I could use on #if UWP in a shared code project?

  • amirvenusamirvenus USMember ✭✭✭
    edited July 2018

    I am also struggling with imageContent .

    I have a url that outputs a png file. How can I convert that png file to imageContent?

    EDIT:

    I used this instead:

    await client.GetByteArrayAsync("https://abc.com/CreatePdf.php")

    for imageContent

    I guess I am making progress! Now my first problem is that the printed image is very very blurry and it can hardly be read. What am I doing wrong?

    The print dialog is also being displayed twice.

  • BushbertBushbert Member ✭✭✭
    edited July 2018

    Your image looks blurry to start with and its generating a jpg rather a png, which would obviously be better for quality.

  • amirvenusamirvenus USMember ✭✭✭
    edited July 2018

    And why is the print dialog being displayed twice?

    It's probably we are calling it twice:

    printDialog.ShowDialog() == true

    I guess I have to remove one

  • BushbertBushbert Member ✭✭✭

    Can you please share your print code so far and advise what platform is affected.

  • BushbertBushbert Member ✭✭✭

    Yes, that would do it :-)

  • amirvenusamirvenus USMember ✭✭✭

    @Bushbert said:
    Can you please share your print code so far and advise what platform is affected.

    I managed to successfully print in WPF! yay!

    All I need really is the same thing for UWP

    this is what I used for WPF:

    #if WPF
      System.Windows.Controls.PrintDialog printDialog = new System.Windows.Controls.PrintDialog();
         // if (printDialog.ShowDialog() == true)
                {
    
                 var image = new System.Windows.Media.Imaging.BitmapImage();
                using (var ms = new System.IO.MemoryStream(await client.GetByteArrayAsync("https://abc.com/CreatePdf.php")))
                {
    
                    image.BeginInit();
                    image.CacheOption = System.Windows.Media.Imaging.BitmapCacheOption.OnLoad; // here
                    image.StreamSource = ms;
                    image.EndInit();
                }
    
                          var vis = new System.Windows.Media.DrawingVisual();
                            var dc = vis.RenderOpen();
                            dc.DrawImage(image, new System.Windows.Rect { Width = image.Width, Height = image.Height });
                            dc.Close();
    
                            var pdialog = new System.Windows.Controls.PrintDialog();
                            if (pdialog.ShowDialog() == true)
                            {
                                pdialog.PrintVisual(vis, "My Image");
                            }
    
                }
    
    #endif
    
  • amirvenusamirvenus USMember ✭✭✭

    BTW, in Print_UWP.cs it was required to add:

    printmgr.PrintTaskRequested -= Printmgr_PrintTaskRequested;

    right after GC.Collection();

  • amirvenusamirvenus USMember ✭✭✭

    The lesson I learned was that it is much better to use DependencyInjection rather than compiler directives as it just produces much cleaner and easier-to-read code.

    Now, May I please ask how I can implement that IPrint interface for Xamarin.Forms.macOS?

  • amirvenusamirvenus USMember ✭✭✭

    @Bushbert said:
    Here's an example solution which downloads and prints an image. I've only had time to implement the UWP solution for now. I will update shortly to include IOS and Android implantation.

    https://github.com/bushbert/XamarinPCLPrinting

    I should say it’s not a pcl project, it’s .net standard to avoid confusion.

    Can we have the Android implementation as well please?

  • JoyPeterJoyPeter Member ✭✭

    @amirvenus said:

    @Bushbert said:
    Here's an example solution which downloads and prints an image. I've only had time to implement the UWP solution for now. I will update shortly to include IOS and Android implantation.

    https://github.com/bushbert/XamarinPCLPrinting

    I should say it’s not a pcl project, it’s .net standard to avoid confusion.

    Can we have the Android implementation as well please?

    I also need

  • BushbertBushbert Member ✭✭✭
     public void PrintForm(byte[] formData)
            {
                Stream inputStream = new MemoryStream(formData);
                string fileName = "form.pdf";
                if (inputStream.CanSeek)
                    //Reset the position of PDF document stream to be printed
                    inputStream.Position = 0;
                //Create a new file in the Personal folder with the given name
                string createdFilePath = System.IO.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), fileName);
                //Save the stream to the created file
                using (var dest = System.IO.File.OpenWrite(createdFilePath))
                    inputStream.CopyTo(dest);
                string filePath = createdFilePath;
                PrintManager printManager = (PrintManager)Forms.Context.GetSystemService(Context.PrintService);
                PrintDocumentAdapter pda = new CustomPrintDocumentAdapter(filePath);
                //Print with null PrintAttributes
                printManager.Print(fileName, pda, null);
            }
    
  • amirvenusamirvenus USMember ✭✭✭

    For anyone who is still looking for an alternative solution:

    I came across a project in which I had to print labels using a Zebra printer (that has ZebraNet Server added to it).

    The solution was to simply create a TCP Client over port 9100 to the ZebraNet server and send the ZPL as a string.
    It works on all Xamarin.Forms target without any DI.

  • BushbertBushbert Member ✭✭✭

    @amirvenus said:
    For anyone who is still looking for an alternative solution:

    I came across a project in which I had to print labels using a Zebra printer (that has ZebraNet Server added to it).

    The solution was to simply create a TCP Client over port 9100 to the ZebraNet server and send the ZPL as a string.
    It works on all Xamarin.Forms target without any DI.

    DI sounds easier.

  • amirvenusamirvenus USMember ✭✭✭
    @Bushbert

    Could you please add the macOS as well?

    Also, UWP bit causes the app to crash randomly with HRESULT exception. Would be nice to address that as well so it could be used as a starting point for hundreds of devs looking for a similar solution.

    Thanks
  • pearlshwetapearlshweta Member ✭✭

    Hi amirvenus,
    I am new to xamarin. So, i think you can understand my frustration too.
    I have already up and running ASP.NET website(without web services). It creates PDF on it on the button click.
    I have to get that PDF from it to the Xamarin app.

    I can see that you have already done this in this question. Can you please help me with it?
    I will really appreciate it.

    @amirvenus said:

    I have decided to migrate an existing WinForm application to XF.

    It’s basically a very simple invoicing software for a shop and I was really excited about the performance when I started the project and managed to create iOS, Android, UWP, WPF and macOS all using the same code base (98% of the code is shared)

    My project is a shared project (NOT PCL) and all I want to be able to do now is to print the invoices (not necessarily silently like POS)

    I have to say that I am baffled that I can’t find a single working example after spending 12 hours on it...

    I had been thinking using Shared Code would be giving me more flexibility to access native functions thanks to the #if #endif compiler directives but now I have minimised my expectations and will be the happiest man alive if I can at least print an invoice in the UWP/WPF only ...

    I decided to create a PDF first to make things easier and consistent in terms of invoicing layout and even that was a big issue (using iTextSharp failed) so I ended up posting data to a WebService and then leaving PDF generation to the backend

    Now, my question is how can I print that PDF using the device’s installed printer? (For now only in UWP/WPF and if I succeed I will be looking for other platforms)

  • amirvenusamirvenus USMember ✭✭✭

    @pearlshweta said:
    Hi amirvenus,
    I am new to xamarin. So, i think you can understand my frustration too.
    I have already up and running ASP.NET website(without web services). It creates PDF on it on the button click.
    I have to get that PDF from it to the Xamarin app.

    I can see that you have already done this in this question. Can you please help me with it?
    I will really appreciate it.

    @amirvenus said:

    I have decided to migrate an existing WinForm application to XF.

    It’s basically a very simple invoicing software for a shop and I was really excited about the performance when I started the project and managed to create iOS, Android, UWP, WPF and macOS all using the same code base (98% of the code is shared)

    My project is a shared project (NOT PCL) and all I want to be able to do now is to print the invoices (not necessarily silently like POS)

    I have to say that I am baffled that I can’t find a single working example after spending 12 hours on it...

    I had been thinking using Shared Code would be giving me more flexibility to access native functions thanks to the #if #endif compiler directives but now I have minimised my expectations and will be the happiest man alive if I can at least print an invoice in the UWP/WPF only ...

    I decided to create a PDF first to make things easier and consistent in terms of invoicing layout and even that was a big issue (using iTextSharp failed) so I ended up posting data to a WebService and then leaving PDF generation to the backend

    Now, my question is how can I print that PDF using the device’s installed printer? (For now only in UWP/WPF and if I succeed I will be looking for other platforms)

    I do that through two methods:

    1. Converting the PDF file to an image (like PNG/JPG) and then returning that URL and using it as an ImageSource in Xamarin.Forms.

    2. Consuming a PDF control like the one available with Syncfusion.

    More info: https://help.syncfusion.com/xamarin/sfpdfviewer/overview

  • pearlshwetapearlshweta Member ✭✭

    Hi amirvenus,
    Thanks a lot for your reply.
    I didn't want to use any third party tool, as they are usually not free.
    But i will try image hack. Thanks.

  • JohnHardmanJohnHardman GBUniversity mod

    @Bushbert - Unless I need more coffee, it looks like the latest version of the Android printing code in your repository is missing some code. It appears that the dependency service isn't registered and CustomPrintDocumentAdapter appears to be missing.

  • JohnHardmanJohnHardman GBUniversity mod

    I've created a printing NuGet, available at http://www.nuget.org/packages/Xam.Plugin.Printing with source at https://github.com/johnshardman/Printing . This includes support for Android, iOS and UWP (although UWP support for WebView and URLs needs some work, and I need to replace one deprecated bit from the iOS code).

    If anybody would like to extend this for any of WPF, MacOS or Tizen, please go ahead. I am not expecting to get to any of those platforms any time soon.

    @Bushbert - I did make use of some of your code, which I've acknowledged in both the ReadMe and in the source. Let me know if you need anything further included.

  • @JohnHardman I'm trying to use your NuGet on a UWP project targeting version 1803 with min version 'Fall Creators Update', but without success. Can you help me, telling if the NuGet works on these version and how to use the NuGet? I'm getting the 'NotImplementedException'. Thanks!

  • JohnHardmanJohnHardman GBUniversity mod

    @MatheusArruda said:
    @JohnHardman I'm trying to use your NuGet on a UWP project targeting version 1803 with min version 'Fall Creators Update', but without success. Can you help me, telling if the NuGet works on these version and how to use the NuGet? I'm getting the 'NotImplementedException'. Thanks!

    At the moment, the NuGet requires the min version to also be 1803. However, that's simply as I haven't got a physical test device running anything older than that currently, so couldn't test on anything before 1803 with a physical device.

    If you want to give it a try, tweak the uap version in this line

    <TargetFrameworks>netstandard2.0;Xamarin.iOS10;MonoAndroid81;uap10.0.18362</TargetFrameworks>
    

    in the Printing.Plugin.csproj to reflect the min version that you want. Rebuild the Printing solution. To use the output from this local build, uninstall the plugin that you have already added, then install the locally built one (on the "Manage Packages for Solution" page, use the settings button at the side of the Package source dropdown to reference the local one). If you're satisfied that it works, go ahead and submit the changed csproj file through GitHub.

    Hope that helps.

Sign In or Register to comment.