ぷるぷるの雑記

低レイヤーがんばるぞいなブログ. 記事のご利用は自己責任で.

WPFその1 -XAMLの書き方-

今更ながら趣味でWPF(Windows Presentation Foundation)を始めました. その過程でXAML(Extensible Application Markup Language)を書き始めたのですが、かなり独特な言語だったのでその備忘録第一弾です. 開発言語はC#を想定します.

XAMLWPFの関係

XAMLを調べる前はXAML=WPFだと思っていたのですが、どうやらそうではないようです. XAMLはあくまでUI(のプロパティや他のUIとの関係)をXMLチックに書ける言語であり、XAMLを利用できる技術(環境)は以下のようなものがあります.

名称 特徴
WPF .NET Framework上で動作する.
UWP WinRTが基本. C++やHTML+JavaScriptでも開発できる.
Xamarin クロスプラットフォーム開発ができる. 2024年5月にサポート終了予定.

つまり、「WPFXAMLを利用」は成立しますが、「XAMLを利用⇒WPF」 は必ずとも成立しません. また、ややこしいことにUWPに関しては「UWP⇒XAMLを利用」とも限りません.

また、XAML単独を解説した本というのはあまりなく、たいていはWPFかUWPかXmarin本の中でXAMLの書き方が解説されているパターンが多いようです.

Windows Formsとの比較

XAMLを用いた開発とWindows Formsの比較はネットで頻繁に議論されています. XAMLWindows Formsを比較すると次のようにまとめられます.

XAMLWindows Formsの比較
XAML WinForms
歴史 比較的新しい 古い
情報 めっちゃ少ない 多い
学習コスト なかなか高い 比較的低い
UIの配置 テキストベース ドラッグアンドドロップ
UIの描画方法 DirectX(GPU) GDI/GDI+(CPU)
リサイズ時のUI再配置 比較的容易 難しい

XAMLの方がWindows Formsより情報量も少なく学習コストが高いのは紛れもない事実です. XAMLが流行らなかったと考えるか、Windows Formsが息が長いかと考えるかは人それぞれだと思います. しかし、後で述べるようなXMLベースの言語でC#のような記述が出来るXAMLはそれなりに面白い仕組みで、触ってみる価値は大いにあると思います.

XAMLの書き方

XAML(XML)と HTMLは一見似ていますが、実際に触ってみると全くの別物です. XAMLはHTMLの進化系と考えるよりもC#の糖衣構文と考えると分かりやすいです. HTMLの場合は基本的には入れ子にできる要素の種類や数に制限がありません. XAMLの場合、入れ子にできる要素と出来ない要素があり、入れ子にできる要素や数にも限りがあります. これはXAMLの各タグ要素は.NET Frameworkのクラスに対応しているからだと考えると分かりやすいです.

例えば、次のXAMLのコードはGrid要素の中にLabel要素が入れ子になっています. GridタグのName属性にはwrapperという属性値が設定されています.

<Window>
    <Grid Name="wrapper">
        <Label>
             fuga
        </Label>
    </Grid>
</Window>

同じことをC#では次のように書くことが出来ます.

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            var label = new Label();
            label.Content = "fuga";
            wrapper.Children.Add(label);
        }
    }

驚くことに、XAMLでName属性を指定すると、C#側では同名のインスタンスとしてアクセスることが出来ます. つまり、JavaScriptでいうところのdocument.getElementById()は不要です(その代わり親子要素間で芋づる式にアクセスするといったことがしにくいです).

C#と比べるとXAMLの方が分かりやすくUIを定義できることが分かります. つまり、XAMLでタグを入れ子にする=親タグに対応するクラスのプロパティを代入するということです.

また、XAMLには以下のようにタグ要素にメンバーアクセス式(.)を使う書き方があります.

<Window>
    <Grid Name="wrapper">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Label>
             fuga
        </Label>
    </Grid>
</Window>

同じことをC#では次のように書くことが出来ます.

   public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            var column1 = new ColumnDefinition();
            var column2 = new ColumnDefinition();
            wrapper.ColumnDefinitions.Add(column1);
            wrapper.ColumnDefinitions.Add(column2);
        }
    }

この例でもやはりXAMLC#の糖衣構文になっていることが分かりますね.

メンバーアクセス式を省略したとき

先ほどXAMLでタグを入れ子にする=親タグに対応するクラスのプロパティを代入するといいましたが、メンバーアクセス式を省略した場合は、自動的に代入されるプロパティが補間されます. たとえば次のLabelタグの書き方はどちらも全く同じ結果になります.

<!-- メンバアクセスを省略した書き方 -->
<Window>
    <Grid Name="wrapper">
        <Label>
             fuga
        </Label>
    </Grid>
</Window>

<!-- メンバアクセスを省略しない書き方 -->
<Window>
    <Grid Name="wrapper">
        <Label Content="fuga"/>
    </Grid>
</Window>

つまり、XAMLでLabelタグのメンバアクセスを省略した場合、自動的にContentプロパティへの代入だと解釈されます. このルールはLabelなどのContentControlクラス(及びその派生クラス)に適用されます. 同様にメンバアクセスを省略した場合、Decoratorクラス(及びその派生クラス)ではChildプロパティへの代入だと解釈され、Panelクラス(及びその派生クラス)ではChildrenプロパティへのAddだと解釈されます.

クラス名 主要な派生クラス メンバアクセス省略時の解釈 入れ子になれる型
ContentControl Label, UserControl Contentプロパティへの代入 Object(stringなども入れ子になれる)
Decorator Border, Viewbox Childプロパティへの代入 UIElement(ContentControlやDecoratorもUIElementクラスの派生クラス)
Panel Grid, Canvas, StackPanel ChildrenプロパティへのAdd UIElement(Childrenプロパティ自体はUIElementCollectionクラス)

XAMLWindows Formsの名前空間

Microsoftが用意しているクラスには同名でも異なる名前空間に属するものが存在します. たとえば、LabelクラスはSystem.Windows.Controls名前空間に属するLabelクラスと、System.Windows.Forms名前空間に属するLabelクラスが存在します. 当然ですがそれぞれのクラスには別々のプロパティが定義されており、ラベルの内容をC#から変える方法も異なります(PaddingプロパティなどはどちらのLabelクラスにも定義されています).

アセンブリ 表示内容のプロパティ
System.Windows.Controls.LabelPresentationFramework.dllLabel.Content
System.Windows.Forms.LabelSystem.Windows.Forms.dllLabel.Text

こんなのはいちいち覚えるようなものではありません. 先に述べたように、XAMLC#の糖衣構文なのですから、XAMLのタグの属性はC#のクラスのプロパティと対応しています. すなわち、.NETのリファレンス自体がXAMLのタグのリファレンスにもなっています.

Childプロパティを持たないものは、子要素を持てない(buttonなど Childプロパティを持つものは、子要素を一つだけモテる(継承元 Decorator). new である borderなど Childrenプロパティを持つものは、子要素を複数定義できる. addchildである (継承元UIElement、cjhildren自体の方はUIElementCollection, 入れられるのはUIElement

まとめ

タイトル詐欺もいいところでXAMLの書き方は少ししか書いてないですね.

XAMLを使えばWebアプリのような見た目のWindowsアプリが作れます. ですが正直なところ、HTML+JavaScriptに慣れているならそもそもXAMLなんか使わずにUWP with HTML+JavaScriptWindowsアプリを作ればよくね?と思わない日はありません. さらに言えば、WindowsアプリではなくWebアプリの方が使いやすくね?とさえ思います.

しかし、たとえ時流には乗っていなくとも触れたことがない技術に触れるのはやはり楽しいです. また、3Dグラフィックを扱うWindowsアプリを作るのであればXAMLはかなり強力な技術になります. 何事もやって損ということはないはずです.

参考

learn.microsoft.com

learn.microsoft.com

learn.microsoft.com

learn.microsoft.com

learn.microsoft.com

learn.microsoft.com

learn.microsoft.com

learn.microsoft.com