Recently I had to render barcodes in a WPF client and had to be able to print them as well.
On this project we were using DevExpress controls, but the Barcode control (at least till version 13.1) was only available for reporting. There was no ‘regular’ WPF control to display barcodes on screen.
I first tried using a Barcode font. I found the website of John T Barton with lots of info about barcodes. Downloaded the latest version of the EAN128 font from the website of Grand Zebu, but unfortunately I ran into problems with the first barcodes I tried (“MSN12345” and “MSN00000”). One of the characters created by the code from the site (to calculate checksum and some compression) wasn’t supported by the font.
After some extra research on the internet I quickly came across the Zen Barcode Rendering framework on Codeplex. This free framework looked like the answer to my problems/requirements.
Unfortunately, it targeted ASP.NET, SSRS and WinForms, but no WPF. So …. no luck there. But, as the framework is an open source project, I downloaded the code and started investigating if it would be possible to make some modifications so it could be used by WPF clients.
After some investigations and trials I gave it a go and started reworking it for WPF:
- Changed measurements (sizes and positions) from int to double
- Changed drawing methods to use a DrawingContext instead of a Graphics object
- Removed the 2D stuff (QR and PDF417), including the corresponding resources, which reduced the size of the DLL from 12MB to less than 50KB!
- I added some extra code to render the barcode in the center of the drawing area (at least as far as the HorizontalAlignment is concerned)
- and maybe some other changes I don’t remember any more…
Anyway, after an hour or two, I was able to render barcodes in WPF, display them on screen and print them.
The technique I used was:
- Create a class that inherited from FrameworkElement.
- Override OnRender
- Catch the DrawingContext, determine the drawingSize (ActualWidth, ActualHeight) of the control
- (Optionally do some calculations for position and size of the barcode)
- Call a method from the Zen.Barcode.Core dll to render the Barcode, and voila: the code is displayed on your WPF-window.
- For printing I used a DrawingVisual-object which returns a DrawingContext when you call RenderOpen() on it.
- This DrawingContext can then be used to draw on using the same method.
- The DrawingVisual-object can be sent to a PrintDialog-object (you can even omit the ShowDialog call, set the PrintTicket and PrintQueue info yourself, and do a silent print without dialog)
This is the code for such a barcode control:
- public class Ean128BarcodeControl: FrameworkElement
- {
- private static readonly BarcodeDraw BarcodeDraw = BarcodeDrawFactory.Code128WithChecksum;
- static Ean128BarcodeControl()
- {
- ClipToBoundsProperty.OverrideMetadata(typeof(Ean128BarcodeControl), new FrameworkPropertyMetadata(true));
- }
- public string Barcode
- {
- get { return (string)GetValue(BarcodeProperty); }
- set { SetValue(BarcodeProperty, value); }
- }
- // Using a DependencyProperty as the backing store for Barcode. This enables animation, styling, binding, etc…
- public static readonly DependencyProperty BarcodeProperty =
- DependencyProperty.Register(“Barcode”, typeof(string), typeof(Ean128BarcodeControl), new PropertyMetadata(null, BarcodePropertyChangedCallback));
- private static void BarcodePropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
- {
- var ctl = sender as Ean128BarcodeControl;
- if (ctl == null) return;
- ctl.InvalidateVisual();
- }
- protected override void OnRender(DrawingContext drawingContext)
- {
- var size = new Rect(0, 0, ActualWidth, ActualHeight);
- drawingContext.DrawRectangle(Brushes.White, null, size);
- if (!string.IsNullOrEmpty(Barcode))
- {
- BarcodeDraw.Draw(drawingContext, Barcode, newBarcodeMetrics1d(2, 4, 60), size);
- }
- }
- }
Just drop it on your WPF Window, set or bind the Barcode property, and voila, you have yourself a working WPF barcode control!
To print it you can use the same code to draw:
- private void PrintButton_Click(object sender, RoutedEventArgs e)
- {
- var printDialog = new PrintDialog();
- var printer = new PrintServer().GetPrintQueues().ToList().FirstOrDefault(x => x.FullName.Contains(“XPS”));
- if (printer == null)
- {
- MessageBox.Show(“No XPS printer found”);
- return;
- }
- printDialog.PrintQueue = printer;
- printDialog.PrintTicket.PageOrientation = PageOrientation.Portrait;
- printDialog.PrintTicket.PageMediaSize = new PageMediaSize(PageMediaSizeName.NorthAmericaLetter);
- printDialog.PrintTicket.PageBorderless = PageBorderless.Borderless;
- var drawingVisual = new DrawingVisual();
- var drawingContext = drawingVisual.RenderOpen();
- if (!string.IsNullOrEmpty(InputText.Text))
- {
- var size = new Rect(0, 0, BarcodeControl.ActualWidth, BarcodeControl.ActualHeight);
- BarcodeDraw.Draw(drawingContext, InputText.Text, new BarcodeMetrics1d(2, 4, 60), size);
- }
- drawingContext.Close();
- printDialog.PrintVisual(drawingVisual, “Print”);
- }
Check the code in this Sample project