24
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

WPFで簡単レポート作成

Last updated at Posted at 2021-12-19

<おことわり>

はじめに

この記事は、Visual Basic Advent Calendar 2021に投稿した『Visual Basic で簡単レポート作成』のC#版です。若干加筆修正しています。

また、以下の続編もありますので、あわせてご覧ください。
WPFで簡単レポート作成(2)複数ページのレポート
WPFで簡単レポート作成(3)ページ指定印刷

帳票は無くならない

請求書、依頼書、出荷明細書など、企業で使う帳票は、ペーパーレスが徐々に浸透しつつある今でも無くなる気配がない。ペーパーレスになったところで、見たいものを簡潔にまとめ上げるレイアウト情報としての帳票は、まだまだ存在し続けるだろう。
そんな帳票を作るのに、AccessのレポートやRDLCなどを使っている人も多いと思うが、

WPF + C# でやってみたら意外に楽しかった:laughing:
FixedDocumentを使う

WPFでレポート作成するのなら、FixedDocumentクラスがお勧めだ。
レポートの大枠が、以下のようにでき上がっている。
大枠.png
この枠の中に、Xamlで帳票を組み立てていく。

Xamlで帳票を組み立てる

せっかくなので、最新のVisual Studio 2022を使い、.NET6で作ってみよう。
まずはWPFプロジェクトを作成する。
CS_WPFプロジェクト作成.png
次にUserControlを追加。
CS_UserControl追加.png
追加したUserControlにXamlをゴリゴリ書いていく。Xamlデザイナーに慣れていない人は、デザインビューでの編集はしないことだ。思い通りにコントロールを配置できず、イライラが増して嫌になる。デザインビューは仕上がりを視覚的に確認するものと割り切った方がいい。
XamlビューでXamlのテキストを直接書き込んでいく。書くと言っても大部分はコピぺで済む。
まずはGridCanvasに変える。Canvasは、レポート内の文字や罫線が固定されるので帳票作成に向いている。Canvas内はセンチメートルでサイズを書ける。
以下、単純な帳票を書いてみた。

ShukkaUserControl.xaml
<UserControl x:Class="ShukkaReport.ShukkaUserControl"
    -- 省略 --
    DataContext="{StaticResource ShukkaVM}"
    Height="1122.51" Width="793.7">
    <UserControl.Resources>
        <Style x:Key="Line1" TargetType="Line">
            <Setter Property="StrokeThickness" Value="1"/>
            <Setter Property="Stroke" Value="Black"/>
        </Style>
    </UserControl.Resources>
    <Canvas Background="White">
        <!--横線-->
        <Line X1="6cm" X2="15cm" Y1="3.5cm" Y2="3.5cm" Style="{StaticResource Line1}"/>
        <Line X1="1cm" X2="20cm" Y1="5cm" Y2="5cm" Style="{StaticResource Line1}"/>
        <Line X1="1cm" X2="20cm" Y1="7cm" Y2="7cm" Style="{StaticResource Line1}"/>
        <Line X1="1cm" X2="20cm" Y1="15cm" Y2="15cm" Style="{StaticResource Line1}"/>
        <Line X1="1cm" X2="20cm" Y1="17cm" Y2="17cm" Style="{StaticResource Line1}"/>
        <Line X1="1cm" X2="20cm" Y1="19cm" Y2="19cm" Style="{StaticResource Line1}"/>
        <!--縦線-->
        <Line X1="1cm" X2="1cm" Y1="5cm" Y2="19cm" Style="{StaticResource Line1}"/>
        <Line X1="10.5cm" X2="10.5cm" Y1="7cm" Y2="19cm" Style="{StaticResource Line1}"/>
        <Line X1="20cm" X2="20cm" Y1="5cm" Y2="19cm" Style="{StaticResource Line1}"/>
        <!--文字列-->
        <TextBlock Text="出 荷 指 図 書" Margin="6.65cm,2cm" Width="8cm" Height="1.5cm" FontSize="46"/>
        <TextBlock Text="出荷指図№" Margin="1.25cm,5.25cm" Height="1.5cm" FontSize="36" FontWeight="Bold"/>
        <TextBlock Text="納品先" Margin="1.25cm,7.25cm" Height="1cm" FontSize="26"/>
        <TextBlock Text="住所" Margin="10.75cm,7.25cm" Height="1cm" FontSize="26"/>
        <TextBlock Text="出荷日" Margin="1.25cm,15.25cm" Height="1cm" FontSize="26"/>
        <TextBlock Text="納品日" Margin="1.25cm,17.25cm" Height="1cm" FontSize="26"/>
        <TextBlock Text="個数" Margin="10.75cm,15.25cm" Height="1cm" FontSize="26"/>
        <TextBlock Text="運送便" Margin="10.75cm,17.25cm" Height="1cm" FontSize="26"/>
        <!--設定値-->
        <TextBlock Text="{Binding 出荷指図NO}" Margin="7cm,5.25cm" Height="1.5cm" Width="10cm" FontSize="36"/>
        <TextBlock Text="{Binding 納品先}" Margin="1.25cm,9cm" Height="5cm" FontSize="20"/>
        <TextBlock Text="{Binding 住所}" Margin="10.75cm,9cm" Height="5cm" FontSize="20"/>
        <TextBlock Text="{Binding 出荷日}" Margin="4cm,15.5cm" Height="1cm" FontSize="20"/>
        <TextBlock Text="{Binding 納品日}" Margin="4cm,17.5cm" Height="1cm" FontSize="20"/>
        <TextBlock Text="{Binding 個数}" Margin="13cm,15.5cm" Height="1cm" FontSize="20"/>
        <TextBlock Text="{Binding 運送便}" Margin="14cm,17.5cm" Height="1cm" FontSize="20"/>
    </Canvas>
</UserControl>

クラス名に名前空間が付加されるのは、VBと異なる。

ページサイズはA4にしている。ここはピクセルでの設定となる。
出荷指図書.png
このレベルならAccessのレポートで十分作れるのだが、Xamlなら更に複雑なことができるのは想像がつくだろう。
コントロールをマウスでドラッグして線が斜めになったとか、部分的に表示位置をずらしたくなったとか、Accessレポートなどではうんざりする修正作業も、Xamlならストレス無く、各コントロールの座標数字を書き換えるだけで済む。

ここで、3行目の、

DataContext="{StaticResource ShukkaVM}

は、本来ならViewModelを作成してから書く。(:warning:これを書かない方法を後述しています)

ViewModelの作成

以下は追加したユーザーコントロールのViewModelである。コードビハインドは一切汚さない。ふざけたコードだが、わかりやすさ重視だ:grin:

ShukkaViewModel.cs
namespace ShukkaReport.ViewModels
{
    public class ShukkaViewModel
    {
        public string 出荷指図NO { get; }
        public string 納品先 { get; }
        public string 住所 { get; }
        public string 出荷日 { get; }
        public string 納品日 { get; }
        public string 個数 { get; }
        public string 運送便 { get; }
        public ShukkaViewModel()
        {
            this.出荷指図NO = "SK20211224-001";
            this.納品先 = "株式会社ほげ商事";
            this.住所 = "東京都中央区〇〇町〇丁目-〇-〇\nレポートビル1階";
            this.出荷日 = "12月24日";
            this.納品日 = "12月25日";
            this.個数 = "10個";
            this.運送便 = "佐山運輸";
        }
    }
}

以下はウインドのViewModelである。こちらもコードビハインドは一切汚さない。

MainWindowViewModel.cs
using System.Windows.Documents;

namespace ShukkaReport.ViewModels
{
    public class MainWindowViewModel
    {
        public FixedDocument ReportViewer { get; }
        public MainWindowViewModel()
        {
            var report = new ShukkaUserControl();
            var page = new FixedPage() { Width = 793.7, Height = 1122.51 };
            var pageContent = new PageContent();
            var doc = new FixedDocument();
            page.Children.Add(report);
            pageContent.Child = page;
            doc.Pages.Add(pageContent);
            ReportViewer = doc;
        }
    }
}

ここでFixedDocumentをこしらえてReportViewerプロパティにセットしている。
今回は単一ページだが、複数ページも実装可能。これを見れば自力で作れると思う。
2ページ目以降は書式が変わってアタッチシート、なんてこともできるだろう。
FixedDocumentの構造は、

親)FixedDocument
子)PageContent
孫)FixedPage

で、孫のFixedPageの子供としてUserControlをChildren.Addする。

App.xamlの編集(:warning:これを書かない方法を後述)

アプリで二つのViewModelを利用できるようにリソースとして登録する。

App.xaml
<Application x:Class="ShukkaReport.App"
    -- 省略 --
    xmlns:local="clr-namespace:ShukkaReport.ViewModels"
    StartupUri="MainWindow.xaml">
    <Application.Resources>
        <local:MainWindowViewModel x:Key="MainVM"/>
        <local:ShukkaViewModel x:Key="ShukkaVM"/>
    </Application.Resources>
</Application>

ここのx:Keyは自分の好みで名付ける。ウインドウとユーザーコントロールのDataContextに、これらの名前を各々設定する。

インテリセンスで各ViewModelが現れない場合は、xmlns:local の記載を各ViewModelのCSファイルの名前空間と一致させる。

MainWindow.xamlの編集

MainWindow.xaml
<Window x:Class="ShukkaReport.MainWindow"
    -- 省略 --
    DataContext="{StaticResource MainVM}"
    Title="MainWindow" Height="600" Width="600">
    <Grid>
        <DocumentViewer Document="{Binding ReportViewer}"/>
    </Grid>
</Window>

クラス名に名前空間が付加されるのは、VBと異なる。

3行目でDataContextにApp.xamlで設定した MainWindowViewModel のリソースをセットしている。(:warning:これを書かない方法を次節に示しています)
また、Grid内でDocumentViewerDocumentReportViewerをバインドしている。
:warning:App.xaml に書かない方法

@FKbelm さんの書かれた『XAML内でDataContextのプロパティにIntelliSenseが効くようにする』の前半部分を適用すると、App.xaml には何も書かずに済みます。これは見習いたい。@FKbelm さん、ありがとうございます。

MainWindow.xaml
<Window x:Class="MainWindow"
    -- 省略 -- >
    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>
ShukkaUserControl.xaml
<UserControl x:Class="ShukkaUserControl"
    -- 省略 -- >
    <UserControl.DataContext>
        <local:ShukkaViewModel/>
    </UserControl.DataContext>

そして、

DataContext="{StaticResource MainVM}"

DataContext="{StaticResource ShukkaVM}"

は消します。

実行結果

実行結果.png
印刷や拡大縮小などの機能が既に組み込まれていて、それらを実装する必要はない。あー何て楽なんだろう。UXの時代に古臭い見た目だが、そんなことは気にしない:laughing:
(ボタン・アイコンの変更は、『WPFで簡単レポート作成(3)ページ指定印刷』をご参照ください:point_left:

WPFによるデスクトップ・アプリは、まだまだ事務処理系の現場で使い倒せるのだ:bangbang:

24
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?