続・Xamarin でプラットフォームごとに別ファイルでリソースを XAML 定義したい
前回の投稿 ではプラットフォームごとに別 XAML ファイルでリソースを定義してみましたが、まだ生ぬるい!
共通コードの中でプラットフォームの分岐処理が含まれてしまっていたのでこれを排除しようと思います
今回改修するクラスは上記の青網掛けの部分
まずは IResources という名前でインタフェースを作ります
/// <summary> /// リソース定義 View のインタフェース /// </summary> public interface IResources { /// <summary> /// リソースを読み込む /// </summary> void Load(); }
そしてこのインタフェースを実装し、ContentView を継承する ResourcesBase という基底クラスを作ります
/// <summary> /// リソース定義 View の基底クラス /// </summary> public abstract class ResourcesBase : ContentView, IResources { /// <summary> /// リソースを読み込む /// </summary> public void Load() { // AppResources にテーマリソースを上書き・追加します foreach (var resource in this.Resources) { if (App.Current.Resources.ContainsKey(resource.Key)) { App.Current.Resources[resource.Key] = resource.Value; } else { App.Current.Resources.Add(resource.Key, resource.Value); } } } }
前回リソースを追加・上書きしていた処理をこちらに移しました
ThemeResources-iOS.xaml を下記のように修正
<?xml version="1.0" encoding="utf-8" ?> <local:ResourcesBase xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:XamarinUnityInjection;assembly=XamarinUnityInjection" x:Class="XamarinUnityInjection.iOS.ThemeResources"> <local:ResourcesBase.Resources> <ResourceDictionary> <x:String x:Key="HourHandColor">#FFFF8469</x:String> <x:String x:Key="MinuteHandColor">#FFFF461C</x:String> <x:String x:Key="SecondHandColor">#FFCC3816</x:String> <x:String x:Key="TickColor">#FF7F4234</x:String> <x:String x:Key="NumberColor">#FF7F230E</x:String> </ResourceDictionary> </local:ResourcesBase.Resources> </local:ResourcesBase>
こちらはコードビハインド
/// <summary> /// iOS 用テーマリソース定義 View /// </summary> public partial class ThemeResources : ResourcesBase { /// <summary> /// コンストラクタ /// </summary> public ThemeResources() { this.InitializeComponent(); } }
継承変えただけ
WinPhone も同様に
<?xml version="1.0" encoding="utf-8" ?> <local:ResourcesBase xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:XamarinUnityInjection;assembly=XamarinUnityInjection" x:Class="XamarinUnityInjection.WinPhone.ThemeResources"> <local:ResourcesBase.Resources> <ResourceDictionary> <x:String x:Key="HourHandColor">#FF08007F</x:String> <x:String x:Key="MinuteHandColor">#FF0D00CC</x:String> <x:String x:Key="SecondHandColor">#FF1000FF</x:String> <x:String x:Key="TickColor">#FF2C267F</x:String> <x:String x:Key="NumberColor">#FF584CFF</x:String> </ResourceDictionary> </local:ResourcesBase.Resources> </local:ResourcesBase>
同様にコードビハインド
/// <summary> /// Windows Phone 用テーマリソース定義 View /// </summary> public partial class ThemeResources : ResourcesBase { /// <summary> /// コンストラクタ /// </summary> public ThemeResources() { this.InitializeComponent(); } }
勘のいい方ならもうお気づきと思いますが、今回も Unity を使っちゃいます!
iOS の Main.cs に RegisterType を追加します
namespace XamarinUnityInjection.iOS { public class Application { // This is the main entry point of the application. static void Main(string[] args) { // アプリケーション起動処理 App.Current.OnLaunchApplication(); // テーマリソースを登録 App.Container.RegisterType<IResources, ThemeResources>(new ContainerControlledLifetimeManager()); // if you want to use a different Application Delegate class from "AppDelegate" // you can specify it here. UIApplication.Main(args, null, "AppDelegate"); } } }
Windows Phone の App.xaml.cs も同様に!
namespace XamarinUnityInjection.WinPhone { public partial class App : Application { /// <summary> /// Provides easy access to the root frame of the Phone Application. /// </summary> /// <returns>The root frame of the Phone Application.</returns> public static PhoneApplicationFrame RootFrame { get; private set; } /// <summary> /// Constructor for the Application object. /// </summary> public App() { // Global handler for uncaught exceptions. UnhandledException += Application_UnhandledException; // アプリケーション起動処理 XamarinUnityInjection.App.Current.OnLaunchApplication(); // テーマリソースを登録 XamarinUnityInjection.App.Container.RegisterType<IResources, ThemeResources>(new ContainerControlledLifetimeManager()); DisplayProperties.AutoRotationPreferences = DisplayOrientations.Landscape | DisplayOrientations.LandscapeFlipped | DisplayOrientations.Portrait | DisplayOrientations.PortraitFlipped; // Standard XAML initialization InitializeComponent(); ~ 中略 ~ } } }
あとは Xamarin.Forms の共通コード側、App.xaml.cs を修正
/// <summary> /// 共通 アプリケーションクラス /// </summary> public partial class App : AppBase { /// <summary> /// アプリケーションクラス参照 /// </summary> public static readonly App Current; /// <summary> /// アプリ内で管理するモジュールのコンテナ /// </summary> public static readonly UnityContainer Container = new UnityContainer(); /// <summary> /// コンストラクタ /// </summary> static App() { Current = new App(); } /// <summary> /// コンストラクタ /// </summary> private App() : base() { this.InitializeComponent(); // Prism.Mvvm.Xamarin アセンブリを読み込み可能にするおまじない var autoWired = ViewModelLocator.AutoWireViewModelProperty.DefaultValue; } /// <summary> /// アプリケーション起動処理 /// </summary> public void OnLaunchApplication() { // 画面状態監視サービス を DI コンテナに登録 Container.RegisterType<IPageStateDetectService, PageStateDetectService>(new ContainerControlledLifetimeManager()); // ViewModel をインスタンス化するデフォルトメソッドを指定します ViewModelLocationProvider.SetDefaultViewModelFactory((type) => Container.Resolve(type)); App.Container.RegisterType<NumberItemViewModel>(new InjectionProperty("Number", 0)); } /// <summary> /// メイン画面を取得します /// </summary> /// <returns>メイン画面</returns> public static Page GetMainPage() { // テーマリソース読み込み App.Container.Resolve<IResources>().Load(); return new TopPage(); } }
GetMainPage() メソッド内で IResources インタフェースのインスタンス解決を行い、テーマリソースを読み込むようにしています
これでプラットフォームごとの分岐処理が消えてスッキリ!
お決まりの動作確認!
動作には影響せず分岐処理の排除ができました!