Xamarin.Formsで設定の読み書きをDependency Serviceを使用しないで行ってみる

Xamarin.Formsで設定の保存をGoogle先生に聞いてみると、一般的な手法(色々な人が解説してくれている)では、Dependency Serviceを使用するのが良さそうだと教えてくれます。

ただ、これはこれで良いけど、データとメソッドを一つのクラスにして、プラットフォーム別の差異を継承で処理しちゃっても良いんじゃね?
と思っちゃったので、ちょっと試してみました。

良い例題がパッと思い浮かばないので、そのままデータの保存で試してみます。


まずは、共通プロジェクトに設定情報を管理するクラスの抽象クラスを定義する。
[XFApp1\ASettingInfo.cs]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace XFApp1
{
	public abstract class ASettingInfo
	{
		/// <summary>ユーザーID</summary>
		public string UserId { get; set; }

		/// <summary>ユーザー名</summary>
		public string UserName { get; set; }

		/// <summary>保存</summary>
		public abstract void Save();

		/// <summary>読込</summary>
		public abstract void Read();
	}
}

そして、とりあえず共通プロジェクトのApp.csにstaticに宣言しちゃう!!
(なんでもかんでも、staticにしちゃダメよって言われそうですが・・・)
ついでに、テストに使用するテキストボックス(Entry)を2つと保存ボタンも定義して、
保存ボタンで入力値を保存して、プログラムの起動時に読み込む処理を記述しておきます。
[XFApp1\App.cs]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Xamarin.Forms;

namespace XFApp1
{
    public class App : Application
    {
	//設定情報保持用
	public static ASettingInfo	MySettingInfo		= null;

	//コントロール
	private Entry	txtUserId	= new Entry();
	private Entry	txtUserName	= new Entry();
	private Button	btSave		= new Button();

        public App()
        {
            // The root page of your application
            MainPage = new ContentPage
            {
                Content = new StackLayout
                {
                    VerticalOptions = LayoutOptions.Center,
                    Children = {
                         new Label {
                             HorizontalTextAlignment = TextAlignment.Center,
                             Text = "Welcome to Xamarin Forms!"
                         },
			 txtUserId,
			 txtUserName,
			 btSave
                     }
                }
            };

            //コントロールのプロパティを適当に・・・
            txtUserId.Placeholder = "ユーザーID";
            txtUserName.Placeholder = "ユーザー名";
            btSave.Text = "保存";
            btSave.Clicked += btSave_Clicked;
	}

	protected override void OnStart()
        {
            //設定を読み込む
            App.MySettingInfo.Read();

            //読み込んだ情報をセット
            txtUserId.Text = App.MySettingInfo.UserId;
            txtUserName.Text = App.MySettingInfo.UserName;
	}

	private void btSave_Clicked(object sender, EventArgs e)
	{
            //入力情報を保存
            App.MySettingInfo.UserId = txtUserId.Text;
            App.MySettingInfo.UserName = txtUserName.Text;
            App.MySettingInfo.Save();
	}
    }
}

次に、Droidプロジェクト側で、実際にインスタンスを作成するクラスを作成します。
内容としては、先ほどのASettingInfoを継承して、先ほどは抽象メソッドとしていた部分の実態を記述します。
※NuGetで、Newtonsoft.Jsonをインストールしておいて下さい。
[XFApp1.Droid\DependeSettingInfo.cs]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Newtonsoft.Json;

namespace XFApp1.Droid
{
	public class DependeSettingInfo : XFApp1.ASettingInfo
	{
		private const string	cFILE_NAME	= "SettingInfo.json";

		/// <summary>保存</summary>
		public override void Save()
		{
			var json = JsonConvert.SerializeObject(this);
			string FilePath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal) + "/" + cFILE_NAME;

			System.IO.File.WriteAllText(FilePath, json);
		}

		/// <summary>読込</summary>
		public override void Read()
		{
			string FilePath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal) + "/" + cFILE_NAME;

			string				json = null;
			DependeSettingInfo	RecvObj = null;

			if (System.IO.File.Exists(FilePath))
			{
				json = System.IO.File.ReadAllText(FilePath);
				RecvObj = JsonConvert.DeserializeObject<DependeSettingInfo>(json);
				this.UserId = RecvObj.UserId;
				this.UserName = RecvObj.UserName;
			}
		}

	}
}

そして、プログラムの開始時に、冒頭で宣言していたstatic変数にインスタンスをセットするようにしておきます。
[XFApp1.Droid\MainActivity.cs]

using System;

using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

namespace XFApp1.Droid
{
	[Activity(Label = "XFApp1.Droid", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
	public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
	{
		protected override void OnCreate(Bundle bundle)
		{
			base.OnCreate(bundle);

			global::Xamarin.Forms.Forms.Init(this, bundle);

			//設定操作クラスのインスタンスを生成
			App.MySettingInfo = new DependeSettingInfo();

			LoadApplication(new App());
		}
	}
}

これで、Droid側で設定の読み書きと参照を行う準備ができました。
早速試してみましょう(^^)/

初回起動時は、当然テキストボックスには何も表示されません。
f:id:shinya_dog:20160628160046j:plain

ユーザーIDとユーザー名を入力して、保存ボタンをクリックしてから、プログラムを再起動すると、
f:id:shinya_dog:20160628160308j:plain

期待していた通りに表示されました(*^o^*)


次は、iOS側の処理になりますが、先ほどのDependeSettingInfo.csはiOS側のプロジェクトでもそのまま動きます。
なので、Visual Studioの機能でリンクを貼ります。

iOSプロジェクトを右クリックして[追加]→[既存の項目]を選択
f:id:shinya_dog:20160628160748j:plain

先ほどDroid側に追加したDependeSettingInfo.csを、リンクとして追加します。
※もちろんリンクとして追加しなければならないということはありませんよ。
f:id:shinya_dog:20160628161038j:plain

そして、Droidの時と同じように、プログラムの開始時に、冒頭で宣言していたstatic変数にインスタンスをセットするようにしておきます。
[XFApp1.iOS\AppDelegate.cs]

using System;
using System.Collections.Generic;
using System.Linq;

using Foundation;
using UIKit;
using XFApp1.Droid;

namespace XFApp1.iOS
{
	// The UIApplicationDelegate for the application. This class is responsible for launching the 
	// User Interface of the application, as well as listening (and optionally responding) to 
	// application events from iOS.
	[Register("AppDelegate")]
	public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
	{
		//
		// This method is invoked when the application has loaded and is ready to run. In this 
		// method you should instantiate the window, load the UI into it and then make the window
		// visible.
		//
		// You have 17 seconds to return from this method, or iOS will terminate your application.
		//
		public override bool FinishedLaunching(UIApplication app, NSDictionary options)
		{
			global::Xamarin.Forms.Forms.Init();

			//設定操作クラスのインスタンスを生成
			App.MySettingInfo = new DependeSettingInfo();

			LoadApplication(new App());

			return base.FinishedLaunching(app, options);
		}
	}
}


同じようにテストしてみると、期待通りに動作しますよね?

UWPの場合は、Droidで作成したクラスをそのまま使う訳にはいきませんが、同じように抽象クラスを継承して作って、UWPで動作するように実装して、プログラム起動時にインスタンスがセットされるようにすれば、動作するはずです。



ここに書いた方法は、Xamarinの技術ではないのであえて書かなくても皆わかっていることかもしれませんが、Xamarinをキーワードに探すとあまり出てこない気がしますし、Xamarin初心者にとっては、動きそうだけど動くのかな?という不安があったのも事実なので、後に続く人の参考になればと書いておきました。

汎用的に使用するメソッドとかであれば、もちろんDependency Serviceを使用した方が良いと思いますが、方法論の一つとして、これも検討の価値はあるのではないかと思います。
もしも問題等あればご指摘下さい。