はじめに

この記事は、初心者向けではありません。 WPFにおける、CommunityToolkit.Mvvmを使って「最低限の」環境を作るための、私的なメモにすぎません。

ここでのプロジェクト名は Memoir とします。プロジェクトはWPFの最新.NET(記述時点では10.0)にします。

プロジェクト作成

NuGetでインストールする必要があるパッケージ

  • CommunityToolkit.Mvvm
  • Microsoft.Extensions.DependencyInjection

まずは Views,ViewModels,Models,Helpers フォルダを作り、MainWindow.xaml を Views に移動する。

そして App.xaml を以下のようにする(StartupUriにViewsを追加する)。

<Application x:Class="Memoir.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:Memoir"
             StartupUri="Views/MainWindow.xaml">
    <Application.Resources>

    </Application.Resources>
</Application>

4K対応

このコードを通すためには、unsafe の許可を通さなければならない。

プロジェクトで「ビルド」-「アンセーフ コード」のチェックボックスにチェックを付けること。

Helpers ディレクトリに以下のファイル DpiHelper.cs を作成する。

namespace Memoir.Helpers;

public static partial class DpiHelper
{
    /// <summary>
    /// DpiAwarenessを設定する
    /// </summary>
    public static void EnablePerMonitorDpiAwareness()
    {
        // アプリケーションをDPI Awareに設定
        if (Environment.OSVersion.Version.Major >= 6 && Environment.OSVersion.Version.Minor >= 3)
        {
            // 新しい SetProcessDpiAwareness
            SetProcessDpiAwareness(ProcessDpiAwareness.ProcessPerMonitorDpiAware);
        }
        else
        {
            // Windows 8.1以前の場合は、SetProcessDpiAwareを使用する(非推奨)
            SetProcessDPIAware();
        }
    }

    /// <summary>
    /// 新しい DPI Aware で使う引数
    /// </summary>
    private enum ProcessDpiAwareness
    {
        ProcessDpiUnaware = 0,
        ProcessSystemDpiAware = 1,
        ProcessPerMonitorDpiAware = 2
    }

    /// <summary>
    /// 古い DPI Aware
    /// </summary>
    /// <returns></returns>
    [LibraryImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static partial bool SetProcessDPIAware();

    /// <summary>
    /// 新しいDPI Aware
    /// </summary>
    /// <param name="awareness"></param>
    /// <returns></returns>
    [LibraryImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static partial bool SetProcessDpiAwareness(ProcessDpiAwareness awareness);
}

DIの設定

App.xaml.cs への改変

    public partial class App : Application
    {
        /// <summary>
        /// サービスの登録をします
        /// </summary>
        public App()
        {
            Services = ConfigureServices();
            Ioc.Default.ConfigureServices(Services);
        }

        /// <summary>
        /// 現在の App インスタンスを取得します
        /// </summary>
        public new static App Current => (App)Application.Current;

        /// <summary>
        /// サービスプロバイダです
        /// </summary>
        public IServiceProvider Services { get; }

        /// <summary>
        /// サービスを登録します
        /// </summary>
        /// <returns></returns>
        private static ServiceProvider ConfigureServices()
        {
            var services = new ServiceCollection();

            services.AddSingleton<IMessenger, WeakReferenceMessenger>();
            services.AddSingleton<IMainWindowViewModel, MainWindowViewModel>();

            return services.BuildServiceProvider();
        }
    }

データバインディング

次に ViewModels\MainWindowViewModel.cs を作成して以下の内容にする。

namespace Memoir.ViewModels;

public interface IMainWindowViewModel { }

public class MainWindowViewModel : IMainWindowViewModel
{

}

ここで App.xaml.cs に以下を追記する

using Memoir.ViewModels;

ウィンドウのタイトル名を変えるなら、MainWindow.xaml で直接「MainWindow」を書き換えるか、データバインディングすること。

MainWindow.xaml.cs をこうする。

using System.Windows;
using CommunityToolkit.Mvvm.DependencyInjection;
using Memoir.ViewModels;

namespace Memoir;

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = Ioc.Default.GetService<IMainWindowViewModel>();
    }
}