◆ css のようなもの → XAML の Style

前にちょっと触れた WPF で作るときの XAML で CSS っぽくする方法

気になったので少し調べてみました
と言っても これで何作るわけでもないのでかる~くです

Style

基本

Style というタグを使って CSS でいう {} のまとまりを表現します
その中の各プロパティは Setter タグを使います
<Style TargetType="Button">
<Setter Property="Background" Value="Azure"/>
<Setter Property="Foreground" Value="Tomato"/>
</Style>

こういうのです
これを CSS 風に書けば
Button {
    Background: Azure;
    Foreground: Tomato;
}

となります

Style の属性 TargetType がタグ名(コントロール名)で Setter の Property にプロパティ名を書いて その設定する値を Value に書きます

Foreground は CSS でいう color です



Style タグは Resources か Style の添付プロパティの下にしか書けないみたい
<Window>
    <Window.Resources>
        <Style TargetType="Button">
            <Setter Property="Background" Value="Purple" />
        </Style>
    </Window.Resources>

    <Button>
        <Button.Style>
            <Style TargetType="Button">
                <Setter Property="Background" Value="Lime" />
            </Style>
        </Button.Style>
        ぼたんてきすと
    </Button>
</Window>

Window.Resources に書くと Window 以下全体のように 書いた場所以下が対象になるようです
Button.Style のような書き方の場合は その Button コントロールひとつを指定して設定できるようです
HTML の style 属性ということかな
Style にしなくても普通の属性でも設定できるのにこういうことも可能なようです

上の XAML の場合は ボタンコントロールは Window の内側ですし 自分の Style 属性もあるので 2 つの Background が設定されています
ですが 自分の Style 属性が優先されるので Lime 色になります

style 属性が優先なのは HTML/CSS でも一緒ですね

クラス

同じ種類のコントロール全部だけじゃなくて CSS でいうクラスのようなこともできます
<Style x:Key="classname" TargetType="Button">
<Setter Property="Background" Value="Crimson"/>
</Style>

という Style があるときに
<Button>普通の</Button>
<Button Style="{StaticResource classname}">色付き</Button>

というボタンを 2 つ用意すると下のほうだけにスタイルが適用されて色がつきます

Style の定義では x:Key 属性にクラス名を書いておきます
コントロールの方では Style 属性に {StaticResource ○○} というフォーマットでクラス名を指定します

HTML/CSS だと
<style>
button.classname{
    background: crimson;
}
</style>
<button>普通の</button>
<button class="classname">色付き</button>
となります

StaticResource に対して DynamicResource もあります
違いは 最初の定義時のものを使うか 動的に変更されるものを使うかということみたいです

継承

CSS 自体にはない機能ですが XAML の Style では継承もできます

Style タグの属性に BasedOn という属性を追加します
<Style x:Key="base" TargetType="Button">
<Setter Property="Background" Value="Gold"/>
<Setter Property="Foreground" Value="Chocolate"/>
</Style>

<Style x:Key="inherited" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="Yellow"/>
<Setter Property="FontSize" Value="20"/>
</Style>

下の Style は上の Style を継承しています
なので Button の Style に inherited を指定すれば 背景が Yellow で文字色が Chocolate でフォントサイズが 20 のボタンになります

BasedOn の書き方はクラスと同じで StaticResource を使います
TargetType が Button のを指定しているのですが {StaticResource base} でもいいんじゃないかと思います(未確認)
(後から確認したくなったときにサッと起動して確認できないのが C# というか多くの言語の不便なところですよね JavaScript なら書きながら試したいことができたら F12 キーを押して即試せるのに……)

インクルード

Winow.Resources に Style を書いていたと思いますが Resources は別ファイルにすることができます
つまり CSS のようにファイルを分けてロードするということができます

ロードされる側はこんな定義の仕方をします

[style.xaml]
<ResourceDictionary
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Style TargetType="Button">
    <Setter Property="Background" Value="#f0f0f0"/>
  </Style>
</ResourceDictionary>

このファイルをロードするときは
<StackPanel>
    <StackPanel.Resources>
        <ResourceDictionary Source="style.xaml"/>
    </StackPanel.Resources>
</StackPanel>

こんな感じでロードします
Window じゃないといけないことはないので StackPanel にしてみました
ロードしたところに 読み込むファイルで定義したものをそのまま入れるような感じです

複数のファイルをロードする場合は マージが必要になるようでこんなに書かないといけません
<StackPanel>
    <StackPanel.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="style1.xaml"/>
                <ResourceDictionary Source="style2.xaml"/>
                <ResourceDictionary Source="style3.xaml"/>
                <ResourceDictionary Source="style4.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </StackPanel.Resources>
</StackPanel>

CSS ぽいものから XAML にする

CSS と比べると全部 XML なのですごく書くのが面倒ですよね
ただ 面倒なだけじゃなくて ぱっと見たときにごちゃごちゃしていて目的のものを探すのも大変です

ということで CSS 風に書いたものを XAML 変換するものを作ってみました
といっても 全部の機能に対応してるわけじゃないです

これだけあれば簡単なものなら困らないんじゃない?くらいなものです
また 完全な XAML を出力するのじゃなくて Style タグのグループとして出力するようになってます

C# に組み込んで実行時に CSS 風なものから変換だと複雑ですし 遅くなりますし そもそもコンパイル時に XAML は IL に変換されてた気がするので 実行時に XAML 生成しても使えなさそうに思えます

どこの Resources に配置するかや 別ファイルにするかなども Style タグだけ作った方が自由にできるのでこっちのほうがいいと思ってます

あと パーサは凄くシンプルに 「{」まで持ってくる 「}」まで持ってくる のような作りなので構文エラーがあるとおかしなことになる可能性は高めです


xcss2xaml.html

使い方

こんな構文
Button.base {
border-thickness: 0;
background: white;
}

Button extends .base {
Foreground: #77a;
}

Button.name extends .base {
BorderThickness: 3;
}

タグ名とクラス名を書けます
extends の後にクラスのセレクタを書けばそのクラスを継承できます

↑のコードで出力される XAML はこちら
<Style TargetType="Button" x:Key="base">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Background" Value="white" />
</Style>
<Style TargetType="Button" BasedOn="{StaticResource base}">
<Setter Property="Foreground" Value="#77a" />
</Style>
<Style TargetType="Button" x:Key="name" BasedOn="{StaticResource base}">
<Setter Property="BorderThickness" Value="3" />
</Style>

どうせなら border とか color とか CSS のプロパティ名と書き方で XAML 変換するようにしてもよかったのですが XAML というか C# のクラス名で慣れてる人もいるでしょうし どこかで BorderBrush とか Foreground とか書くことはでてきそうなので そのときに CSS のプロパティ名で書いてると不便なこともあるかなーと思ってそのまま出力することにしました

未対応


Style をネストして
section div h1 {}
みたいな構造にすることもできるようですが 非対応です
(めんどくさくなった)

:hover のようなこともトリガーで可能ですが非対応です
(めんd(ry))