Blazor Client-Sideアプリで帳票印刷する

Blazor Client-Sideアプリで、帳票印刷アプリケーションの作成をしてみたいと思います。

今回は、[paper.cssを使ってブラウザだけで名刺を印刷する](https://qiita.com/okoppe8/items/abcafdad3a894bca7f38)で、実現されていることをオマージュ(~~最大限パクリ~~)させて頂き、Blazorに換装してみたいと思います。

[Demo](https://blazorappprint.firebaseapp.com/)

サンプルコード
[https://github.com/kimux8/BlazorAppPrint](https://github.com/kimux8/BlazorAppPrint)

# 概要

参照元サイト様のBlazor版です。JavaScript + CSSにより名刺印刷アプリを実現されています。

paper.cssを使ってブラウザだけで名刺を印刷する
[https://qiita.com/okoppe8/items/abcafdad3a894bca7f38](https://qiita.com/okoppe8/items/abcafdad3a894bca7f38)

また、参照元ではCSSフレームワークである`paper.css`を活用されています。

paper.css
[https://github.com/cognitom/paper-css](https://github.com/cognitom/paper-css)

そろそろ真面目に、HTMLで帳票を描く話をしようか
[https://qiita.com/cognitom/items/d39d5f19054c8c8fd592](https://qiita.com/cognitom/items/d39d5f19054c8c8fd592)

今回は、移植するにあたり、はまったところ（帳票作成とあまり関係ないことも含め）を中心に記述していきたいと思います。

# ページ間でのパラメータの渡し方について

Blazorでのページ遷移はいくつかやり方がありますが、`NavigationManager`の`NavigateTo("リンク先")メソッド`を使用する方法があります。パラメータが複数ある場合、複数の`ルートパラメータ`を指定することができますが、数が多すぎる場合、必要な情報を定義したクラスを`Shared`に定義して`Inject`して連携した方がスッキリします。

### ルートパラメータによるパラメータ渡し
```c#:index.razor

@code{
    string p0 = @"営業推進部営業1課
シニアマネージャー";
    ...
    string p5 = @"〒100-8111
東京都千代田区千代田1-1
ロイヤルパレスパークビル 15F
電話: 03-1111-1111(代表) FAX: 03-2222-2222
携帯: 090-3333-3333
mail: kii-taichiro@dummy.lo.go.jp";

    void OnPrint()
    {
        // カッコ悪い
        navMgr.NavigateTo(string.Format("/print/{0}/{1}/{2}/{3}/{4}/{5}", p0, p1, p2, p3, p4, p5));
    }
}
```

```c#:Print.razor
@page "/print/{p0}/{p1}/{p2}/{p3}/{p4}/{p5}"
```

### Injectによるパラメータ渡し

```C#:shared/Person.cs
namespace BlazorAppPrint.Shared
{
    public class Person
    {
        public string P0 { get; set; }
        public string P1 { get; set; }
        public string P2 { get; set; }
        public string P3 { get; set; }
        public string P4 { get; set; }
        public string P5 { get; set; }
        ....
    }
}
```

```c#:Startup.cs
using BlazorAppPrint.Shared;

namespace BlazorAppPrint
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<Person>(); // インスタンス化
        }
    }
}
```

```c#:index.razor
@page "/"

@inject NavigationManager  navMgr
@inject Person person
@code{
    protected override void OnInitialized()
    {
        person.P0 = @"営業推進部営業1課
シニアマネージャー";

        person.P1 = "きい";
        ...
        person.P5 = @"〒100-8111
東京都千代田区千代田1-1
ロイヤルパレスパークビル 15F
電話: 03-1111-1111(代表) FAX: 03-2222-2222
携帯: 090-3333-3333
mail: kii-taichiro@dummy.lo.go.jp";     
    }

    void OnPrint()
    {
        // すっきりー
        navMgr.NavigateTo("/print");
    }
}
```

```c#:Print.razor
@page "/print"
@inject Person person
```

# CSS分離できない問題

Blazorでは、CSSなど静的ファイルは、`wwwroot/index.html`に記述します。ここに定義すると、すべてのページで読み込まれてしまうので、特定のページだけ適用したい！というのが難しいわけですね。これは、他のフレームワークからBlazorに移行しようとしたとき、大きな壁になるかもしれません。

今回は`paper.css`のCSSフレームワークを活用しているのですが、`paper.css`を特定のページに適用することができないため静的リンクしていません。その為、参照元サイトでもそうですが、`paper.css`の一部を`site.css`に移植しています。

CSS Isolation in Blazor Components
[https://github.com/dotnet/aspnetcore/issues/10170](https://github.com/dotnet/aspnetcore/issues/10170)

### はじめからある不要なスタイルの削除

帳票デザインのページでは、絶対位置指定などベタに調整することがあると思います。Blazorではデフォルトプロジェクトから作成した時に、既に`site.css`に多くのスタイルが定義された状態であり、これらの定義が邪魔になることがあります。
今回は、`app`に定義されたスタイルをコメントアウトしています。

```css:wwwroot/css/site.css
app {
    /*コメントアウト*/
    /*position: relative;
    display: flex;
    flex-direction: column;*/
}
```

# Tableでは、子要素をコンポーネント化しない

今回のアプリケーションでは、名刺デザインが2つ(プレビュー用と印刷用)が登場しますが、`Table`により実装しています。ここをサクっとコンポーネント化すれば、繰り返し処理などはスッキリするのですが、`Table`と中の要素(`tr,td`)を別コンポーネントする場合、下記のようなCSSでは効かないので、注意が必要です。

```css:wwwroot/css/site.css
table.preview {
    position: relative;
    ...
    transform-origin: top;
}

    table.preview td {
        border: 1px solid;
        ...
        position: relative;
    }
```

`table.preview td`を使用するときは、`preview`と`td`のコンポーネントは同じコンポーネントに書く。

```c#:Page.razor
<table class="preview">
  <tr>
    <td>...  ここを別コンポーネントにすると、CSSがめんどい
  </tr>  
</table>
```

既存のCSSを使いまわす場合、注意が必要ですね。

# 常に親ウィンドウ

印刷処理は、`JavaScript`の`window.print()`をコールしています。参照元サイトでは印刷のプレビュー画面が閉じられた後、印刷用ページのウィンドウを閉じる`window.close()`をコールしますが、今回は**親ウィンドウ自体が印刷用の画面**となる為、`window.close()`が使えません。その為、`window.history.back(-1)`により、前画面に遷移することで、同様の動作を行っています。

```html:wwwroot/index.html
...
<body>
    ...
    <!--印刷ダイアログ表示用-->
    <script>
        window.printwindow = () => {
            setTimeout(function () {
                window.print();
                // windows.close()は使えないので、ひとつ前に戻る
                window.history.back(-1);
            }, 200);
            return true;
        }
    </script>
<body>
```

# 最後に

Blazorでの帳票印刷は、HTMLベースでの帳票作成が手っ取り早いです。とは言え、レポート作成のパッケージをゴリゴリ使用しているアプリケーションのBlazorへの移行などは、少々大変かもしれません。