Introduction
This article explains a simple method to create a QR Code inside a standard control. The solution is a UserControl for future re-usability. There are many libraries that could help us in operations like that, but in this article I will use Google Charts () together with System.Net and System.IO namespaces.
Google Charts can be queried using POST request (see the details here). Therefore, we must:
a) query the remote server, with specific POST parameters (more on this later),
b) retrieve the server's response (a PNG image)
c) use it for our means, namely paint it on our control.
Creating UserControl
Open a new project in Visual Studio, then add a new User Control. I've set the BorderStyle property to Fixed3D and DoubleBuffered to True (to avoid flickering when the control refreshes itself).
The standard URL through that we will query is the following: http://chart.googleapis.com/chart?chs={WIDTH}x{HEIGHT}&cht=qr&chl={DATA} (parameters in brackets will be replaced with real parameters). chs will specify QR Code resolution (width x height), whereas chl will contain the data to be represented using the barcode. The parameter related to the barcode size could be easily deduced from our control's property (since a standard control has a width and a height), but we will create a new property to store a certain amount of text, representing the data that our QR Code will show.
In our UserControl, we start declaring the standard URI as a constant, our Data Property and an internal variable to store data in the local context:
- Const _GOOGLE_URL As String = "http://chart.googleapis.com/chart?chs={WIDTH}x{HEIGHT}&cht=qr&chl={DATA}"
- Dim _DATA As String = String.Empty
-
- Property Data As String
- Get
- Return _DATA
- End Get
- Set(value As String)
- _DATA = value
- End Set
- End Property
When we use our Control the Data Property will be available in both Code View and Design Mode:
Now that we can compose a URI with all the requested parameters, we must forge a Data Property to encode its content before the web request. This way, we can be sure that no special character will come to break our query. I've implemented a private function for this that, after calling, will return the URI with modified parameters, the least of which will be encoded (thanks to the WebUtility.UrlEncode function).
- Private Function getQRURI() As String
- Dim _qrAddr As String = _GOOGLE_URL.Replace("{WIDTH}", Me.Width.ToString).Replace("{HEIGHT}", Me.Height.ToString)
- _qrAddr = _qrAddr.Replace("{DATA}", WebUtility.UrlEncode(_DATA))
-
- Return _qrAddr
- End Function
We'll replace the first two tag parameters, {WIDTH} and {HEIGHT}, with our control's size, whereas the data's parameter will be the content of our Data Property, encoded (for details on WebUtility.UrlEncode, please refer here).
We're now ready to fetch our image from remote servers and use the returning buffer to paint our QR Code on our control. Since I want a QR Code to be painted at the standard OnPaint Event (taking advantage of the PaintEventArgs argument it exposes), I will override it, adding my code as in the following:
- Protected Overrides Sub OnPaint(e As PaintEventArgs)
- MyBase.OnPaint(e)
- If _DATA Is Nothing Then Exit Sub
-
- Dim client As New WebClient()
- Dim bytes() As Byte = client.DownloadData(getQRURI())
- client.Dispose()
-
- Dim memStream As New IO.MemoryStream(bytes)
- Dim bmp As Bitmap = Bitmap.FromStream(memStream)
- memStream.Dispose()
-
- e.Graphics.DrawImage(bmp, 0, 0)
- End Sub
MyBase.OnPaints calls standard paint operations. Next, we will check if there is data to query (exiting method otherwise), and proceeding the querying remote server with a new instance of WebClient. Using the call at DownloadData, to which we pass the result of our URI-formatting function, we'll fill an array of bytes that is the server response, in other words the PNG image representing our QR Code.
An image-type variable can be initialized by reading a Stream (like when we wish to open an image existing on our hard-drive, a stream to our local copy of it). Since we'll have our bytes in memory, we can declare a MemoryStream based on our array and use it as a Bitmap source. At this point, having a perfectly working bitmap, we can exploit the variable e that the OnPaint event provides access to us, to draw the image on our control, at [0;0] location.
Use QrBox in Windows Forms
After compiling our project, the QRBox will be available in the ToolBox, ready to be used on our Forms.
Using it is simple and sufficient to set the Data Property and call for a control's refresh.
The following example Form shows how it works: I've added a QrBox control to my Form together with a standard TextBox and Button.
When the user presses the "Make" Button, we'll read the TextBox content, passing it to the QrBox Data Property and invoking the Refresh() method. This is done to start the remote query towards Google Charts. The code on the Button click is shown below.
- Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
- QrBox1.Data = TextBox1.Text
- QrBox1.Refresh()
- End Sub
The code for QrBox UserControl is as follows:
- Imports System.Net
-
- Public Class QRBox
- Const _GOOGLE_URL As String = "http://chart.googleapis.com/chart?chs={WIDTH}x{HEIGHT}&cht=qr&chl={DATA}"
- Dim _DATA As String = String.Empty
-
- Property Data As String
- Get
- Return _DATA
- End Get
- Set(value As String)
- _DATA = value
- End Set
- End Property
-
- Private Function getQRURI() As String
- Dim _qrAddr As String = _GOOGLE_URL.Replace("{WIDTH}", Me.Width.ToString).Replace("{HEIGHT}", Me.Height.ToString)
- _qrAddr = _qrAddr.Replace("{DATA}", WebUtility.UrlEncode(_DATA))
-
- Return _qrAddr
- End Function
-
- Protected Overrides Sub OnPaint(e As PaintEventArgs)
- MyBase.OnPaint(e)
- If _DATA Is Nothing Then Exit Sub
-
- Dim client As New WebClient()
- Dim bytes() As Byte = client.DownloadData(getQRURI())
- client.Dispose()
-
- Dim memStream As New IO.MemoryStream(bytes)
- Dim bmp As Bitmap = Bitmap.FromStream(memStream)
- memStream.Dispose()
-
- e.Graphics.DrawImage(bmp, 0, 0)
- End Sub
-
- Public Sub New()
- InitializeComponent()
- End Sub
- End Class
Demo
The code discussed in the article could be downloaded from here.
I hope this will be useful for your projects.