しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

続・Xamarin でプラットフォームごとに別ファイルでリソースを XAML 定義したい

前回の投稿 ではプラットフォームごとに別 XAML ファイルでリソースを定義してみましたが、まだ生ぬるい!

共通コードの中でプラットフォームの分岐処理が含まれてしまっていたのでこれを排除しようと思います

f:id:matatabi_ux:20150108214201p:plain

今回改修するクラスは上記の青網掛けの部分

まずは 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");
        }
    }
}

名前空間iOS なのがポイント!

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 インタフェースのインスタンス解決を行い、テーマリソースを読み込むようにしています

これでプラットフォームごとの分岐処理が消えてスッキリ!

お決まりの動作確認!

f:id:matatabi_ux:20141205080512p:plain

動作には影響せず分岐処理の排除ができました!