【Material Design Themes】トグルボンタンでダークモードに切り替える

# はじめに
Material Design Themesのベーステーマをトグルボンタンで切り替えるユーザーコントロールを作成したので、備忘録として公開します。
MVVMパターンで記述するため、CommunityToolkit.Mvvmも合わせて使用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562156/672e43e1-b8dc-237b-f688-58475ab0a813.png) ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562156/70786afe-9f1a-51bd-dbcf-fa1b9b53617f.png)

## 情報
**アプリケーション**
- アプリケーション：WPFアプリケーション
- ターゲットフレームワーク：.Net 8.0

**ライブラリ**
- CommunityToolkit.Mvvm 8.2.2
- MaterialDesignThemes 5.0.0
- MaterialDesignColors 3.0.0

## プロジェクトの作成
プロジェクト名を`MaterialDesignApps`としてプロジェクトを作成しました。
作成が完了したら、画像のようにフォルダを作成してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562156/a8bed178-281d-0ca7-9a0b-5da0b82ef550.png)




フォルダ作成ができたら`MainWindow`をViewsフォルダに移動してください。このとき、`MainWindow`の名前空間を`MaterialDesignApps.Views`に変更してください。また、`App.xaml`のStartupUriを`Views\MainWindow.xaml`に変更してください。

## ライブラリのインストール
NuGetパッケージの管理から`CommunityToolkit.Mvvm`と`MaterialDesignThemes`、`MaterialDesignColors`をインストールしてください。

## App.xamlの設定
```diff_xml:App.xaml
<Application
    x:Class="MaterialDesignApps.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MaterialDesignApps"
    xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
    StartupUri="Views\MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <materialDesign:BundledTheme BaseTheme="Light" PrimaryColor="Blue" SecondaryColor="Blue"/>
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign3.Defaults.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>
```

## MainWindow.xamlの設定
次に、MainWindow.xamlに`Style="{StaticResource MaterialDesignWindow}"`を追加します。
```xml:Views\MainWindow.xaml
<Window ...
    Style="{StaticResource MaterialDesignWindow}">
    ...
</Window>
```

## 確認
先程の`App.xaml`の`BaseTheme`を`Dark`に変更して、アプリケーションを起動してください。
これまでの作業に問題がなければ、背景が黒色のアプリケーションが起動されます。
これで、WPFアプリケーションでMaterial Design Themesを使用する準備が整いました。

## ThemeHelper.cs
`Helpers`フォルダに`ThemeHelper.cs`を作成します。
```Csharp:Helpers\ThemeHelper.cs
using MaterialDesignColors;
using MaterialDesignThemes.Wpf;
using System.Windows;

namespace MaterialDesignApps.Helpers;

public class ThemeHelper
{
    /// <summary>
    /// BundledThemeの設定
    /// </summary>
    public static void SetBundledTheme(BundledTheme bundledTheme)
        => Application.Current.Resources.MergedDictionaries.Add(bundledTheme);

    /// <summary>
    /// BundledThemeの設定
    /// </summary>
    public static void SetBundledTheme(BaseTheme baseTheme, PrimaryColor primaryColor, SecondaryColor secondaryColor)
    {
        SetBundledTheme(new BundledTheme
        {
            BaseTheme = baseTheme,
            PrimaryColor = primaryColor,
            SecondaryColor = secondaryColor
        });
    }

    /// <summary>
    /// BaseThemeの取得
    /// </summary>
    public static BaseTheme GetBaseTheme() => GetBundledTheme()?.BaseTheme ?? BaseTheme.Light;

    /// <summary>
    /// PrimaryColorの取得
    /// </summary>
    public static PrimaryColor GetPrimaryColor() => GetBundledTheme()?.PrimaryColor ?? PrimaryColor.Blue;

    /// <summary>
    /// SecondaryColorの取得
    /// </summary>
    public static SecondaryColor GetSecondaryColor() => GetBundledTheme()?.SecondaryColor ?? SecondaryColor.Blue;

    /// <summary>
    /// BundledThemeの取得
    /// </summary>
    public static BundledTheme GetBundledTheme()
    {
        var bundledTheme = Application.Current.Resources.MergedDictionaries
                              .OfType<BundledTheme>()
                              .FirstOrDefault();
        return bundledTheme ?? new BundledTheme()
        {
            BaseTheme = BaseTheme.Light,
            PrimaryColor = PrimaryColor.Green,
            SecondaryColor = SecondaryColor.Green
        };
    }
}
```

## ThemeSwitcherViewModel.cs
後に`ThemeSwitcher`というユーザーコントロールを作成しますが、そのViewModelとして`ThemeSwitcherViewModel`を作成します。
```Csharp:ViewModels\ThemeSwitcherViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using MaterialDesignApps.Helpers;
using MaterialDesignThemes.Wpf;

namespace MaterialDesignApps.ViewModels;

public partial class ThemeSwitcherViewModel : ObservableObject
{
    [ObservableProperty]
    bool isDarkMode;

    /// <summary>
    /// チェックボックスの値変更時
    /// </summary>
    partial void OnIsDarkModeChanged(bool value)
    {
        var theme = ThemeHelper.GetBundledTheme();
        theme.BaseTheme = value ? BaseTheme.Dark : BaseTheme.Light;
        ThemeHelper.SetBundledTheme(theme);
    }

    /// <summary>
    /// コンストラクター
    /// </summary>
	public ThemeSwitcherViewModel()
    {
        var theme = ThemeHelper.GetBundledTheme();
        isDarkMode = theme.BaseTheme == BaseTheme.Dark;
    }

}
```

## ThemeSwitcher.xaml
`Controls`フォルダにユーザーコントロールを追加してください。コントロール名は`ThemeSwitcher`としました。ThmeSwitcher.xaml.csに変更はありません。
この作業が完了したら、一度プロジェクトをビルドしてください。ビルドが完了すると、Visual Studioのツールボックスに`ThemeSwitcher`が現れます。
```xml:Controls\ThemeSwitcher.xaml
<UserControl
    x:Class="MaterialDesignApps.Controls.ThemeSwitcher"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:local="clr-namespace:MaterialDesignApps.Controls"
    xmlns:vm="clr-namespace:MaterialDesignApps.ViewModels"
    mc:Ignorable="d" >
    <UserControl.DataContext>
        <vm:ThemeSwitcherViewModel/>
    </UserControl.DataContext>
    <StackPanel
        Orientation="Horizontal">
        <TextBlock
            VerticalAlignment="Center"
            Text="Light"
            Margin="0 0 10 0"/>
        <ToggleButton
            IsChecked="{Binding IsDarkMode}"/>
        <TextBlock
            VerticalAlignment="Center"
            Text="Dark"
            Margin="10 0 0 0"/>
    </StackPanel>
</UserControl>
```

## MainWindow.xaml

```xml:Views\MainWindow.xaml
<Window
    xmlns:Controls="clr-namespace:MaterialDesignApps.Controls" 
    x:Class="MaterialDesignApps.Views.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:MaterialDesignApps.Views"
    Style="{StaticResource MaterialDesignWindow}"
    mc:Ignorable="d"
    Title="MainWindow" Height="200" Width="300">
    <Grid>
        <Controls:ThemeSwitcher
            HorizontalAlignment="Center"
            VerticalAlignment="Center"/>
    </Grid>
</Window>
```

## 起動
起動してトグルボタンによりテーマが切り替われば完成です。

## 最後に
この記事は備忘録として作成しました。
コードの使用によって生じるいかなる損害も、作者は一切責任を負いません。