Windowsアプリでラベル印刷のアプリを作りたいが、レイアウト作成が面倒。
と、億劫になっていたところ、XAMLで簡単に印刷レイアウトを作成し印刷できるみたいので
簡潔にまとめてみました。
WPFライブラリ を使用していますが WPFアプリ のみならず、Windows Forms でも使用可能です。
印刷サイズの変更は可能なので、A4で帳票作成なども出来ます。
設定方法も記載しますので、適宜変更してください。
ユーザーコントロールライブラリの作成
1、WPFアプリ もしくは Windowsフォームアプリ を作成
2、ユーザーコントロールライブラリを作成
[ソリューション で右クリック]→[追加]→[新しいプロジェクト]→[WPF ユーザーコントロールライブラリ]
3、プロジェクト名は『LabelPrint』とします。
4、名前の変更。『UserContol1.xml』を『Layout.xml』に変更
5、プロジェクト参照を追加
[本体アプリプロジェクト で右クリック]→[追加]→[プロジェクト参照]→[LabelPrint にチェック]→[OK]
ソースコード
レイアウトの作成 (Layout.xaml)
<?xml version="1.0" encoding="UTF-8"?> <UserControl x:Class="LabelPrint.Layout" x:Name="Layout1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:LabelPrint" mc:Ignorable="d" Height="151" Width="227"> <FixedPage Name="fixedPage"> <Canvas Background="White" Height="151" Width="227" VerticalAlignment="Center" HorizontalAlignment="Center"> <!--枠線--> <Border BorderBrush="#FF000000" BorderThickness="1" Background="#FFFFFFFF" Margin="0.2cm" Height="3.6cm" Width="5.6cm" CornerRadius="5" /> <StackPanel Orientation="Horizontal" Margin="0.35cm"> <!--文字列--> <StackPanel Width="1.8cm"> <TextBlock Margin="-3" Height="0.5cm" FontSize="0.3cm" TextAlignment="Right" Text="メーカー品番:"/> <TextBlock Margin="-3" Height="0.5cm" FontSize="0.3cm" TextAlignment="Right" Text="メーカー名:"/> <TextBlock Margin="-3" Height="0.5cm" FontSize="0.3cm" TextAlignment="Right" Text="保管場所:"/> <TextBlock Margin="-3" Height="0.5cm" FontSize="0.3cm" TextAlignment="Right" Text="内容量:"/> </StackPanel> <!--設定値--> <StackPanel Margin="0.2cm, 0" Width="3cm"> <TextBlock Margin="-3" Height="0.5cm" FontSize="0.3cm" TextAlignment="Left" Text="{Binding メーカー品番, ElementName=Layout1}"/> <TextBlock Margin="-3" Height="0.5cm" FontSize="0.3cm" TextAlignment="Left" Text="{Binding メーカー名, ElementName=Layout1}"/> <TextBlock Margin="-3" Height="0.5cm" FontSize="0.3cm" TextAlignment="Left" Text="{Binding 保管場所, ElementName=Layout1}"/> <TextBlock Margin="-3" Height="0.5cm" FontSize="0.3cm" TextAlignment="Left" Text="{Binding 内容量, ElementName=Layout1}"/> </StackPanel> </StackPanel> <!--会社名--> <TextBlock Margin="3.5cm, 3.4cm, 0, 0" Height="0.5cm" FontSize="0.2cm" TextAlignment="Left" Text="株式会社****"/> <!--Code39--> <StackPanel Margin="0.6cm, 3.1cm, 0, 0"> <Path Width="3cm" Height="0.4cm" x:Name="Code128Image" Data="" Fill="Black" Stretch="Fill" /> <TextBlock Height="0.25cm" x:Name="Code128Text" FontSize="0.15cm" TextAlignment="Center" Text="" /> </StackPanel> <!--QRCode--> <Path Margin="4.1cm, 1.7cm, 0, 0" Width="1.5cm" Height="1.5cm" x:Name="QRCodeImage" Data="" Fill="Black" Stretch="Fill" /> </Canvas> </FixedPage> </UserControl>
[NuGetパッケージ]よりZXing.Net(Ver.0.16.6以上)をインストールしてください。
※今回はVer.0.16.9を使用
using System.Printing; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Shapes; using System.Xml.Linq; using ZXing; using ZXing.QrCode; using ZXing.QrCode.Internal; using ZXing.Rendering; namespace LabelPrint { public partial class Layout : UserControl { private static readonly DependencyProperty TextProperty1 = DependencyProperty.Register(nameof(メーカー品番), typeof(string), typeof(Layout)); private static readonly DependencyProperty TextProperty2 = DependencyProperty.Register(nameof(メーカー名), typeof(string), typeof(Layout)); private static readonly DependencyProperty TextProperty3 = DependencyProperty.Register(nameof(保管場所), typeof(string), typeof(Layout)); private static readonly DependencyProperty TextProperty4 = DependencyProperty.Register(nameof(内容量), typeof(string), typeof(Layout)); private string? _QRCode; private string? _Code128; public Layout() { InitializeComponent(); } public string メーカー品番 { get => (string)GetValue(TextProperty1); set => SetValue(TextProperty1, value); } public string メーカー名 { get => (string)GetValue(TextProperty2); set => SetValue(TextProperty2, value); } public string 保管場所 { get => (string)GetValue(TextProperty3); set => SetValue(TextProperty3, value); } public string 内容量 { get => (string)GetValue(TextProperty4); set => SetValue(TextProperty4, value); } public string QRCode { get => this._QRCode ?? ""; set { this._QRCode = value; CreateBarcode(QRCodeImage, this._QRCode, BarcodeFormat.QR_CODE); } } public string Code128 { get => this._Code128 ?? ""; set { this._Code128 = value; CreateBarcode(Code128Image, this._Code128, BarcodeFormat.CODE_128); } } private void CreateBarcode(Path path, string text, BarcodeFormat barcodeFormat) { string data = "F1 "; foreach ( XElement el in XElement.Parse( new BarcodeWriter<SvgRenderer.SvgImage> { Format = barcodeFormat, Options = new QrCodeEncodingOptions { ErrorCorrection = ErrorCorrectionLevel.M, CharacterSet = "UTF-8", NoPadding = true, }, Renderer = new SvgRenderer(), }.Write(text).Content ).Elements()) { if (el.Name.LocalName == "rect") { string x = el.Attribute("x").Value; string y = el.Attribute("y").Value; string width = el.Attribute("width").Value; string height = el.Attribute("height").Value; //data += $"M{x},{y} v{height} h{width} v-{height} Z"; data += $"M{x},{y} h{width} v{height} h-{width} Z"; path.Data = (Geometry?)new GeometryConverter().ConvertFromInvariantString(data); } else if (el.Name.LocalName == "text") { if (path == Code128Image) Code128Text.Text = el.Value; } } } public void Print(string printer, int qty) { new PrintDialog { PrintTicket = new PrintTicket { PageMediaSize = new PageMediaSize(this.Width, this.Height), CopyCount = qty, PageResolution = new PageResolution(96, 96), PageBorderless = PageBorderless.None, PageScalingFactor = 1, }, PrintQueue = new PrintQueue(new PrintServer(), printer), }.PrintVisual(fixedPage, "LabelPrint"); } } }
最後に リビルド を行ってください。
印刷メソッドの読み出し方法 (使用方法)
new LabelPrint.Layout { メーカー品番 = "HOGE-001", メーカー名 = "××株式会社", 保管場所 = "A棚", 内容量 = "200g", Code128 = "HOGE-001", QRCode = "HOGE-001", }.Print("Microsoft Print to PDF", 1);
プロパティに 印刷内容 を入力し
.Printメソッド で プリンタ名 と 部数 を入力するだけで使用できます。
解説
レイアウト (Layout.xaml)
<UserControl ... x:Name="Layout1" Height="151" Width="227">
x:Name="Layout1" はデータバインディングで必要な名前です。
Height="151" Width="227" でサイズの設定をしています。ここではpxを使用してください。
WPFのユーザーコントロールは96dpiで設定されているので、サイズを変更したい場合は mm ⇒ px 変換を行ってください。
A4サイズの場合は Height="1123" Width="794"
今回は高さ40mm×幅60mm設定です。
<FixedPage Name="fixedPage"> ... </FixedPage>
印刷ページを作成する際に必須です。
FixedPageタグ 内が1ページとなります。
Name は印刷メソッドで使用します。
<!--文字列-->
<StackPanel Width="1.8cm">
<TextBlock Margin="-3" Height="0.5cm" FontSize="0.3cm" TextAlignment="Right" Text="メーカー品番:"/>
...
</StackPanel>
<!--設定値-->
<StackPanel Margin="0.2cm, 0" Width="3cm"> <TextBlock Margin="-3" Height="0.5cm" FontSize="0.3cm" TextAlignment="Left" Text="{Binding メーカー品番, ElementName=Layout1}"/>
...
</StackPanel>
StackPanel や Canvas などを使用し、レイアウトを整えていきます。
Margin や Height、Width 等の寸法設定はここでは『cm』での調整が可能です。
Text="{Binding メーカー品番, ElementName=Layout1}" でデータバインディングを行ってます。
Binding ***は任意の名前、ElementNameは先ほど設定したx:Nameを書きます。
<!--Code39--> <StackPanel Margin="0.6cm, 3.1cm, 0, 0"> <Path Width="3cm" Height="0.4cm" x:Name="Code128Image" Data="" Fill="Black" Stretch="Fill" /> <TextBlock Height="0.25cm" x:Name="Code128Text" FontSize="0.15cm" TextAlignment="Center" Text="" /> </StackPanel>
バーコード描画用の Path を用意します。
詳細はこちら
【C# .NET8.0】XAMLにQRコードをSVGで表示【ZXing.NET】 - 製造業 スマートDX推進課 スマート工場DIY日記
private static readonly DependencyProperty TextProperty1 = DependencyProperty.Register(nameof(メーカー品番), typeof(string), typeof(Layout));
public string メーカー品番 { get => (string)GetValue(TextProperty1); set => SetValue(TextProperty1, value); }
...
private string? _QRCode;
public string QRCode { get => this._QRCode ?? ""; set { this._QRCode = value; CreateBarcode(QRCodeImage, this._QRCode, BarcodeFormat.QR_CODE); } }
データバインディング用のプロパティを宣言します。
nameof(****) には<!--設定値-->で設定した Binding **** を入力
QRコードの set にはバーコード作成メソッドを書き、プロパティセットと同時に描画します。
private void CreateBarcode(Path path, string text, BarcodeFormat barcodeFormat) { string data = "F1 "; foreach ( XElement el in XElement.Parse( new BarcodeWriter<SvgRenderer.SvgImage> { Format = barcodeFormat, Options = new QrCodeEncodingOptions { ErrorCorrection = ErrorCorrectionLevel.M, CharacterSet = "UTF-8", NoPadding = true, }, Renderer = new SvgRenderer(), }.Write(text).Content ).Elements()) { if (el.Name.LocalName == "rect") { string x = el.Attribute("x").Value; string y = el.Attribute("y").Value; string width = el.Attribute("width").Value; string height = el.Attribute("height").Value; //data += $"M{x},{y} v{height} h{width} v-{height} Z"; data += $"M{x},{y} h{width} v{height} h-{width} Z"; path.Data = (Geometry?)new GeometryConverter().ConvertFromInvariantString(data); } else if (el.Name.LocalName == "text") { if (path == Code128Image) Code128Text.Text = el.Value; } } }
バーコード描画用のメソッドです。QRコードとCODE128共通で使用しています。
詳細はこちら
【C# .NET8.0】XAMLにQRコードをSVGで表示【ZXing.NET】 - 製造業 スマートDX推進課 スマート工場DIY日記
public void Print(string printer, int qty) { new PrintDialog { PrintTicket = new PrintTicket { PageMediaSize = new PageMediaSize(this.Width, this.Height), CopyCount = qty, PageResolution = new PageResolution(96, 96), PageBorderless = PageBorderless.None, PageScalingFactor = 1, }, PrintQueue = new PrintQueue(new PrintServer(), printer), }.PrintVisual(fixedPage, "LabelPrint"); }
印刷メソッドです。PrintDialogクラスを使用しています。
PageMediaSize 印刷サイズ設定 (Layout.xaml の Height、Widthを直接参照)
PageResolution プリンタへ送る解像度 (96×96dpi固定です)
印刷サイズ と 解像度 をプリンタに送っておけば、プリンタ側で解像度処理します。
文字やバーコードは全て Path なので、拡大縮小しても文字がぼやける事がないです。
.PrintVisualメソッド 印刷するFixedPage と キュー名 を指定しています。
最後に リビルド を行ってください。
あとは、元アプリから 印刷メソッドの読み出し方法 を使用すれば簡単に印刷が出来ます。
あとがき
部材管理に使用するラベル印刷を内製化したいと依頼があり
本来であればPDFを作って、位置をpx単位で調整して、レイアウト確認して、修正して...という流れで超面倒くさい。
しかし、調べてみると WPFユーザーコントロール を直接印刷できるらしい。
これならレイアウトもすぐに確認出来て、位置もcm単位で調整できるので作業がかなり楽になった。
だが、問題は WPF という事だ。
WPF の VMMV がどうも理解できず、どうにか Windows Formsアプリ から出来ないかと考えていたら
Windows Formsアプリ でも普通にWPFライブラリが使用できてしまった。
印刷アプリは Windows Forms で、印刷レイアウトは WPF でという形で作ってみました。