Click here to Skip to main content
15,867,686 members
Articles / Mobile Apps / Android

Printing from a Xamarin.Android Application Developed with Visual Studio

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
15 Jul 2014CPOL5 min read 70.7K   59   14   25
Printing from a Xamarin.Android application developed with Visual Studio

Introduction

If you have ever had to develop a business application that targets the Android / mobile platform, you may have had to print from your application. Unlike printing from your desktop PC at home or work, you don't tend to connect your mobile device to a printer, so printing becomes a more difficult and problematic issue. In this article, I will describe how you can print from your Xamarin.Android application. Although the article was written with Xamarin.Android and C# in mind, the article lends itself just as easily to Eclipse and Java.

Background

This article uses a Star Micronics thermal printer. A thermal printer produces a digital image by heating thermal paper. The range from Star Micronics enable printing from web-based applications via HTTP requests. They are small, lightweight and have a very small memory footprint making them ideal for printing from mobile devices. They also support Bluetooth and so can be used wirelessly, making them even more appropriate for mobile device printing.

To use the printer in your application, you will firstly need to download the Star Micronics SDK. Once you have downloaded the SDK, you will need to add a reference the StarPrinter.dll assembly to your Visual Studio project.

A Note About Responsiveness

Before we get started, it is important to keep application responsiveness firmly in mind. The worst thing that can happen to your application is that it triggers an "Application Not Responding" (ANR) dialog. Within Android, the system guards against applications that are insufficiently responsive for a period of time by displaying a dialog stating your application has stopped responding.

Printing is exactly the sort of task that is likely to take relatively more time to complete than other tasks, and therefore a prime candidate for triggering an ANR dialog. You should therefore carefully consider how your application will perform printing tasks without triggering an ANR. You should not perform print tasks on the main UI thread, and instead consider another strategy such as implementing your print tasks using a Thread or AsyncTask instead.

A detailed discussion of Android application responsiveness is outside the scope of this article, but is certainly something that you need to consider when designing and implementing your application.

This article assumes familiarity with Android graphical elements such as Bitmap, Canvas, Paint, TextLayout and StaticLayout.

Adding Star Micronics to Your Application

To include Star Micronics print functionality in your application, you will need to add the following reference to your code:

C#
using Com.Starmicronics.Stario;

Finding Your Star Micronics Bluetooth Printer

Before your application can use the printer, it will firstly have to find it. Star printers can be located using either Bluetooth or TCP (LAN) protocols. Although in practice your application will use Bluetooth, I have added an example of how you can also search for your printer using TCP for the sake of completeness.

C#
new Thread(() =>
            {
                try
                {
                    IList<PortInfo> portList = StarIOPort.SearchPrinter("BT");
                    if (!portList.Any())
                    {
                        portList = StarIOPort.SearchPrinter("BT:DeviceName");
                    }
                    if (!portList.Any())
                    {
                        portList = StarIOPort.SearchPrinter("BT:MacAddress");
                    }
                    if (!portList.Any())
                    {
                        portList = StarIOPort.SearchPrinter("BT:Star Micronics");
                    }
                    if (!portList.Any())
                    {
                        portList = StarIOPort.SearchPrinter("TCP");
                    }

                    if (!portList.Any())
                    {
                        RunOnUiThread(() =>
                        ShowAlert("No printers found, connect on bluetooth first",
                            ToastLength.Long, Resource.Drawable.printer_cross));
                    }
                }
                catch (StarIOPortException ex)
                {
                    RunOnUiThread(() =>
                        Toast.MakeText(this, "Error finding printers: 
                        " + ex.Message, ToastLength.Long).Show());
                }
            }).Start();

To locate your Star printer, you need to invoke the SearchPrinter() function. This function returns a list of printers that it finds using the specified interface types to search. It will search for printers on LAN or paired Bluetooth devices. You will probably want to invoke this code at application startup.

Note how the printer search is executed on a Thread() so it does not occupy the main UI thread and possibly trigger an ANR. Note also that the mechanism invokes the SearchPrinter() function sequentially, specifying several different interface types. This gives your application a greater chance of successfully finding your printer.

C#
IList<PortInfo> portList = StarIOPort.SearchPrinter(string target);

Valid values for the target parameter are:

  • BT
  • BT:<DeviceName>
  • BT:<MacAddress>
  • TCP
  • TCP:<IPAddress>

Where <DeviceName>, <MacAddress> and <IPAddress> are the device name, MAC address and IP address respectively. These can be specified optionally.

Finding a Printer Port

Once you have found the printer, you need to open a connection to it. The function GetPort() is used to accomplish this. If successful, it will return a handle to the printer port.

C#
StarIOPort port = StarIOPort.GetPort(string portName, string portSettings, int timeoutMillisecs);
  • portName is the name of the printer found previously using the SearchPrinter() function
  • i.e. portList[0].PortName
  • portSettings is for either Ethernet or Bluetooth. For the purposes of this article, I will focus only on Bluetooth. If using a mini printer, then specify this with mini. This is required for all Bluetooth printers.
  • timeoutMillisecs is the timeout in milliseconds used when writing or reading from the port. 1000 milliseconds is 1 second.

Example usage:

C#
StarIOPort port = StarIOPort.GetPort(portList[0].PortName, "mini", 10000);

Memory Considerations

Before proceeding and showing how to actually print from your printer, it is important to note that Android devices are low powered, low memory devices. As such, they lack the corresponding CPU and memory horse power of other devices such as a laptop or desktop PC. Memory limitations were the single biggest issue I experienced when implementing print functionality for the Android platform.

If the text required to be printed contains images (such as logos, boxes around the text, signatures, etc.), then you may want to consider creating a bitmap image and printing this image. Due to the memory limitations of Android devices, you may want to consider how often you print out these images. Initially, my applications threw "Out of memory" errors as I was printing the entire image list at the end of the print routine. I resolved this by building up a list of bitmaps and printing these one at a time to the printer.

Here is the class declaration for the bitmap list that the application used:

C#
private static List<Bitmap> _imageList;

Here is the code to print your bitmap list:

C#
//this is declared at the class level
private static readonly List<byte> InitialisePrinter = new List<byte> { Esc, 0x40 };

private static void PrintImageCollection(StarIOPort port)
{
    try
    {
        if (_imageList != null && _imageList.Any())
        {
            foreach (var bmp in _imageList)
            {
                var command = new List<byte>();
                var starBitmapReceipt = new StarBitmap(bmp, true, 832);
                command.AddRange(InitialisePrinter);
                command.AddRange(starBitmapReceipt.GetImageEscPosDataForPrinting(true, true));
                port.WritePort(command.ToArray(), 0, command.Count);
            }
            _bitmap.Dispose();
            _imageList.Clear();
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

The key method to note in this code snippet is WritePort(byte[] writeBuffer, int offset, int size).

C#
WritePort(byte[] writeBuffer, int offset, int size);
  • writeBuffer is the array containing the data to be written
  • offset is the start offset for writing data
  • size is the amount of data that should be written to the pointer

Full Listing of a Print Class

In the example code below, some basic details are printed, i.e. name and address. Each piece of information to be printed is added to a bitmap image one-at-a-time. The bitmaps are added to an image list. The print routine iterates through this image list and prints the individual bitmaps.

C#
using System;
using System.Collections.Generic;

using Android.Graphics;
using Android.Text;

using Com.Starmicronics.Stario;

public class Printing
{
    public PrintForm()
    {
        private static Canvas _canvas;
        private static Paint _paint;
        private static Bitmap _bitmap;
        private static TextPaint _textPaint;
        
        private static readonly List<byte> InitialisePrinter = new List<byte> { Esc, 0x40 };
        private static List<Bitmap> _imageList;
        private static readonly Bitmap.Config BitmapConfig = Bitmap.Config.Argb4444;
    
        public static void PrintDetails(StarIOPort port)
        {
            try
            {
                //we will assume that the methods SearchPrinter() and GetPort() have already been
                //invoked at application startup and were successful

                PrintText("\nMy Details", Bold, Layout.Alignment.AlignNormal, 26);
                PrintText("Name", String.Format("{0}", 
                "Dominic Burford"), 400, false, false, rowHeight);
                PrintText("Addr1", String.Format("{0}", 
                "1 High Street"), 400, false, false, rowHeight);
                PrintText("Addr2", String.Format("{0}", 
                "London"), 400, false, false, rowHeight);
                PrintText("Postcode", String.Format("{0}", 
                "XX1 1YY"), 400, false, false, rowHeight);
                
                _imageList.Add(_bitmap.Copy(BitmapConfig, false));
                NewBitmapImage(port);

                //to print more information you will need to repeat the above steps i.e.
                
                //PrintText("\nMy Further Details", Bold, Layout.Alignment.AlignNormal, 26);
                //PrintText("Further Details 1", String.Format("{0}", 
                "details 1"), 400, false, false, rowHeight);
                //PrintText("Further Details 2", String.Format("{0}", 
                "details 2"), 400, false, false, rowHeight);
                                
                //_imageList.Add(_bitmap.Copy(BitmapConfig, false));
                //NewBitmapImage(port);

                //etc 
            }
            catch(Exception ex)
            {
                throw ex;
            }
        }

        private static void NewBitmapImage(StarIOPort port)
        {
            try
            {
                if (port != null)
                    PrintImageCollection(port);

                _bitmap.Dispose();
                _bitmap = null;
                _bitmap = Bitmap.CreateBitmap(832, 10, BitmapConfig);
                _canvas = new Canvas(_bitmap);
                _canvas.DrawColor(Color.White);
                _canvas.Translate(0, 0);
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

        private static void PrintImageCollection(StarIOPort port)
        {
            try
            {
                if (_imageList != null && _imageList.Any())
                {
                    foreach (var bmp in _imageList)
                    {
                        var command = new List<byte>();
                        var starBitmapReceipt = new StarBitmap(bmp, true, 832);
                        command.AddRange(InitialisePrinter);
                        command.AddRange(starBitmapReceipt.GetImageEscPosDataForPrinting(true, true));
                        port.WritePort(command.ToArray(), 0, command.Count);
                    }
                    _bitmap.Dispose();
                    _imageList.Clear();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private static void PrintText(string text, Typeface typeface, 
            Layout.Alignment alignment, int textSize, int width = 832)
        {
            try
            {
                using (var details = _bitmap.Copy(BitmapConfig, false))
                {
                    if (_paint == null)
                        _paint = new Paint();

                    _paint.SetTypeface(typeface);
                    _paint.TextSize = textSize;
                    _textPaint = new TextPaint(_paint);

                    var statlayout = new StaticLayout(text, _textPaint, width, 
                                     alignment, 1, 0, false);

                    _bitmap = Bitmap.CreateBitmap(832, statlayout.Height + 
                              details.Height + 30, BitmapConfig);

                    _canvas = new Canvas(_bitmap);
                    _canvas.DrawColor(Color.White);
                    _canvas.Translate(0, 0);
                    _canvas.DrawBitmap(details, 0, 0, _paint);
                    _canvas.Translate(0, details.Height);

                    statlayout.Draw(_canvas);

                    _canvas.Translate(0, -details.Height);

                    details.Dispose();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }
}

You would need to repeat the below steps for each section of information you wanted to print if you had other sections of information to print.

C#
//repeat this for each piece of information you want to print
PrintText("\nSome text", Bold, Layout.Alignment.AlignNormal, 26);

//once you have collected your print information,
//you then add the corresponding bitmaps to your image collection for printing
_imageList.Add(_bitmap.Copy(BitmapConfig, false));
NewBitmapImage(port);

Summary

Hopefully, this article has given you sufficient information to start printing from your own Xamarin.Android application. Feel free to leave a comment if you would like me to further elaborate on anything within this article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead Gold-Vision CRM
United Kingdom United Kingdom
I am a professional software engineer and technical architect with over twenty years commercial development experience with a strong focus on the design and development of web and mobile applications.

I have experience of architecting scalable, distributed, high volume web applications that are accessible from multiple devices due to their responsive web design, including architecting enterprise service-oriented solutions. I have also developed enterprise mobile applications using Xamarin and Telerik Platform.

I have extensive experience using .NET, ASP.NET, Windows and Web Services, WCF, SQL Server, LINQ and other Microsoft technologies. I am also familiar with HTML, Bootstrap, Javascript (inc. JQuery and Node.js), CSS, XML, JSON, Apache Cordova, KendoUI and many other web and mobile related technologies.

I am enthusiastic about Continuous Integration, Continuous Delivery and Application Life-cycle Management having configured such environments using CruiseControl.NET, TeamCity and Team Foundation Services. I enjoy working in Agile and Test Driven Development (TDD) environments.

Outside of work I have two beautiful daughters. I am also an avid cyclist who enjoys reading, listening to music and travelling.

Comments and Discussions

 
QuestionSource Code Pin
Tushar M P30-Aug-19 0:34
Tushar M P30-Aug-19 0:34 
QuestionWhere is StarPrint.dll? Pin
Vladimir Araque13-Nov-18 8:10
Vladimir Araque13-Nov-18 8:10 
QuestionDifferent Printer Pin
Member 1230686714-Jul-16 22:47
Member 1230686714-Jul-16 22:47 
QuestionException in GetPort method Pin
Bukky Ogunkunle24-Dec-15 10:40
Bukky Ogunkunle24-Dec-15 10:40 
AnswerRe: Exception in GetPort method Pin
Dominic Burford28-Dec-15 21:18
professionalDominic Burford28-Dec-15 21:18 
PortSettings - The settings for the port

Ethernet Settings: mini - Using a miniature printer
a - Set socket to keep alive
n - Use TCP no delay
Examples: "mini;an", "mini;a", "a", "n", ""

BlueTooth Settings: mini - Using a miniature printer (this is required for all BlueTooth printers)
f - Automatically flush after data is written to stream
u - use unsecure bluetooth connection. (Only for android api level 10 and up)

Examples: "mini;uf", "mini"
"There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult." - C.A.R. Hoare

Home | LinkedIn | Google+ | Twitter

GeneralRe: Exception in GetPort method Pin
Bukky Ogunkunle29-Dec-15 5:04
Bukky Ogunkunle29-Dec-15 5:04 
GeneralRe: Exception in GetPort method Pin
Dominic Burford30-Dec-15 0:04
professionalDominic Burford30-Dec-15 0:04 
GeneralRe: Exception in GetPort method Pin
Dominic Burford30-Dec-15 0:07
professionalDominic Burford30-Dec-15 0:07 
QuestionMisleading article Pin
kamnas6-Nov-14 2:03
kamnas6-Nov-14 2:03 
AnswerRe: Misleading article Pin
Dominic Burford6-Nov-14 3:23
professionalDominic Burford6-Nov-14 3:23 
GeneralRe: Misleading article Pin
kamnas6-Nov-14 4:21
kamnas6-Nov-14 4:21 
GeneralRe: Misleading article Pin
Dominic Burford6-Nov-14 4:40
professionalDominic Burford6-Nov-14 4:40 
GeneralRe: Misleading article Pin
kamnas6-Nov-14 4:50
kamnas6-Nov-14 4:50 
GeneralRe: Misleading article Pin
Dominic Burford6-Nov-14 5:20
professionalDominic Burford6-Nov-14 5:20 
GeneralRe: Misleading article Pin
kamnas6-Nov-14 6:10
kamnas6-Nov-14 6:10 
QuestionWhich SDK did you import? Pin
Member 8029214-Aug-14 9:11
Member 8029214-Aug-14 9:11 
AnswerRe: Which SDK did you import? Pin
Dominic Burford4-Aug-14 12:29
professionalDominic Burford4-Aug-14 12:29 
QuestionCould not find StarPrinter.dll in the SDK Pin
RamanaNV20-Jul-14 4:47
RamanaNV20-Jul-14 4:47 
AnswerRe: Could not find StarPrinter.dll in the SDK Pin
Dominic Burford20-Jul-14 9:36
professionalDominic Burford20-Jul-14 9:36 
GeneralRe: Could not find StarPrinter.dll in the SDK Pin
RamanaNV21-Jul-14 9:00
RamanaNV21-Jul-14 9:00 
GeneralRe: Could not find StarPrinter.dll in the SDK Pin
Dominic Burford21-Jul-14 11:22
professionalDominic Burford21-Jul-14 11:22 
GeneralRe: Could not find StarPrinter.dll in the SDK Pin
RamanaNV21-Jul-14 17:51
RamanaNV21-Jul-14 17:51 
GeneralRe: Could not find StarPrinter.dll in the SDK Pin
Member 1039193110-Oct-17 3:07
Member 1039193110-Oct-17 3:07 
QuestionFlexibility Pin
Ed(Member 1767792)16-Jul-14 10:05
Ed(Member 1767792)16-Jul-14 10:05 
AnswerRe: Flexibility Pin
Dominic Burford17-Jul-14 1:18
professionalDominic Burford17-Jul-14 1:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.