1.4K Views
December 09, 24
スライド概要
【登壇者募集中】.NET Conf 2024 後! C# Tokyo カンファレンス - connpass
https://csharp-tokyo.connpass.com/event/334045/
このイベントの発表スライドです。
WPFに最近の技術を色々取り込んで、見た目 や開発のしやすさを改善しようとしてみる 2024/12/9 須藤(suusanex)
自己紹介 ID:suusanex( connpass・Twitter・GitHub共通) 名前:須藤圭太 サイエンスパーク株式会社という独立系ソフトウェアベンダーに所属 4年ほど受託開発で、上流から下流まで全部を回す ここ10年ほどは、自社製品開発も担当 Windowsアプリ開発のネタが多い
概要 今年春のBuildでWPFとWin UI 3の併用の方針が出た 最新の体験ならWinUI 3チャレンジ、既存資産を生かすならWPF続投、という感じ 最新の機能や見た目などもどんどんWPFに取り込めるようになっている が、WPFのプロジェクトテンプレートは未だに真っ白 自力で最新のものを取り込むかどうかで、大違いになってきている どんなメリットがあって取り込みはどうすればいいかを、紹介します!
紹介する内容 WPFでもWin11っぽいUIを作れるFluentテーマ(要.NET 9) 色々新機能の前提としてGenericHost WPFでもWindowsAppSDK WindowsAppSDK アプリライフサイクル それを使って多重起動時のアクティブ化を実現 アプリライフサイクルとGenericHostは共存OK おまけ コンソールアプリでもGenericHost
WPFでもWin11っぽいUIを作れるFluent テーマ(要.NET 9) WPFは独自描画なので、OSのUIの変化に追従していない MahApps.Metro等のライブラリで何とかするか、WinUI 3に行くしか無かった ついに、容易に使える組み込みのFluentテーマが登場 ダークテーマにも対応 残念ながら.NET 9以降必須だが、使うのは簡単
取り込みは簡単
プロジェクトの.NETを9に変更
リソースディクショナリに次の1行を追加
(8以下だとPresentationFramework.Fluentをロードできなくて例外)
<ResourceDictionary
Source="pack://application:,,,/PresentationFramework.Fluent;component/Themes
/Fluent.xaml" />
せっかく配信なので、VisualStudioで動くところを見せます
色々新機能の前提としてGenericHost 最近の新機能を入れようとすると、サンプルで当たり前のように DI(Dependency Injection)が出てきます しかしWPFのテンプレートは真っ白で、そんな物は影も形もありません GenericHostを組み込めばその辺も解決できるので、まずそこの話から GenericHostはDIやLoggerなど開発に便利なインフラが全部入ってる ある意味デスクトップアプリにおける.NET Aspire TaskやLINQなどと同じく、使うのが基本であり、使わないのは古くて開発しづ らいコード、になってきている(個人的評価) オフィシャル情報: https://learn.microsoft.com/jajp/dotnet/core/extensions/generic-host?tabs=appbuilder
GenericHostの組み込み方 1,Template Studio for WPF テンプレートなので楽だが、Metro等色々入ってるので不要なら手動で消す必要あり .NET 6で止まっててだいぶコードが古い・・・(Nullableなど未対応) GenericHostだけではなく、MVVMのCommunity ToolkitやWindowsとPageの Navigataionの仕組みなど色々入っている 特に説明するほどでは無いので、こちらからインストールしてどうぞ https://marketplace.visualstudio.com/items?itemName=TemplateStudio.TemplateS tudioForWPF 2,手動で組み込み このセッションではこれをメインに説明します サンプルコード: https://github.com/suusanex/sample_wpf_generichost
WPFテンプレートに最小限GenericHost
ざっとポイントを書きますが、せっかく配信なので、VisualStudioで見せます
WPFのテンプレートに、まずGenericHostを追加
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
App.xamlのStartupUriは外して、StartupとExit追加、Startupでホスト生成
private IHost _host;
private async void OnStartup(object sender, StartupEventArgs e){
_host = Host.CreateDefaultBuilder(e.Args)
.Build();
await _host.StartAsync(); }
ホストになるクラスを追加。StartAsyncでメインウインドウをShow()
public class ApplicationHostService : IHostedService
DIのところでホストになるクラスを登録
.ConfigureServices(collection => {
collection.AddHostedService<ApplicationHostService>(); })
WPFでもWindowsAppSDK WinUI 3とセットで登場した、Windows開発をやりやすくするAPI群 そもそもWinUI 3使う機会少なくて長らく謎の存在(個人差があります) WPFでも使えるようになった! 最新Windowsらしい動きになるちょっとした便利機能も有る 一部採用するだけでもちょっとリッチになる ちょっとずつでも取り込んでいきたい 必要ランタイムが増えるなど、配布で考えることが増えるのは難点
WPFにWindowsAppSDK組み込み ざっとポイントを書きますが、せっかく配信なので、VisualStudioで見せます 開発環境にWindows App SDKをインストール Windows App SDK 用の最新のダウ ンロード - Windows apps | Microsoft Learn NuGetで組み込み・RuntimeIdentifiersを追加 WindowsPackageTypeに値Noneを追加して、普通のWPFの配布方法 (UnPackaged)かつWindows App SDKの初期化を自動で行う これが最小限。あとは必要な初期化コードを自動生成してくれる <PropertyGroup> <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework> <RuntimeIdentifiers>win-x64</RuntimeIdentifiers> <WindowsPackageType>None</WindowsPackageType> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.6.240923002" /> </ItemGroup>
WindowsAppSDK アプリライフサイクル アプリの開始・終了・アクティブ化などを統一的に管理できる仕組み 大きなポイントとして、同一exeから起動される複数のプロセスを「インスタ ンス」として管理できる 例えばこれを利用して、よく要望されるこういう動きを実現できる o o exeは多重起動禁止 o exeを多重起動した場合、すでに起動しているウインドウをアクティブにする 他にも使いどころはありそうだが、ここではその使い方を紹介
アプリライフサイクルで多重起動の動作
を実装
ざっとポイントを書きますが、せっかく配信なので、VisualStudioで見せます
アプリのStartupの処理でインスタンスを取得
var instances = AppInstance.GetInstances();
インスタンスの数が1以下だったらインスタンスのActivatedイベントを登録し、そ
こでメインウインドウをActivate
AppInstance.GetCurrent().Activated += OnActivated;
private void OnActivated(object? sender, AppActivationArguments e) {
Current.Dispatcher.Invoke(() => Current.MainWindow?.Activate()); }
インスタンスの数が1を超えていたら、1つ目のインスタンスにリダイレクトして
(Activationを呼び出して)アプリ終了
instances.First() .RedirectActivationToAsync(args);
サンプルコード: https://github.com/suusanex/sample_wpf_winappsdk
アプリライフサイクルとGenericHostは 共存OK アプリライフサイクルは初期化の流れに手が入るので他との共存が心配になる GenericHostとの共存も問題なし! やっていることのレイヤが違うので。 今日出てきたサンプルをくっつけるだけで行けます くっつけたものをVisualStudioで見せます
おまけ コンソールアプリでもGenericHost
WPFの話をしてきましたが、コンソールアプリを作る場合もGenericHostは使
いたい! → 使えます
ざっとポイントを書きますが、せっかく配信なので、VisualStudioで見せます
MainでGenericHostを開始
_ = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddHostedService<ConsoleHostedService>(); })
.RunConsoleAsync();
IHostApplicationLifetime.ApplicationStartedを登録してそちらで処理
public class ConsoleHostedService(IHostApplicationLifetime _AppLifetime) : IHostedService {
public async Task StartAsync(CancellationToken cancellationToken) {
_AppLifetime.ApplicationStarted.Register(OnStarted);
[STAThread]
private void OnStarted()
サンプルコード: https://github.com/suusanex/sample_console_generic_host
まとめ WPFの空っぽのテンプレートから、もっと最新技術を使えるように色々盛り込 む方法を紹介しました 皆さん、知らないものはもちろん、知ってても実装方法がピンとこなかったも のも有るのでは? このセッションの情報でハードルが下がって取り込みができ、開発効率が良く なったら嬉しいです 新しくて便利な物はどんどん取り入れて、効率よく開発して、価値に集中でき るようにしていきましょう!