Introduction
In this tip, I'll cover a simple method to create a QR Code inside a standard control, wrapping up everything inside a UserControl
for future reference. There are many libraries which could help us in operations like that, but in this post, I will use Google Charts (https://developers.google.com/chart/), together with System.Net
and System.IO
namespaces.
Background
Google Charts can be queried though POST request (see here for details: https://developers.google.com/chart/image/docs/post_requests?csw=1), so we must:
- query remote server, with particular POST parameters (more on this later)
- retrieve the server's response (a PNG image)
- use it for our means, namely paint it on our control
So, open a new project in Visual Studio, then add a new User Control. I've set BorderStyle
property to Fixed3D
, and DoubleBuffered
to True
(in order to avoid flickering when the control refreshes itself).
Using the Code
The standard URL 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), while chl
will contain the data to be represented through barcode. The parameter related to the barcode size could be easily deduced from our control's property (since a standard control naturally have a width and a height), but we will create a new property to store a certain amount of text, representing the data 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=_
[This link is external to TechNet Wiki. It will open in a new window.] _
{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 will use our control, 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 Data
Property in order to encode its content before web request. This way, we can be sure no special character will come to break our query. I've implemented a private
function for this. Calling on it return an URI with replaced 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, while 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 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:
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 on standard paint operations. Next, we check if there is data to query (exiting method otherwise), and proceeding querying remote server with a new instance of WebClient
. Through the call at DownloadData
, to which we pass the result of our URI-formatting function, we'll fill an array of bytes, which is the server response, i.e. the PNG image representing our QR Code.
An image-type variable can be initialized through 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, which the OnPaint
event give access to us, to draw the image on our control, at [0;0]
location.
After compiling our project, the QRBox
will be available in the ToolBox
, ready to be used on our Forms.
Using it is simple, it's sufficient to set its Data
Property, and call for a control's refresh.
The following example Form shows how it works: I've added to my Form a QrBox
control, together with a standard TextBox
ad 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, in order to start the remote query towards Google Charts. The code for the Button's click will be simply like that:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
QrBox1.Data = TextBox1.Text
QrBox1.Refresh()
End Sub
UserControl's Complete Source Code
The complete 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
I hope this could be useful for your projects.
Happy coding, and good luck with your work!
History
- 2015-01-03: First release for CodeProject