2015年5月30日土曜日

WPF 印刷で、用紙サイズを混在したり、A4 縦以外にするには

可能といえば可能ですが、不可避な不具合があるので期待しない方が無難でしょう…

問題点:
  • printDialog.PrintQueue.GetPrintCapabilities().PageMediaSizeCapability で列挙できる用紙サイズしか使用できません。
  • この用紙サイズ一覧はプリンタドライバーによって異なります。
  • ISOB4, JISB4 が列挙されない(使用できない)プリンターもあります。

回避案:

本題の件、参考: How to print multiple pages each with a different page size?

流れ:
  • まず、XpsDocumentWriter.CreateVisualsCollator を取り出します。
  • printDialog.PrintTicket を取得しておきます。以下 printTicket
  • BeginBatchWrite 呼び出し。
  • foreach PAGES →
  • printTicket.PageMediaSize を設定。
    • printDialog.PrintQueue.GetPrintCapabilities().PageMediaSizeCapability で取得した一覧から選択します。
    • new PageMediaSize(...) してもまったく効果を発揮しません。
  • printTicket.PageOrientation を設定。
    • 縦 = PageOrientation.Portrait
    • 横 = PageOrientation.Landscape
  • Write(Visual visual, PrintTicket printTicket) 呼び出し。
    • 印刷したいものが visual になります。
  • ← foreach PAGES
  • EndBatchWrite 呼び出し。


実証サンプル WpfMultiPageSizesSample をアップしました。
該当するコードはこの辺です。

WPF印刷での、マルチスレッド対応

[案1]

PrintDialogを他のスレッドから使う⇒NGでした。

InvalidOperationExceptionが発生します。

[案2]

PrintDialogのPrintQueueを、他のスレッド持って行きます⇒NGでした。

同じく、Writeの中でInvalidOperationExceptionが発生します。

[案3]

重い処理だけ、他のスレッドに持って行きました。

DispatcherObjectを継承するクラスは、基本的にスレッド境界を越えられないと思った方が良いかもしれません。

DrawingVisualもダメでした。Drawing.Freeze()を呼び出して凍った状態にしても、DrawingVisualがスレッドチェックに引っ掛かるので、撃沈。。。

2015年5月22日金曜日

WPF と PDF の用紙サイズ

PDF: pt = 1/72 inch
WPF: dip = 1/96 inch

A3
PDF 842 x 1191 [pt]
WPF 1122.51968503937 x 1587.40157480315 [dip]

PDF → WPF : pt * 1.333158770830606
WPF → PDF : dip * 0.750098204264871

2015年5月8日金曜日

BindingGroupとCollectionViewSourceとDataTableとナビゲーション

ふと気になりました。

BindingGroup.CommitEdit()がダメだと言っている状態で、移動するとどうなるのか。。。

結論は、部分的に捨てられ、部分的に保存されます。
  • BindingGroup.CommitEdit()は、Falseを返します。変更はすべて保留になり、ソースは元のままです。
  • そこで、無理やり移動すると:
  • 保存できるBindingは、個別にソースへ保存します。
  • 保存できないBindingは、ソースへ保存しません(できません)。元のままです。
  • 一貫性は、どうなる?

次の図をご覧ください。


DataContextは、CollectionViewSourceに。

CollectionViewSource.Sourceは、DataTableになっています。

Windowに、BindingGroupを使用しています。

Name : 文字列
Comment : 文字列
Age : Int16整数

XAML:

<Window x:Class="WpfApplication21.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="687">
    <FrameworkElement.BindingGroup>
        <BindingGroup />
    </FrameworkElement.BindingGroup>
    <Grid>
        <Label Content="Name:" HorizontalAlignment="Left" Margin="44,43,0,0" VerticalAlignment="Top"/>
        <TextBox HorizontalAlignment="Left" Height="23" Margin="110,47,0,0" TextWrapping="Wrap" Text="{Binding Name}" VerticalAlignment="Top" Width="120"/>
        <Label Content="Comment:" HorizontalAlignment="Left" Margin="21,93,0,0" VerticalAlignment="Top"/>
        <TextBox HorizontalAlignment="Left" Height="122" Margin="110,97,0,0" TextWrapping="Wrap" Text="{Binding Comment}" VerticalAlignment="Top" Width="292" AcceptsReturn="True"/>
        <Label Content="Age:" HorizontalAlignment="Left" Margin="55,237,0,0" VerticalAlignment="Top"/>
        <TextBox HorizontalAlignment="Left" Height="23" Margin="110,237,0,0" TextWrapping="Wrap" Text="{Binding Age,TargetNullValue=''}" VerticalAlignment="Top" Width="120"/>
        <Button Content="Back" HorizontalAlignment="Left" Margin="329,275,0,0" VerticalAlignment="Top" Width="75" Name="bBack" Click="bBack_Click" Height="34"/>
        <Button Content="Next" HorizontalAlignment="Left" Margin="409,275,0,0" VerticalAlignment="Top" Width="75" Name="bNext" Click="bNext_Click" Height="34"/>
        <Button Content="Save" HorizontalAlignment="Left" Margin="249,275,0,0" VerticalAlignment="Top" Width="75" Name="bSave" Click="bSave_Click" Height="34"/>
        <Button Content="New" HorizontalAlignment="Left" Margin="169,290,0,0" VerticalAlignment="Top" Width="75" Name="bNew" Click="bNew_Click" />
        <DataGrid HorizontalAlignment="Left" Margin="423,10,0,0" VerticalAlignment="Top" Height="209" Width="246" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" />

    </Grid>
</Window>

C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApplication21 {
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window {
        public MainWindow() {
            InitializeComponent();

            cvs.Source = dt;
            DataContext = cvs;
        }

        DataSet1.DataTable1DataTable dt = new DataSet1.DataTable1DataTable();
        CollectionViewSource cvs = new CollectionViewSource();

        private void bBack_Click(object sender, RoutedEventArgs e) {
            cvs.View.MoveCurrentToPrevious();
        }

        private void bNext_Click(object sender, RoutedEventArgs e) {
            cvs.View.MoveCurrentToNext();

        }

        private void bSave_Click(object sender, RoutedEventArgs e) {
            MessageBox.Show(BindingGroup.CommitEdit() + "");
        }

        private void bNew_Click(object sender, RoutedEventArgs e) {
            dt.Rows.Add();
        }
    }
}


操作:


FlowDocumentのページサイズと用紙サイズ

印刷の用紙サイズ:
PrintTicket.PageMediaSize

FlowDocumentのページサイズ:
FlowDocument.PageWidth
FlowDocument.PageHeight

設定例:
    // http://miteshsureja.blogspot.jp/2012/06/printing-flow-document-using-wpf.html
    FlowDocument fd = new FlowDocument();
    
    ...

    var prn = new PrintDialog();

    var t = prn.PrintTicket;
    t.PageMediaSize = new System.Printing.PageMediaSize(System.Printing.PageMediaSizeName.ISOA3, 1122.51968503937, 1587.40157480315);
    t.PageOrientation = System.Printing.PageOrientation.Landscape;

    switch (t.PageOrientation.Value) {
        default:
            fd.PageWidth = t.PageMediaSize.Width.Value;
            fd.PageHeight = t.PageMediaSize.Height.Value;
            break;
        case System.Printing.PageOrientation.Landscape:
        case System.Printing.PageOrientation.ReverseLandscape:
            fd.PageWidth = t.PageMediaSize.Height.Value;
            fd.PageHeight = t.PageMediaSize.Width.Value;
            break;
    }

    UtWpfPrint.Print3("図面目次", prn, fd);

2015年4月8日水曜日

WPF印刷用紙サイズとDesignWidth/DesignHeight

参考値です。
LocalPrintServer.DefaultPrintQueue.GetPrintCapabilities().PageMediaSizeCapabilityにて取得しました。

ISOA0 (3178.56 x 4493.76)
ISOA1 (2245.44 x 3178.56)
ISOA2 (1587.40157480315 x 2245.03937007874)
ISOA3 (1122.51968503937 x 1587.40157480315)
ISOA4 (793.700787401575 x 1122.51968503937)
ISOA5 (559.370078740158 x 793.700787401575)
ISOA6 (396.850393700787 x 559.370078740158)
ISOC5Envelope (612.283464566929 x 865.511811023622)
ISODLEnvelope (415.748031496063 x 831.496062992126)
ISOSRA3 (1209.52062992126 x 1700.79874015748)
JISB4 (971.338582677165 x 1375.74803149606)
JISB5 (687.874015748032 x 971.338582677165)
JapanChou3Envelope (453.543307086614 x 888.188976377953)
JapanChou4Envelope (340.157480314961 x 774.803149606299)
JapanDoubleHagakiPostcardRotated (559.370078740158 x 755.905511811024)
JapanHagakiPostcard (377.952755905512 x 559.370078740158)
JapanYou4Envelope (396.850393700787 x 888.188976377953)
NorthAmericaArchitectureBSheet (1152.72188976378 x 1727.28188976378)
NorthAmericaExecutive (696 x 1008)
NorthAmericaGermanLegalFanfold (816 x 1248)
NorthAmericaLegal (816 x 1344)
NorthAmericaLetter (816 x 1056)
NorthAmericaMonarchEnvelope (372 x 720)
NorthAmericaNumber10Envelope (396 x 912)
NorthAmericaNumber9Envelope (372 x 852)
NorthAmericaStatement (528 x 816)
NorthAmericaTabloid (1056 x 1632)
OtherMetricA4Plus (793.700787401575 x 1247.24409448819)


使い易いように、CSVでも出力しました。列: PageMediaSizeName,Width,Height

ISOA0,3178.56,4493.76
ISOA1,2245.44,3178.56
ISOA2,1587.40157480315,2245.03937007874
ISOA3,1122.51968503937,1587.40157480315
ISOA4,793.700787401575,1122.51968503937
ISOA5,559.370078740158,793.700787401575
ISOA6,396.850393700787,559.370078740158
ISOC5Envelope,612.283464566929,865.511811023622
ISODLEnvelope,415.748031496063,831.496062992126
ISOSRA3,1209.52062992126,1700.79874015748
JapanChou3Envelope,453.543307086614,888.188976377953
JapanChou4Envelope,340.157480314961,774.803149606299
JapanDoubleHagakiPostcardRotated,559.370078740158,755.905511811024
JapanHagakiPostcard,377.952755905512,559.370078740158
JapanYou4Envelope,396.850393700787,888.188976377953
JISB4,971.338582677165,1375.74803149606
JISB5,687.874015748032,971.338582677165
NorthAmericaArchitectureBSheet,1152.72188976378,1727.28188976378
NorthAmericaExecutive,696,1008
NorthAmericaGermanLegalFanfold,816,1248
NorthAmericaLegal,816,1344
NorthAmericaLetter,816,1056
NorthAmericaMonarchEnvelope,372,720
NorthAmericaNumber10Envelope,396,912
NorthAmericaNumber9Envelope,372,852
NorthAmericaStatement,528,816
NorthAmericaTabloid,1056,1632
OtherMetricA4Plus,793.700787401575,1247.24409448819

数値の単位がよく分かっていません。大体、96dpiでのピクセル数?という感じがするのです。

ILSpyで調査しました所、寸法(μm) ÷ 25400 × 96.0 で計算するようです。A4縦の幅の場合、210000/25400*96→793.7007874015748
(ReachFramework, MS.Internal.Printing.Configuration.UnitConverter.LengthValueFromMicronToDIP)

Note: 個人的に使う余白値 Margin="20"

2015年3月20日金曜日

WPFの印刷画面はCanvasかGridか

最終的には、Gridに統一しました。

Canvasですと、後で余白を追加する際に、融通が利かなかった為です。

Gridは意外に便利でした。
アンカーを付ける(例:HorizontalAlignment="Stretch")ことができるので、急な寸法変更にも楽に対応できます。

ちなみに、Cavnasを使うと、XAMLの書き方は、こんな感じです。

<Canvas>
  <!-- 表のセル、枠線付きで -->
  <Label Content="{Binding xxx}" 
         Padding="3,1,0,1" 
   BorderBrush="Black" BorderThickness="1" 
   Canvas.Left="53" Canvas.Top="56" />
  ...
</Canvas>

Canvas.LeftCanvas.Topが即値です。レイアウトが崩れにくくていい感じかもしれませんが、いざ横幅を縮めるとなると、再調整に掛かる重労働は必至です。。。
Labelを使うのは、枠線を付けられるからです。TextBlockでは、枠線が付きません。

問題点:
  • 箱のかたまりを、他の場所にコピーしたい場合。Canvas.LeftCanvas.Topの数値を後から手で触るのは大変そう。
  • 余白を付けずに用紙を設計してしまった場合。後でMargin="20"を追加すると、かなり幅が縮みます。

課題山盛りで、試行錯誤を致しました。

そこで、次の様にし、落ち着きました。

<Grid HorizontalAlignment="Left" VerticalAlignment="Top" Margin="4,98,0,0" Height="46" Width="389">
 <Grid.RowDefinitions>
  <RowDefinition Height="21*" /> <!-- ヘッダ -->
  <RowDefinition Height="25*" /> <!-- 金額部分 -->
 </Grid.RowDefinitions>
 <Grid.ColumnDefinitions>
  <ColumnDefinition Width="113*" /> <!-- 発行年月日 -->
  <ColumnDefinition Width="71*" /> <!-- 御得意先 -->
  <ColumnDefinition Width="54*" /> <!-- 担当 -->
  <ColumnDefinition Width="54*" /> <!-- 区分 -->
  <ColumnDefinition Width="97*" /> <!-- 伝票No. -->
 </Grid.ColumnDefinitions>
 <Rectangle Grid.RowSpan="2" Grid.ColumnSpan="5" RadiusY="5" RadiusX="5" Stroke="Black" />
 <Border BorderBrush="Black" BorderThickness="0,1,0,0" Grid.ColumnSpan="5" Grid.Row="1" />
 <Border BorderBrush="Black" BorderThickness="1,0,0,0" Grid.RowSpan="2" Grid.Column="1" />
 <Border BorderBrush="Black" BorderThickness="1,0,0,0" Grid.RowSpan="2" Grid.Column="2" />
 <Border BorderBrush="Black" BorderThickness="1,0,0,0" Grid.RowSpan="2" Grid.Column="3" />
 <Border BorderBrush="Black" BorderThickness="1,0,0,0" Grid.RowSpan="2" Grid.Column="4" />

 <Label Content="発 行 年 月 日" Padding="2" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Column="0" />
 <Label Content="御得意先" Padding="2" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Column="1" />
 <Label Content="担 当" Padding="2" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Column="2" />
 <Label Content="区 分" Padding="2" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Column="3" />
 <Label Content="伝 票 No." Padding="2" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Column="4" />

 <Label Content="{Binding [日付],StringFormat=yyyy.MM.dd}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Padding="1" Grid.Row="1" Grid.Column="0" />
 <Label Content="{Binding [得コード]}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Padding="1" Grid.Row="1" Grid.Column="1"  />
 <Label Content="{Binding [担当]}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Padding="1" Grid.Row="1" Grid.Column="2"  />
 <Label Content="{Binding [区分]}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Padding="1" Grid.Row="1" Grid.Column="3"  />
 <Label Content="{Binding [伝票番号]}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Padding="1" Grid.Row="1" Grid.Column="4"  />
</Grid>

プレビュー:


特に機密情報を含まないので、そのまま載せています。

構造は次のようなイメージです:

<Grid>
  Grid横の情報を定義
  Grid縦の情報を定義

  枠線エリア
  Rectangleで、丸い枠を描画します。Grid.ColumnSpanGrid.RowSpanを明示する必要が有ります。
  Borderで、上だけ線、左だけ線、の形で使って、格子を描画しています。

  ヘッダエリア
  Labelを使っています。Grid.ColumnGrid.Rowを使い、所定のセルへ格納します。Width/Heightを書く必要は全く有りません。

  ボディーエリア
  Binding中、[日付]の[]は、IDictionary辞書の中を見るので、付けます。
</Grid>

このように記述すると、すっきり致しませんか?

ColumnDefinition Width="113*"と書くと、比率ベースで寸法が決まります。親Gridの大きさが変わったとしても、レイアウトが極端に崩れる心配が有りません。

という事で、この形が目下での理想かな、と考えています。

バーコード印刷は、Imageではなく、Rectangleの集合で

バーコード印刷で、はまりました。

WriteableBitmapを使い、Imageにバーコードの画像をセットします。

それを印刷すれば、完璧なバーコードのエコシステムが出来上がるはずでした。

しかし、実機のバーコードリーダーでは読み取ることができません。

バーの並びというか、配列が変化し、変質しているのです!

試しに、PDFプリンターに印刷したらどうかと思い、試しました所、再現しました。変質しています。

結局、バーコードのビットマップを印刷するのはやめて、Canvasの中にRectangleを詰め込むようにしたら、いけました。

zxingを使ったサンプル。ご利用の際は、自己責任で:

    public class Code39Printer : Viewbox {
        public static DependencyProperty TextProperty = DependencyProperty.Register(
            "Text", typeof(String), typeof(Code39Printer), new PropertyMetadata(
                TextChangedCallback
                )
            );

        public String Text { get { return (String)GetValue(TextProperty); } set { SetValue(TextProperty, value); } }

        static void TextChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) {
            var self = (Code39Printer)d;

            var wr = new Code39Writer();
            bool[] bin = wr.encode(("" + e.NewValue + "").Trim('*').ToUpperInvariant());

            int cx = bin.Length;

            Canvas cv = new Canvas();
            int BW = 1;
            int BH = 20;
            int MH = 3;
            int MW = 10;
            cv.Height = MH + BH + MH;
            cv.Width = MW + cx * BW + MW;
            for (int x = 0; x < cx; x++) {
                if (bin[x]) {
                    Rectangle r = new Rectangle();
                    r.Fill = Brushes.Black;
                    Canvas.SetTop(r, MH);
                    Canvas.SetLeft(r, MW + BW * x);
                    r.Width = BW;
                    r.Height = BH;
                    cv.Children.Add(r);
                }
            }
            self.Child = cv;
        }
    }

使い方、例:

  <MY:Code39Printer Text="{Binding bacord}" xmlns:MY="clr-namespace:YOURNAMESPACE" />