vorfee's Tech Blog

Just another tech blog

Prism.DryIoc でも Microsoft.Extensions.Logging や Microsoft.EntityFrameworkCore が使いたい

WPF の優れたフレームワークの1つである Prism (PrismLibrary) の悩み事の1つに、そのままでは Microsoft.Extensions.DependencyInjection (以下、MSDI) の機能が利用できないことがある。

Prism では Microsoft.Extensions.Logging (以下、MSLogging) が使用できるだろうか?答えは YES である。

結論

DryIoc.Microsoft.DependencyInjection のNuGetパッケージをインストールし、App.xaml.cs を以下のように編集する。

// 以下のusingは拡張メソッドのために必須
using DryIoc.Microsoft.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Prism.Ioc;

public partial class App : Prism.DryIoc.PrismApplication
{
    // ...中略...

    protected override Prism.Ioc.IContainerExtension CreateContainerExtension()
    {
        var serivces = new ServiceCollection();
        services.AddLogging(options =>
        {
            // Todo: ここにLoggerの設定を記述
        });
        services.AddDbContext<DbContext>(/*Todo: ここにDatabaseの設定を記述*/);

        var container = new DryIoc.Container(CreateContainerRules())
            .WithDependencyInjectionAdapter(services);
        return new Prism.DryIoc.DryIocContainerExtension(container);
    }
}

これにより、以下のようなクラス名をカテゴリとする ILogger を解決可能となる。

public class SampleClass
{
    public SampleClass(Microsoft.Extensions.Logging.ILogger<SampleClass> logger)
    {
        logger.LogInformation("SampleClass created.");
    }
}

また、Microsoft.EntityFrameworkCore の内部で使用されるログ情報も正常に取得できるようになった。

参考

stackoverflow.com

stackoverflow.com

Python Jinja2入門 その2 Jinja2スクリプトの作成

前回に引き続き、Jinja2を使ったテキストテンプレート機能の作成方法を紹介する。

前回の記事は以下。

vorfee.hatenablog.jp

スクリプトの作成

まず、template.txt という名前で以下のテンプレートテキストファイルを作成する。

こんにちは、{{name}}さん!

次に main.py という名前で以下のスクリプトを作成する。

from jinja2 import Environment, FileSystemLoader

env = Environment(loader=FileSystemLoader('./'))
template = env.get_template('template.txt')
data = { 'name': '田中' }
print(template.render(data))

Pythonでmain.pyを実行すると、{{name}}部分が「田中」に置換された結果が画面に表示される。

PS> python main.py
こんにちは、田中さん!

足早に説明したが、これからスクリプトを1行ずつ紐解いていく。

スクリプトの説明

1~2行目 - 宣言

1~2行目は、jinja2のPythonモジュールを使用する宣言である。

from jinja2 import Environment, FileSystemLoader
  • Environment はJinja2を実行する環境を定義するクラスで、様々な基本設定を行う。
  • FileSystemLoader はテンプレートの読み込み方を定義するクラスで、テンプレートをテキストファイルからを読み込むことができる。

FileSystemLoader以外にも様々な読み込み方があるが、それは後日の紹介とする。

3行目 - 実行環境定義

3行目は、Jinja2の実行環境を新規作成している。

env = Environment(loader=FileSystemLoader('./'))
  • 変数 env にJinja2実行環境インスタンスを代入する。
  • loader にはテンプレートの読み込み方を定義するインスタンスを設定する。
  • FileSystemLoader('./') は、カレントディレクトリを基準のパスとして、テンプレートファイルを探索するというインスタンスである。

4行目 - テンプレートエンジン作成

4行目は、テンプレート実行インスタンス(テンプレートエンジン)を新規作成している。

template = env.get_template('template.txt')
  • 変数 template にテンプレート実行インスタンスを代入する。
  • get_template('template.txt') では、3行目に指定した場所を基準としたテンプレートファイルの名前 を指定し、テンプレート実行インスタンスを作成する。

5行目 - データモデル作成

5行目は、テンプレートに適用するデータを定義している。

data = { 'name': '田中' }
  • 変数 data に辞書を代入する。
  • 辞書のキーに「name」、値に「田中」を指定しており、これはテンプレート内の変数「name」を「田中」に置き換えるという意味になる。

今回はデータをスクリプト上で作成しているが、実際にはCSVファイルにしたり、YAMLファイルにしたり、様々な方法で外部保存することが考えられる。

6行目 - テンプレート実行

6行目は、テンプレートを実行(render)している。

print(template.render(data))
  • template.render(data)では、template.txt に対して data を適用し、その結果を文字列として返す。

今回は説明のために画面上にprintしているが、テンプレート結果はファイルに書き込むほうが一般的だろう。

まとめ

説明は長くなってしまったが、スクリプト自体は非常に短く数分で作成できる。内容も非常に簡潔で理解しやすく、Jinja2が非常に優れたライブラリであることが分かる。

次の記事ではもう少し実用に近いサンプルを示す予定。

Python Jinja2入門 その1 基礎知識

Pyhonに Jinja というライブラリがある。Jinjaを使用するとテキストファイルの一部を文字列置換できる。

Jinja is a fast, expressive, extensible templating engine. Special placeholders in the template allow writing code similar to Python syntax. Then the template is passed data to render the final document.

Jinjaは高速で表現に富み、拡張可能なテンプレートエンジンです。Pythonに似た文法で、特別なマークをテンプレートファイルに埋め込むことができます。マーク箇所は渡されたデータに置き換えられて出力されます。

-Jinja Documentationより引用、筆者訳 https://jinja.palletsprojects.com/

この記事では第一弾として、Jinjaでできることを簡単に紹介する。

Jinjaって何?

Jinjaはテンプレートエンジンである。テンプレートエンジンとは、Wikipediaで以下のように定義される。

テンプレートエンジンはテンプレートと呼ばれる雛形と、あるデータモデルで表現される入力データを合成し、成果ドキュメントを出力するソフトウェアまたはソフトウェアコンポーネントである。

テンプレートエンジン - Wikipedia

なお、ひな形を使わずにドキュメントを生成するソフトウェアはジェネレータなどと呼ばれる。

テンプレートエンジンはひな形の一部を置き換えるという単純な処理ゆえに適用範囲が広く、高い汎用性がある。ジェネレータは生成するドキュメントの内容(構文)を解析・把握できる機能が求められ、複雑な処理をこなすことができる反面、汎用性に乏しい。

Jinjaの用語

Jinjaで使われる用語を紹介する。難しい用語はないが、覚えておけばプログラムを書く際の助けとなる。

用語 日本語表記 説明
Template テンプレート テンプレート処理、またはテンプレート処理に用いられるテキストのこと。
Template Engine テンプレートエンジン Jinja2のこと。テンプレートを実行するシステム。
Environment (プログラム用語)テンプレートエンジンの基本設定を取り扱うPythonクラス。
Loader (プログラム用語)テンプレートの読み込み方を取り扱うPythonクラス。

Jinjaでできること

先にも述べたが、Jinjaはテンプレートエンジンである。Jinjaでできることのうち、主要なものを紹介する。

  • 変数の置換
  • for(繰り返し)
  • if(条件分岐)
  • include(ファイル分割)

変数の置換

{{変数名}} と表記した個所の変数を置換できる。

こんにちは {{user_name}} さん!

変数が数値の場合、四則演算ができる。

参考: https://jinja.palletsprojects.com/templates/#variables

for (繰り返し)

{% for item in items %} の形式で繰り返し処理ができる。

{% for person in people %}
氏名:{{person.name}} 年齢:{{person.age}} 性別:{{person.sex}}
{% endfor %}

参考: https://jinja.palletsprojects.com/en/3.0.x/templates/#for

if (条件分岐)

{% if 条件式 %} の形式で条件分岐ができる。

{% if age < 18 %}
18歳以下は閲覧できません。
{% endif %}

参考: https://jinja.palletsprojects.com/en/3.0.x/templates/#if

include(ファイル分割)

{% include "テンプレートファイル" %} でテンプレートファイルを分割、テンプレート処理時に結合できる。

{% include "template2.j2" %}

参考: https://jinja.palletsprojects.com/en/3.0.x/templates/#include

次回はスクリプトを書き、Jinjaでテキスト置換をする方法を紹介する。

vorfee.hatenablog.jp

SDK形式のcsprojを持つWPFプロジェクトでApp.xamlのMainメソッド自動生成を抑制する方法

SDKプロジェクトのWPFアプリケーションでMainメソッド自動生成を防止する。

Microsoftの公式リファレンスに載っている情報なのだが、忘れやすいためメモ。公式リファレンスの記載場所は以下。

Microsoft.NET.Sdk.Desktop の MSBuild プロパティ - .NET | Microsoft Docs

本記事の適用可能対象フレームワーク

.NET 5以降もおそらくWPFである限りは適用可能。

結論

csprojファイルにEnableDefaultApplicationDefinitionを追加する。

<PropertyGroup>
  <EnableDefaultApplicationDefinition>false</EnableDefaultApplicationDefinition>
</PropertyGroup>

上記を追加した後のcsprojファイルは、以下のようになる。

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net461</TargetFramework>
    <UseWPF>true</UseWPF>
    <EnableDefaultApplicationDefinition>false</EnableDefaultApplicationDefinition>
  </PropertyGroup>

</Project>

WPFのMainメソッド

Mainメソッドの自動生成を抑制したなら、自力でMainメソッドを作成する必要がある。一般にProgram.csというファイルをプロジェクトに追加し、以下の内容を記載する。

(1)最小形

Visual Studioが自動生成するコードと同じ。

namespace WpfApp1
{
    public static class Program
    {
        [System.STAThread]
        public static void Main()
        {
            var app = new App();
            app.InitializeComponent();
            app.Run();
        }
    }
}

(2)スプラッシュスクリーンを表示

スプラッシュスクリーンを表示する場合、プロジェクトにスプラッシュスクリーンを追加し、メインメソッドの先頭でSystem.Windows.SplashScreen.Showを呼ぶ。

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

  <!-- 中略 -->

  <ItemGroup>
    <SplashScreen Include="SplashScreen1.png" />
  </ItemGroup>

</Project>
namespace WpfApp1
{
    public static class Program
    {
        [System.STAThread]
        public static void Main()
        {
            var splashScreen = new System.Windows.SplashScreen("SplashScreen1.png");
            splashScreen.Show(autoClose: true);
            var app = new App();
            app.InitializeComponent();
            app.Run();
        }
    }
}

WPFの依存関係プロパティとは?添付プロパティとは?

注意:本記事は独学による個人的な備忘録のため、誤りが含まれる可能性があります。

依存関係プロパティ(DependencyProperty)

DependencyPropertyWPFプロパティシステム を構成する機能の一つである。

添付プロパティ(Attached Property)

添付プロパティWPFプロパティシステムにおける DependencyProperty の形態の一つである。

依存関係プロパティの概要

.NET 共通型システムには Property が定義されている。いわゆるC#の Property のことだが、ここでは区別のため CLR Property と表記する。

DependencyProperty は CLR Property をラッピングし、WPFプロパティシステム で利用される。平たく言うと、DependencyProperty とは WPF が外部拡張性を獲得し、コントロールをうまく制御するために作られた仕組みの一部のことだ。

WPFのコントロールは例外なく DependencyObject を基底クラスに持つ。これはオブジェクトがWPFプロパティシステムに参加したければ、必ず DependencyObject を基底クラスに持たなくてはいけないというルールがあるからだ。

添付プロパティの概要

添付プロパティは WPFプロパティシステムの機能の1つで、WPFのコントロールに備わっていないDependencyPropertyを、あたかも初めから備わっていたかのように振舞わせることができる機能である。

添付プロパティってどんなの?

WPFを使用するうえで意識することはないが、添付プロパティは普段から頻繁に利用している。例えば以下のようなXAMLも添付プロパティを利用している。どれが添付プロパティか分かるだろうか?

<Grid>
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="100"/>
    <ColumnDefinition Width="300"/>
  </Grid.ColumnDefinitions>
  <TextBlock Grid.Column="0" Text="あいうえお"/>
  <TextBlock Grid.Column="1" Text="かきくけこ"/>
</Grid>

Grid.Column="0" の部分が添付プロパティである。しかし、TextBlock の定義を見ても Grid.Column というプロパティは存在しない*1。では次の例はどうだろうか?

<Canvas>
  <Rectangle Canvas.Top="20" Canvas.Left="55" Width="30" Height="100" Fill="Red"/>
</Canvas>

Canvas.Top="20"の部分が添付プロパティである。ところが Rectangle の定義を見ても Canvas.Top というプロパティは見当たらない。

このように、そのコントロール自体にプロパティが存在しないが、添付プロパティを使用することでコントロールに後付けで DependencyProperty を追加し、値を保持することができる。

先の例において、添付プロパティのCanvas.Top が Rectangle に指定されている。Canvas.Top は Canvas がRectangleを描画する際の絶対座標である。つまり、この値を利用するのはCanvasであり、Rectangle自身はこの値を使用しないが、値を保持できている。

添付プロパティが無かったら何がマズい?

勉強の理解を深めるときに逆転の発想をするのは良いことだ。だから、添付プロパティが無かったらどうなるか想像してみることにする。

もし 添付プロパティ がなかったら、Canvasの実装 はどうなるだろうか?

Canvasの機能要件(例)

  • Canvasは、子に複数個のコントロールを持つことができる
  • Canvasは、子のコントロールにLeft, Topの絶対座標を指定して並べることができる

添付プロパティ無しでこれらの要件を満たすためには、Canvasが子に持つことができるすべてのコントロールにTop、Leftプロパティを定義しなければいけないだろう。きっとMicrosoftの.NETチームはICanvasChildみたいな名前のインターフェースを用意して、Canvasに描画可能なコントロールすべてにこれを実装することになる。

では中心の絶対座標を指定して配置できるようなMyCanvasというコントロールを自作したとする。

MyCanvasの機能要件(例)

  • MyCanvasは、子に複数個のコントロールを持つことができる
  • MyCanvasは、子のコントロールにCenterの絶対座標を指定して並べることができる

このコントロールの子はCenterというプロパティが必要で、IMyCanvasChildを実装しなければならない。するとどうだろう、Microsoftの.NETチームはそんなものを知らないので、WPFのすべてのコントロールはIMyCanvasChildを実装していない。なのでWPFのコントロールはMyCanvasで一切利用できない。

これでは拡張性が無さ過ぎて使い物にならないのは明らかである。だから添付プロパティはWPFの柔軟な拡張性に大きく寄与していることがやんわりと理解できる。

添付プロパティの用途は?

そのうち時間ができたら追記予定・・・

*1:そもそも.NETではプロパティ名にピリオドは使用できないことからも、このプロパティが異質であることが見て取れる

Microsoft Wordでページ内リンクをCtrl+ クリックし、リンク先に飛んだ後に元の位置に戻る方法

仕事でWord仕様書を読まされる際、ページ内の参照が多用されていることがあるかもしれない。

この記事はこんな方のためのものです

  • 何かとWordを使う機会がある
  • Ctrl + クリックで別ページに飛んだが、元の位置が分からなくなった
  • ブラウザなら戻るボタンを押せば元の位置に戻るのに・・・イライラ

結論

Alt + 左矢印 を押せば元の位置に戻ります。

でも俺はブラウザみたいに戻るボタンを表示したいんだ!という場合

Google Chromeのようにウインドウの左上に戻る/進むボタンを表示できます。

設定手順(Word2016)

  1. クイックアクセスツールバー(Wordウインドウ枠左上)の ▼ をクリック
  2. 「その他のコマンド」をクリック
  3. コマンドの選択から「リボンにないコマンド」を選択
  4. 一覧の中から「←戻る」「→進む」という項目を選択し、追加する

混乱しないためにも、やり直す、元に戻すはクイックアクセスツールバーから削除しておいたほうがよいでしょう。Ctrl+ZとCtrl+Yで代替できますし、この2つは割と常識です。(Alt+矢印左も常識だろ!とは言わないで・・・)

Windows 10の記憶域の新規作成で 0x00000032 エラーが発生する場合の対処

エラーの状態

[コントロールパネル] > [システムとセキュリティ] > [記憶域] > [新しいプールと記憶域の作成] からドライブを選択し、 [プールの作成] をクリックすると、エラーダイアログ(0x00000032)が表示される場合がある。

f:id:vorfee:20180520002924p:plain

画像引用: https://answers.microsoft.com/en-us/windows/forum/windows_10-files-winpc/storage-spaces-win-10-cant-add-new-drive-error/eca10bd6-5564-45a0-9649-d00619a68fe6

修復

バイスマネージャを開き、ディスクを再認識させることで、エラーを修復できる可能性がある。

以下に修復手順の例を示す。

1. デバイスマネージャを開く

Win + Rでファイル名を指定して実行ダイアログを開き、devmgmt.mscを入力する。

f:id:vorfee:20180520003153p:plain

2. デバイスを無効にする

バイスマネージャの該当するディスクデバイスを右クリックし、デバイスを無効にする。

f:id:vorfee:20180520003838p:plain

3. デバイスを有効にする

2と同じ手順で、無効にしたディスクデバイスを再び有効にする。

参照・引用

zshプロンプトの時間表示をリアルタイムっぽく更新する

シグナルについて少し学んだのでzshでの活用法を考えてみた。

方法

zshタイムアウトを1秒に設定する。タイムアウトごとに送られてくるSIGALRMをトリガーにして、プロンプトを更新する。

export TMOUT=1
function TRAPALRM() {
  zle .reset-prompt
}

結果

時計が秒刻みで更新されるようになった。

f:id:vorfee:20170202182502g:plain

発生する問題

  • プロンプトを更新する際に情報の再計算をする。

    時刻以外に再計算するもの(例:git, svnの情報表示など)がたくさんあると、処理しなければならないものが増えて、むやみにリソースを消費する。ノートパソコンならバッテリを消耗するかもしれない。

  • 補完などで行送りが発生すると表示がおかしくなる。

    1秒ごとに更新されてテキストがグチャグチャになる。

別のアプローチ

常に更新するのではなく、コマンドを実行したときだけ更新することもできる。過去に紹介している。