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?

Sign In or Register to comment.