こんにちは!
今日は ServiceProvider を使った ViewModelLocator パターンの実装例を、WPF アプリの最小構成でサクッと紹介します。
「まずは動くサンプルを見たい!」という方向けに、① App 起動時に DI コンテナを組み立てる → ② ViewModelLocator で公開 → ③ View でバインド の 3 ステップでまとめました。コピペで動くレベルにしてあるので、ぜひ手元のプロジェクトで試してみてください。
0. 動作確認環境
- Visual Studio 2022
- .NET9
- WPF
ソースコードはこちらに置いておきます。
1. App.xaml / App.xaml.cs ― ServiceProvider を用意する
<Application x:Class="blog_WPF_ViewModelLocator.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:blog_WPF_ViewModelLocator" StartupUri="MainWindow.xaml"> <Application.Resources> <vm:ViewModelLocator x:Key="Locator" xmlns:vm="clr-namespace:blog_WPF_ViewModelLocator" /> </Application.Resources> </Application>
using Microsoft.Extensions.DependencyInjection; using System.Windows; namespace blog_WPF_ViewModelLocator { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : Application { public static IServiceProvider Services { get; private set; } = null!; protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); var services = new ServiceCollection(); // ===== 依存登録はここにまとめる ===== services.AddTransient<MainViewModel>(); // =================================== Services = services.BuildServiceProvider(); } } }
ポイントは App.Services
に完成形の ServiceProvider を格納 しておくことです。これだけで「どのクラスからも DI を経由してインスタンス取得」ができるようになります。
2. ViewModelLocator.cs ― DI への“窓口”を作る
using Microsoft.Extensions.DependencyInjection; namespace blog_WPF_ViewModelLocator; /// <summary> /// XAML から ViewModel を解決するための案内所 /// XAML では StaticResource で呼び出すだけ /// </summary> public class ViewModelLocator { // MainWindow 用 public MainViewModel MainViewModel => App.Services.GetRequiredService<MainViewModel>(); // 別の画面が増えたら同じ書式で追加する // public SettingsViewModel SettingsViewModel => // App.Services.GetRequiredService<SettingsViewModel>(); }
App.Services.GetRequiredService<T>()
を使ってDIコンテナからViewModelを取ってきています。余計なロジックは一切不要で、継続的な保守がラクになります。
MainWindow.xaml ― 実際の View での使い方
<Window x:Class="blog_WPF_ViewModelLocator.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:blog_WPF_ViewModelLocator" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800" DataContext="{Binding MainViewModel, Source={StaticResource Locator}}"> <Grid> <TextBlock Text="{Binding Title}" FontSize="24" /> </Grid> </Window>
上記 1 行👇が ViewModel を“注入”している決め手 です。
DataContext="{Binding MainViewModel, Source={StaticResource Locator}}"
StaticResource Locator
で ViewModelLocator インスタンス を取得
Path=MainViewModel
で DI コンテナ経由の MainViewModel を解決
結果として DataContext
にセット——これだけで MVVM が成立します
MainViewModel.cs ― 一応ViewModelも貼っておきます
namespace blog_WPF_ViewModelLocator; public class MainViewModel { public string Title { get; set; } = "From ViewModel"; }
上記のコードで”From ViewModel”と表示されれば成功です!

4. ちょっと深掘り Q&A
疑問 | 回答 |
---|---|
デザインタイムで値が見えない… | Blend でデザイナー表示するときは DI が未初期化なので null になります。ViewModelLocator 内で DesignerProperties.IsInDesignTool を見てモック ViewModel を返すか、d:DataContext でデザイン専用 VM を指定するとラクです。 |
スコープ (Singleton/Transient) の使い分けは? | ViewModel は基本 Transient で都度生成、リポジトリや API クライアントは Singleton にするのが典型です。AddScoped は ASP.NET Core 向けの “HTTP1 リクエスト単位” なので WPF ではあまり出番ナシ。 |
Hot Reload 時に更新が反映されない | Hot Reload は既存オブジェクトを再利用するため、登録情報を変更したらアプリ再起動が安全です。 |
5. まとめ
- App 起動時に ServiceCollection→ServiceProvider を組む
- ViewModelLocator では
App.Services.GetRequiredService<T>()
で取り出すだけ - View 側は StaticResource+Binding で DataContext を設定
- デザインタイムや Hot Reload のクセを知っておくと開発がラク
「公式 DI を WPF でも普通に使える」と分かれば、ASP.NET Core や MAUI とのコード共有もしやすくなります。まずはこのテンプレートを貼り付けて、自分の ViewModel/Service を登録してみてください。