【WPF】WPFとは何か簡単にまとめてみた【C#】

# 概要
WPFとは何か、自身の勉強も兼ねてまとめてみました。

# 目次
- [WPFとは](#wpfとは)
- [XAMLとは](#xamlとは)
- [歴史](#歴史)
- [MVVMについて](#mvvmについて)
- [Prismについて](#prismについて)
- [その他](#その他)


# WPFとは
WPFは「Windows Presentation Foundation」の略で、マイクロソフトにより開発されました。Windowsアプリケーションの画面を作成するための技術で、UI部分はXMLベースのマークアップ言語を使用した「XAML」というもので定義します。これにより、XAMLを使用してUI部分を作成するデザイナーと、ロジック部分を作成するプログラマの作業を分離して開発を進めることができます。
Visual Studioを使用して、C#を用いて開発することができます。

# XAMLとは
「ザムル」と発音します。「Extensible Avalon Markup Language」の略です。「Avalon」は、WPFの開発当初のコードネームでした。XAMLはUIの構造と見た目をマークアップで定義できるものです。

XAMLのコードは全てC#でも記述することができます。XAMLはC#でのインスタンスを階層的に表現したものです。
例えば、以下のようなxamlがあるとすると、
```xml
<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    Title="サンプルウィンドウ"
    Width="300" Height="200">
    
    <Grid>
        <Button Content="クリックしてください" 
                HorizontalAlignment="Center"
                VerticalAlignment="Center"/>
    </Grid>
</Window>
```

以下のようにC#でも表現できます。

```C#
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        // Windowの設定
        Title = "サンプルウィンドウ";
        Width = 300;
        Height = 200;

        // Gridの作成と設定
        var grid = new Grid();
        Content = grid;

        // Buttonの作成と設定
        var button = new Button
        {
            Content = "クリックしてください",
            HorizontalAlignment = HorizontalAlignment.Center,
            VerticalAlignment = VerticalAlignment.Center
        };

        // GridにButtonを追加
        grid.Children.Add(button);
    }
}
```

# 歴史
マイクロソフトは2002年に.NET Frameworkをリリースした後あたりから、新しいUIフレームワークの必要性を感じていました。
従来の[Windows Forms](https://ja.wikipedia.org/wiki/Windows_Forms)には以下のような課題があったからです。
- グラフィック描画性能の限界
- モダンなUIが実現できない
- 開発効率を格段に上げられない

そこでマイクロソフトは上記を改善するような「WPF」の開発を進め、2006年に発売されたWindows Vistaにプリインストールされる形で公開されました。

# Windows Formsとの違い
WPFとWindows Formには以下のような違いがあります。

| 特徴 | WPF | Windows Forms |
|:-:|:-:|:-:|
| グラフィックス | [Direct3D]()という3Dグラフィックスを描画できるAPIを使用 |  [GDI/GDI+](https://ja.wikipedia.org/wiki/Graphics_Device_Interface)を使用 |
| 描画形式<br>[※ベクターとラスターの違い](https://321web.link/vector/) | ベクターベース |  ラスターベース |
| カスタマイズ性 | 高い<br>(スタイルやテンプレートを使用して再利用や柔軟な変更が可能)  |  比較的制限される |
| 学習コスト  | 比較的高い<br>(C#以外にxamlの取得やMVVMパターンなどの把握が必要になるため)  | 比較的低い  |

:::note
Microsoftが提供するマルチメディアAPIの総称は[DirectX](https://ja.wikipedia.org/wiki/Microsoft_DirectX)といいますが、Direct3Dはその中に含まれる3Dグラフィックス処理用のAPIのことを指します。
:::

# MVVMについて
MVVMは (Model-View-ViewModel) の略で、WPFアプリを開発するための設計パターンです。3つの主要なコンポーネントに分割して、それぞれの責任を明確に分離することで、保守性が向上できます。
特に大規模なアプリケーション開発や、複数人での開発を行う際にその価値が発揮されます。

### Model
アプリのデータと[ビジネスロジック](https://wa3.i-3-i.info/word13666.html)を担当します。
データベースや外部のサービスと連携を行うのもModelの仕事です。
UI部分は意識せず、データの処理に集中する部分です。

### View
ユーザーが視覚的に見ることができるUIの部分です。XAMLを使用して定義します。
データバインディングという機能を用いて、ViewModelと連携を行います。
ビジネスロジックは意識せず、表示に集中する部分です。

### ViewModel
ModelとViewの橋渡しとしての役割を持ちます。
データバインディングを用いてViewと連携を行います。
また、コマンドを通じてユーザーがUIの操作を行った直後の処理を担当します。

# Prismについて
Prismは、WPFアプリケーション開発のMVVMパターンを効率よく書くことをサポートしてくれるライブラリです。

### インストール方法
Visual StudioのNugetからインストールできます。

https://www.nuget.org/packages/Prism.Core

https://www.nuget.org/packages/Prism.Wpf

:::note
「Prism.Core」はPrismのコアとなるパッケージです。「Prism.Wpf」はwpf用のパッケージで、他にも「Prism.Form」というXamarin用のパッケージがあります。
:::

例えば、Modelはプロパティが変更された際、ViewModelに通知する必要があります。
Prismを使用しない方法でそれを書くと、`INotifyPropertyChanged`を使用する形で以下のような形になると思います。

#### Model
```C#:Model.cs
    public class Model : INotifyPropertyChanged
    {
        public string Name
        {
            get => _name;
            set
            {
                if (_name != value)
                {
                    _name = value;
                    OnPropertyChanged();
                }
            }
        }
        private string _name;

        public event PropertyChangedEventHandler PropertyChanged;
        
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
```

#### ViewModel
```C#:ViewModel.cs
public class ViewModel : INotifyPropertyChanged
{
    private readonly Model _model;
    private string _displayName;

    public ViewModel(Model model)
    {
        _model = model;
        _model.PropertyChanged += HandleModelPropertyChanged;
        _displayName = model.Name;
    }

    private void HandleModelPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(Model.Name))
        {
            _displayName = _model.Name;
            OnPropertyChanged(nameof(DisplayName));
        }
    }

    public string DisplayName
    {
        get => _displayName;
        private set
        {
            if (_displayName != value)
            {
                _displayName = value;
                OnPropertyChanged();
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
```

上記をprismを使用すると、以下のようにシンプルに記載できます。

#### Model
```C#:Model.cs
public class Model : BindableBase
{
    private string _name;
    public string Name
    {
        get => _name;
        set => SetProperty(ref _name, value);
    }
}

```

#### ViewModel
```C#:ViewModel.cs
public class ViewModel : BindableBase
{
    private readonly Model _model;
    private string _displayName;

    public ViewModel(Model model)
    {
        _model = model;
        _model.PropertyChanged += HandleModelPropertyChanged;
        _displayName = model.Name;
    }

    private void HandleModelPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(Model.Name))
        {
            DisplayName = _model.Name;
        }
    }

    public string DisplayName
    {
        get => _displayName;
        private set => SetProperty(ref _displayName, value);
    }
}
```

このようにPrismの`BindableBase`を継承するだけで、手動で実装する箇所が少なくなります。

:::note
上記以外にもprismの機能はたくさんあります。
また、他にWPFの開発を効率的に進められるライブラリとして、[ReactiveProperty](https://www.nuget.org/packages/ReactiveProperty)というものもあります。
:::

参考：
- [WPF Prism を使ってみた](https://houwa-js.co.jp/2020/09/20200915/)
- [ざっくりPrismの使い方①](https://blog.hn-pgtech.com/2023-10-26/)

# その他
以下のブログにWPF入門用の詳しいブログ記事がまとめられていました。
- [WPF4.5入門 その62「まとめ」](https://blog.okazuki.jp/entry/2014/12/27/200015)

また、以下のページによると、Visual Studioのベース部分はWPFで作成されているよう（？）でした。

- [WPF ってほぼ使われてないよね](https://var.blog.jp/archives/69202816.html)
- [visualstudioのGUIはWPFで作られているのですか](https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q10265281020)

# 自分で記載したWPF関連の記事
自分で記載したWPFの記事をまとめました。
こちらはほぼ自分用です。
- [ツールチップにURLリンクを埋め込み良い感じに表示する方法](https://qiita.com/MetroOsamu/items/569a5bda44c0b5dfa289)
- [DataTemplateSelectorクラスを使用して、動的にコントロールの種類を変更する方法](https://qiita.com/MetroOsamu/items/572bdb0f86b284fea588)
- [ControlTemplateやStyleを別ファイルに定義し、Viewから利用する方法](https://qiita.com/MetroOsamu/items/e9961eab881e749f3532)
- [Converterの作成方法](https://qiita.com/MetroOsamu/items/58503e935b21dc392cc4)
- [viewを作成する際のテンプレートコード](https://qiita.com/MetroOsamu/items/106ef17efd37ba1bedb9)
- [Window、Page、UserControlの違い](https://qiita.com/MetroOsamu/items/4db76fbbdca6951cd68c)
- [トグルボタンに表示されている文字の修正を行った際、微修正だと思ったら意外と改修が必要だった](https://qiita.com/MetroOsamu/items/b1290ddab31ae6e481b2)
- [ModelのPropertyChangedをViewModelのPropertyChangedとしてそのまま発行するViewModelの基底クラスを作成してみた](https://qiita.com/MetroOsamu/items/408dbc9d29e8aea18885)