本文は、Vicky&Jamesが2024年10月22日に韓国Microsoft本社BMWミートアップで行った講演内容を再編集したものです。このワークショップでは、XAMLベースの各種プラットフォーム、クロスプラットフォーム戦略、そして効果的なプロジェクトアーキテクチャ設計に必要な中核技術について深く掘り下げました。
紹介
こんにちは、韓国と日本のMicrosoft MVP夫妻のVicky&Jamesです。私たちはWPFを皮切りに、Uno Platformを含むXAMLベースのフレームワークやプロジェクトアーキテクチャ設計に深い関心と経験を持っています。各種プロジェクトのソースコードは、私たちのGitHubリポジトリでご確認・ダウンロードいただけます: GitHub - jamesnetgroup

目次
- XAMLプラットフォームとクロスプラットフォームの概要
- クロスプラットフォームを考慮した.NETバージョン選択戦略
- ViewとViewModelの接続戦略分析
- フレームワーク設計に必要な機能と実装方式
- 他プラットフォームでのWPF技術の効果的な活用戦略
- 分散プロジェクト管理のBootstrapper設計方法論
- デスクトップクロスプラットフォームでWPF技術を最大限に活用する戦略
1. XAMLプラットフォームとクロスプラットフォームの概要
XAMLは、UIを宣言的に定義するためのマークアップ言語であり、複数のプラットフォームで使用されています。XAMLはオブジェクト(すなわちクラス)からなる階層構造を持ち、開発者はオブジェクト指向でUIを設計・管理できます。この構造により、開発者が直接XAMLを扱うことは自然なことです。
WPFが登場した当初は、開発者とデザイナー間のコラボレーションが強調されましたが、実際にはXAML領域も通常は開発者が担当します。これは、XAMLが単なるデザインではなく、オブジェクトベースの階層構造を形成し、複雑なカスタムコントロールの実装においても重要な役割を果たすからです。この開発者向けの設計アプローチにより、XAMLはWPFだけでなく、その後登場した多くのプラットフォームでも中核的なコンポーネントとなっています。
特に、WPFはすべてのXAMLベースのプラットフォームに大きな影響を与え、それらのプラットフォームにとって最も重要なリファレンスとなっています。
1.1 主要なXAMLフレームワーク
- WPF: Windowsデスクトップアプリケーション開発のための強力なフレームワークで、豊富なUIとグラフィックス機能を提供します。
- Silverlight: Webブラウザで動作するインターネットアプリケーション向けのプラットフォームで、現在はサポート終了。WPFの軽量版であり、プラグインとして動作しました。Web標準ポリシーの変更によりプラグインベースのWebプラットフォームが消滅し、Silverlight 2ではTriggerの欠点を補うために**VisualStateManager(VSM)**が導入されました。
- Xamarin.Forms: iOS、Android、Windowsをサポートするモバイルアプリ開発プラットフォーム。Monoベースの最初のXAMLクロスプラットフォームであり、Microsoftに戦略的に買収され、.NET Coreの基盤となりました。
- UWP (Universal Windows Platform): Windows 10以降で動作するアプリケーションを開発するためのプラットフォーム。Microsoft Storeへの登録が必要で、WinAPIの使用制限などの制約があります。WPFと同じカスタムコントロール設計をサポートします。
- WinUI 3: WindowsネイティブUIフレームワークで、最新のWindowsアプリ開発のための次世代UIプラットフォームです。UWPのすべての長所を継承しつつ、その制限を解決し、WPFの拡張性を取り入れています。
- MAUI (.NET Multi-platform App UI): .NET 6から導入されたクロスプラットフォームUIフレームワークで、単一プロジェクトでモバイルおよびデスクトップアプリを開発できます。
- Uno Platform: さまざまなプラットフォームでUWPおよびWinUIのAPIを使用できるフレームワークで、Web(WebAssembly)、モバイル、デスクトップをサポートします。ほぼすべてのプラットフォームをサポートし、WPFと同じカスタムコントロール設計を提供します。
- Avalonia UI: クロスプラットフォームでWPFスタイルのXAMLを使用できるオープンソースUIフレームワークです。WPFと同じカスタムコントロール設計をサポートし、独自の技術拡張によりさまざまなプラットフォームをサポートします。
- OpenSilver: レガシーSilverlightをOpenSilverに移行するために最適化されたオープンソースプラットフォームです。Silverlightとほぼ同じ方法で動作し、WPF開発者にも馴染みのある環境を提供します。
2. クロスプラットフォームを考慮した.NETバージョン選択戦略
クロスプラットフォームアプリケーション開発では、使用する.NETバージョンを慎重に選択する必要があります。これは、互換性、機能性、ターゲットプラットフォームのサポートに直接影響します。
2.1 .NETバージョンオプション
- .NET Framework: Windows専用で、主に既存のWPFやWinFormsアプリケーションに使用されます。
- .NET Standard 2.0 / 2.1: さまざまな.NET実装間の互換性を提供するために設計された標準です。
- .NET (Core) 3.0以上: Windows、macOS、Linuxをサポートするクロスプラットフォームの.NET実装で、最新機能とパフォーマンス改善が含まれています。
2.2 選択基準と考慮事項
クロスプラットフォームのサポートが必要な場合は、.NET Coreまたは最新の.NETを選択すべきです。既存の.NET Frameworkライブラリやパッケージとの互換性が重要な場合は、.NET Standard 2.0を使用します。最新の機能やパフォーマンス改善が必要な場合は、.NET 5以上を検討します。
また、クロスプラットフォームフレームワークは.NET 5.0から互換性を考慮しており、最新バージョンで継続的に機能改善が行われているため、最新の.NETバージョンを選択することをお勧めします。
戦略的提案:
- 共通ライブラリは
.NET Standard 2.0で記述し、最大の互換性を確保します。 - 各プラットフォーム用のプロジェクトを作成し、共通ライブラリを参照します。
- 可能であれば
.NET 6以上を使用し、最新の機能とパフォーマンス改善を活用します。
3. ViewとViewModelの接続戦略分析
MVVM(Model-View-ViewModel)パターンでは、ViewとViewModelの接続が核心部分です。接続方法の違いにより、MVVMの使用方法が大きく変わります。したがって、MVVMを使用する目的に応じてDataContextの割り当て方法を決定する必要があります。
3.1 従来のDataContext直接割り当て方式
コードビハインドでViewModelを作成し、ViewのDataContextに直接割り当てます。
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
利点:
- 実装がシンプルで直感的
- ViewModelの生成タイミングを明確に制御できる
- コンストラクタに必要なパラメータを渡せる
欠点:
- ViewとViewModelの間に強い結合が生じる
- 単体テスト時にViewModelをモックするのが難しい
- 依存性注入(DI)を活用しにくい
- DataContextの割り当てタイミングを直接指定する必要があり、一貫性を保つのが難しい場合がある
3.2 XAMLでViewModelインスタンスを作成
XAMLでDataContextを設定してViewModelをインスタンス化します。
<Window x:Class="MyApp.MainWindow"
xmlns:local="clr-namespace:MyApp.ViewModels">
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<!-- Window content -->
</Window>
利点:
- XAMLのIntelliSenseサポートによりバインディングエラーを減らせる
- デザイナーで実際のデータバインディングをプレビューできる
- ViewとViewModelの関係を明確に表現できる
欠点:
- ViewModel作成時に依存性注入を行いにくい
- 複雑な初期化ロジックやパラメータ渡しが制限される
- DataContextの割り当てタイミングが強制され、柔軟性が低下する
3.3 ViewModelの直接作成と依存関係の受け渡し
コードビハインドでViewModelを作成する際に、必要な依存関係を直接渡します。
public MainWindow()
{
InitializeComponent();
var dataService = new DataService();
var loggingService = new LoggingService();
DataContext = new MainViewModel(dataService, loggingService);
}
利点:
- ViewModel作成時に必要な依存関係を明確に渡せる
- 複雑な初期化ロジックを実装できる
- 実行時に柔軟にViewModelインスタンスを作成できる
欠点:
- ViewがViewModelの依存関係を知る必要がある
- 依存関係が増えるとコードビハインドが複雑になる
- ViewとViewModelの結合度が依然として高い
- DataContextの割り当てタイミングを直接指定する必要があり、一貫性を保つのが難しい場合がある
- DIを使ってプロジェクトを管理しないため、依存関係が混乱する可能性がある
3.4 依存性注入(DI)コンテナの活用
DIコンテナを使用してViewModelとその依存関係を管理することで、ViewとViewModelの結合度を下げます。
public MainWindow()
{
InitializeComponent();
DataContext = ServiceProvider.GetService<MainViewModel>();
}
利点:
- ViewとViewModelの結合度を下げる
- 依存関係を一元管理し、保守性を向上
- 単体テストが容易
- 実行時に依存関係を柔軟に変更できる
欠点:
- DIコンテナの初期設定が複雑になる可能性がある
- チームメンバーがDIパターンを理解する必要がある
- それでもDataContextにViewModelを直接作成する必要があり、割り当てタイミングの一貫性を保つのが難しい場合がある
- ViewModelをシングルトンとして管理するかインスタンスとして管理するか、Viewのライフサイクルを考慮する必要がある。明確なルールを策定し、厳守する必要がある
3.5 Viewの自動ViewModel作成戦略
上記の問題を解決するために、Viewが作成される規定のタイミングで、依存性注入を介してViewModelを作成しDataContextに割り当てる方法が考えられます。例えば、ContentControlベースのViewを設計し、ViewModelを自動的に作成する方法が効果的です。
public class UnoView : ContentControl
{
public UnoView()
{
this.Loaded += (s, e) =>
{
var viewModelType = ViewModelLocator.GetViewModelType(this.GetType());
DataContext = ServiceProvider.GetService(viewModelType);
};
}
}
利点:
- DataContext割り当てタイミングの一貫性を保てる
- ViewとViewModelの結合度を下げる
- ViewModelの作成と依存性注入が自動処理される
- Viewは自身がどのViewModelを使うべきかを知る必要がない
これはほぼ欠点のない方法であり、単一のViewを管理することで、タイミングと処理ロジックを統一できます。構造的な完成度と拡張性の面でも優れた設計が保証されます。
ただし、ViewとViewModelのマッピングが必要であり、Dictionaryやマッピングテーブルを使用して実装できます。これにより、ViewとViewModelの接続情報を一元管理できます。マッピングによる接続管理の方法については、後の「Bootstrapper設計方法論」で詳しく説明します。
4. フレームワーク設計に必要な機能と実装方式
アプリケーションアーキテクチャを設計する際には、「再利用性と拡張性」を考慮したフレームワークを構築することが重要です。そのために、依存性注入(DI)コンテナの使用は不可欠です。
4.1 依存性注入(DI)コンテナの使用
DIは現代のソフトウェア開発において不可欠なパターンであり、依存関係の管理と結合度の低減に大きく役立ちます。しかし、WPFのようなデスクトップアプリケーションでは、Webアプリケーションでよく使われるMicrosoft.Extensions.DependencyInjectionのようなDIコンテナが完全に適しているとは限りません。
4.1.1 Microsoft.Extensions.DependencyInjectionの使用と注意点
Microsoft.Extensions.DependencyInjectionは.NET標準のDIコンテナであり、.NET Foundationの標準と言えます。ASP.NET Core、EntityFrameworkCore、MAUIなど、さまざまなプラットフォームやフレームワークでほぼすべてのシステムで使用されており、Transient、Scoped、Singletonといったライフサイクル管理機能を提供します。
しかし、WPFでは、この標準DIのライフサイクルがWPFの実際の状況と完全に一致しない可能性があります。
注意点:
- WPFなどのデスクトップアプリケーションでは、
Scopedライフサイクルは不要な場合がある TransientやSingletonの概念はサービスやWebアプリケーション向けに設計されており、WPFでは一部の機能が適用できない可能性がある- 必要以上の複雑さをもたらす可能性があり、WPFの使用シナリオには、よりシンプルで軽量なDIコンテナが適している場合がある
もちろん、Transientのようなライフサイクルを使用しなくてもDIを使用することは可能ですが、これらのポイントを正確に理解することが重要です。
4.1.2 CommunityToolkit.MvvmのDI
CommunityToolkit.Mvvmは、Microsoft.Extensions.DependencyInjectionのようなDIを直接提供しません。これは、Microsoft.Extensions.DependencyInjectionとWPFのライフサイクル特性が完全に一致しない可能性があるためと考えられます。
しかし、CommunityToolkit.MvvmはIoc.Defaultを提供し、開発者が任意のDIコンテナを使用できるようにしています。System.IServiceProviderインターフェースを実装したDIコンテナであれば、登録して使用できます。
したがって、CommunityToolkit.Mvvmを使用する際はDIを選択できます。最もよく使われるDIの一つは間違いなくMicrosoft.Extensions.DependencyInjectionであり、PrismのようなDIを使用することも非常に効果的な組み合わせです。
4.1.3 DIコンテナを直接設計する利点
IServiceProviderインターフェースに基づいてDIを設計する方法は、CommunityToolkit.MvvmのIoc.Defaultに登録でき、内部機能の接続と互換性を実現できます。IServiceProviderはGetServiceなどの最も基本的な機能のみを要求するため、非常にシンプルなDIを実装できます。
利点:
- 必要な機能のみを含むシンプルなDIコンテナを実装し、プロジェクトの複雑さを軽減
- 内部でさまざまな機能を設計、制御、拡張できる
- フレームワーク全体のアーキテクチャとプロジェクト設計を正確に構築できる
- 特定のプラットフォームに依存しない統一されたDIコンテナを提供し、クロスプラットフォーム開発に有利
サンプルコード:
// IServiceProviderに基づくDIコンテナの実装
public class SimpleServiceProvider : IServiceProvider
{
private readonly Dictionary<Type, Func<object>> _services = new();
public void AddService<TService>(Func<TService> implementationFactory)
{
_services[typeof(TService)] = () => implementationFactory();
}
public object GetService(Type serviceType)
{
return _services.TryGetValue(serviceType, out var factory) ? factory() : null;
}
}
// DIコンテナの登録と使用
var serviceProvider = new SimpleServiceProvider();
serviceProvider.AddService<IMainViewModel>(() => new MainViewModel());
Ioc.Default.ConfigureServices(serviceProvider);
このようにIServiceProviderインターフェースに基づいてシンプルなDIコンテナを実装すれば、CommunityToolkit.MvvmのIoc.Defaultに登録でき、内部機能の接続と互換性を実現できます。Microsoft.Extensions.DependencyInjectionやPrismなどの主要なDIが重すぎると感じる場合、自分で直接実装するのは非常に魅力的な選択肢です。
注意:
IServiceProviderなどのSystem.ComponentModel標準に従わない場合、CommunityToolkit.MvvmのIocとの互換性が失われる可能性があります。しかし、CommunityToolkit.MvvmをMVVM関連のモジュールとしてのみ使用し、特定のプラットフォームやフレームワークに依存しない、より専門的で統一されたDIコンテナを作成することも可能です。これは、クロスプラットフォームなど複数のXAMLプラットフォームで共通して使用できるフレームワークを作成するのに非常に適しています。
5. WPF技術の他プラットフォームでの効果的な活用戦略
他のXAMLプラットフォームでWPFの強力な機能を最大限に活用するには、いくつかの歴史的背景と中核戦略を理解する必要があります。また、WPF技術を直接使用できるプラットフォームの特性を詳細に理解することも重要です。
5.1 プラットフォーム間の特徴と差異の理解
UWPとWinUI 3の違い: UWPはWindows 10専用プラットフォームであり、アプリストアの登録ガイドラインやWinAPIの制限などにより、WPFやWinFormsといった従来のプラットフォームとの互換性が低いものでした。そのため、WinUI 3が登場し、UWPのすべての長所を継承しつつ、その問題点を改善し、WPFのように自由度の高いプラットフォームへと発展しました。
Uno Platformデスクトップ版とWinUI 3の一貫性: Uno PlatformはWindows、macOS、Linuxのデスクトッププラットフォームをサポートし、WinUI 3の方法に完全に準拠しています。したがって、WinUI 3はUWPのコアライブラリを直接使用し、Uno Platformも同じくWinUI 3の方法を採用しているため、
Microsoft.*で始まるすべてのDLLライブラリを共有して使用できます。
これらのプラットフォーム間の特徴を理解することで、Uno Platform Desktopが非常に効率的で魅力的なプラットフォームであることがわかります。したがって、WPFとUno Platform間で技術を共有・変換する戦略は、WinUI 3やUWPとの密接な関係から、非常に効果的かつ効率的です。
5.2 VisualStateManager(VSM)の徹底活用
すべてのプラットフォームがWPFのTriggerを直接使用できるわけではないため、代替戦略が必要です。VisualStateManager(VSM)はこの問題を解決する上で中核的な役割を果たします。
VSMはSilverlight 2.0で導入され、Triggerの不足を補うために開発され、カスタムコントロールとXAML間の状態処理に最適化されました。その後、.NET 4.0でVSMはWPFにも導入され、WPFのButton、CheckBox、DataGrid、Windowなど、すべてのCustomControlの内部設計がTriggerからVSMに変更されました。
利点:
- Triggerを直接使用できないプラットフォームでも、VSMを介して同じ機能を実現できる
- UI状態管理とアニメーションを効果的に実装できる
- 異なるプラットフォームの異なる動作をVSMで統一できる
最終的に、VSMを集中的に使用することで、WPF、Uno Platform Desktop、WinUI 3、UWPなどのプラットフォーム上で同じXAMLとCustomControlを構築し、ソースコードを完全に共有できます。
5.3 IValueConverterの柔軟な活用
IValueConverterはデータバインディング時に値を変換できるインターフェースであり、プラットフォーム間の差異を抽象化するのに非常に役立ちます。
戦略的な使用:
- Triggerとほぼ同じ機能を実現・代替でき、シンプルで効果的なソースコードを記述できる
- 毎回Converterを作成する必要があり、再利用性の基準があいまいになりがちなので、再利用性を追求しすぎず、柔軟に使用することを推奨
- 再利用性がなくても直感的に使用し、明確な命名によって分岐を最小限に抑え、特化して使用する
限界と補完:
IValueConverterだけでは限界があるIValueConverterは単純な変換に適用し、複雑なシナリオの管理は負担になるため、そのような場合はVSMで解決すべき- 複雑な状態処理は
VisualStateManagerを使用するのが良い
要約すると、IValueConverterはVSMの不足を補完し、単純で直接的な変換作業には、再利用性を過度に追求せず、直感的かつ柔軟に使用すべきです。
6. 分散プロジェクト管理のBootstrapper設計方法論
アプリケーションが複雑になりモジュール化されるにつれて、初期化プロセスと依存関係管理がますます重要になります。Bootstrapperパターンは、これらの初期化ロジックを集中管理するのに非常に役立ちます。
すべてのプラットフォームはApplication設計を基本としていますが、各プラットフォームの特性と方法が異なるため、Application設計はそれぞれ異なります。したがって、すべてのプラットフォームで同じ開発方法を維持するために、Bootstrapper構造設計を使用することは非常に効果的です。
6.1 Bootstrapperの役割と必要性
Bootstrapperの機能:
- 依存性注入設定: DIコンテナを初期化し、必要なサービス、View、ViewModelを登録します。
- ViewとViewModelの接続管理: 依存性注入を介してViewを登録し、ViewとViewModel間のマッピングを管理します。
- 設定の一元管理: すべての設定をBootstrapperで管理し、アプリケーションプロジェクトはその役割のみを実行し、残りの機能実装はプロジェクトの分散化とモジュール化によって管理します。
- さらに、一元管理プロジェクトを柔軟に拡張でき、Applicationに影響を与えません。
利点:
- アプリケーションの初期化ロジックを分離することで、コードの可読性と保守性が向上します。
- プロジェクトの分散化とモジュール化により、機能実装を独立して開発できます。
- プラットフォーム間の構造的な差異を最小限に抑え、一貫したアーキテクチャを維持できます。
6.2 Bootstrapperの設計方案
サンプルコード:
namespace Jamesnet.Core;
public abstract class AppBootstrapper
{
protected readonly IContainer Container;
protected readonly ILayerManager Layer;
protected readonly IViewModelMapper ViewModelMapper;
protected AppBootstrapper()
{
Container = new Container();
Layer = new LayerManager();
ViewModelMapper = new ViewModelMapper();
ContainerProvider.SetContainer(Container);
ConfigureContainer();
}
protected virtual void ConfigureContainer()
{
Container.RegisterInstance<IContainer>(Container);
Container.RegisterInstance<ILayerManager>(Layer);
Container.RegisterInstance<IViewModelMapper>(ViewModelMapper);
Container.RegisterSingleton<IViewModelInitializer, DefaultViewModelInitializer>();
}
protected abstract void RegisterViewModels();
protected abstract void RegisterDependencies();
public void Run()
{
RegisterViewModels();
RegisterDependencies();
OnStartup();
}
protected abstract void OnStartup();
}
このような抽象化により、管理構造を明確に強調し、仮想メソッドでタイミングと順序を制御できます。これにより、柔軟な拡張と完成度の向上が可能になり、Applicationに影響を与えず、さまざまなプラットフォームで一貫した方法で動作します。
7. クロスプラットフォームデスクトップ環境でWPF技術を最大化する戦略
他のXAMLベースのクロスプラットフォームフレームワークでWPFで使用される技術とパターンを最大限に活用することで、開発効率を向上させることができます。
7.1 すべてのプラットフォームで動作するフレームワークの実現

Jamesnet.Coreは.NET Standard 2.0ベースのフレームワークであり、WPF、Uno Platform、WinUI 3で同じプロジェクト設計を実現できます。このフレームワークには以下の特徴があります。
- DI設計: IServiceProviderベースのDIコンテナを利用し、CommunityToolkit.Mvvmと連携可能。
- MVVM Bootstrapper: プロジェクトの初期化と依存性注入を一元管理。
- ViewとViewModel間の接続管理: レイヤー管理などを通じてViewとViewModelの結合度を低減。
- すべてのXAMLベースプラットフォームで統一動作。
- リポジトリのソースコードを直接参照し、デバッグ、機能実装、拡張、研究が容易。
利点:
- WPF、Uno Platform、WinUI 3のいずれで開発しても、同じアーキテクチャを維持できます。
Uno Platform Desktopを使用すると、macOSやLinuxで開発・実行できます。JetBrains Riderを使用してクロスプラットフォーム開発環境を構築できます。
7.2 実際の実装ケース分析
League of Legends クライアントリファクタリングプロジェクトでは、Jamesnet.Coreフレームワークを利用し、WPF、Uno Platform、WinUI 3といった異なるプラットフォームで同じコードベースとアーキテクチャを実現しています。




- WPF版: GitHub - leagueoflegends-wpf
- Uno Platform版: GitHub - leagueoflegends-uno
- WinUI 3版: GitHub - leagueoflegends-winui3
戦略的アプローチ:
- Jamesnet.Coreフレームワークにより、プロジェクト設計の統一性を維持。
- DIコンテナとBootstrapperを利用して、ViewとViewModelを一元管理。
VisualStateManager(VSM)を使用してTriggerを代替し、異なるプラットフォームで同じ方法でUI状態を管理。
成果:
- 97%以上のコード共有を達成し、他のプラットフォームへの拡張の可能性を最大化。
- さまざまなプラットフォームで一貫したユーザー体験と開発方法論を提供し、技術移行を容易に。
- プロジェクトの分散化、モジュール化、管理の集中化により、開発と保守のコストを大幅に削減。
- CustomControlのモジュール化開発により、リファクタリングと拡張性が向上し、GPT、ClaudeなどのAIアプリケーションにおいても、分散されたビューシステムがより効果的。
結論
WPFの技術とパターンは今なお強力であり、これらをクロスプラットフォーム開発に適用することで、開発効率とコードの再利用性を向上させることができます。特にJamesnet.Coreフレームワークを使用し、DIコンテナによる一元管理戦略とBootstrapperの導入は、ViewとViewModel間の結合度を低減し、保守性を高めるのに役立ちます。
また、VisualStateManagerとIValueConverterを積極的に使用することで、プラットフォーム間の差異を最小限に抑え、一貫した設計を維持できます。これらの戦略を通じて、WPFの基盤を超え、戦略的にクロスプラットフォーム技術の拡張を実現できます。
特に、UWP、WinUI 3、Uno Platformの間では、XAML関連のDLLが100%同じように使用されるため、これらのプラットフォーム間にはほとんど違いがありません。そのため、WPF開発者にとっては、Uno Platformデスクトップ版の使用は非常に効果的かつ戦略的です。なぜなら、WPFからUnoへの移行は数時間で完了し、WinUI 3への移行も非常に容易だからです。
今後、WPF技術とXAMLベースのフレームワークはさらに発展し、これらを利用したクロスプラットフォーム開発はますます重要になるでしょう。開発者はこれらのトレンドをしっかりと捉え、適切な戦略を策定し、高品質なアプリケーションを開発する必要があります。
参考
主要リポジトリ
- Jamesnet.Coreフレームワーク: GitHub - jamesnet.core
- すべてのXAMLベースプラットフォームで動作するフレームワークで、DI、MVVM、Bootstrapperなどの機能を提供します。
- League of Legends クライアントリファクタリングプロジェクト:
- WPF版: GitHub - leagueoflegends-wpf
- Uno Platform版: GitHub - leagueoflegends-uno
- WinUI 3版: GitHub - leagueoflegends-winui3
現在公開中のWPFチュートリアル(カスタムコントロール)
