なりたさまかく語りき

結局好きなこと書くのがいちばんよい。

(書きかけ)【Xamarin.Forms】MVVMっぽくPCLStorageでファイル操作する~その1~

ども。なりたさまだよ。

今回はNugetパッケージのPCLStorageを使ってファイル操作してみましたろぐです。 ファイルの読み書きは本来はアプリ固有領域でもローカルストレージでもプラットフォーム固有の実装が必要ですが、 PCLStorageを使うと共通プロジェクトからファイル操作を呼び出せます。 やってみよう。

TL;DR(Too Long,Don't Read/「長すぎて読むのやめた」から転じて要約の意)
  • ボタンを押すとファイル作成・ファイル全消去ができるだけのアプリつくった
  • MVVMだからボタンイベントじゃなくてCommand使った。
  • PCLStorage でピクルスのアイコンになってるのオシャレだと思う。

例によってソースはぎっはぶに置いてます。

GitHub - johta/FilePractice

うごき

どうでもいいけどはてなブログに動画って直接上げられないんですね。今どきはリンクの共有で事足りるから要らないと判断したってことらしい。普通に不便ですね。

View

はボタンの配置とListView定義してるだけなので省略。

ViewModel

 [AddINotifyPropertyChangedInterface]
    class MainPageViewModel
    {
        //ローカルプロパティの宣言
        public ICommand CreateCommand { get; }
        public ICommand DeleteCommand { get; }
        public ObservableCollection<String> Files { get; set; }
        public string FileText { get; set; }


        /// <summary>
        /// コンストラクタ
        /// </summary>
        public MainPageViewModel()
        {
            //コマンドを初期化
            CreateCommand = new CreateCommand(CreateFile);
            DeleteCommand = new DeleteCommand(DeleteFiles);

            //表示のリフレッシュ
            Initialize();
        }

        /// <summary>
        /// ファイル作成と表示のリフレッシュ
        /// </summary>
        private async void CreateFile()
        {
            var localStorage = FileSystem.Current.LocalStorage;
            var time = DateTime.Now.ToString("yyyyMMddHHmmss");
            var filename = time + ".txt";
            var file = await localStorage.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
            await file.WriteAllTextAsync("Date:"+time);
            Initialize();
        }

        /// <summary>
        /// ファイル全消去と表示のリフレッシュ
        /// </summary>
        private async void DeleteFiles()
        {
            var localStorage = FileSystem.Current.LocalStorage;
            var files = localStorage.GetFilesAsync().Result;
            foreach (var file in files)
            {
               await file.DeleteAsync();
            }
            Initialize();
        }

        private async void Initialize()
        {
            var localStorage = FileSystem.Current.LocalStorage;
            var files = localStorage.GetFilesAsync().Result;
            Files = new ObservableCollection<string>() { };

            foreach (var file in files)
            {
                Files.Add(file.Name);
            }
        }
    }

    internal class CreateCommand : ICommand
    {
        private readonly Action _action;

        internal CreateCommand(Action action)
        {
            this._action = action;
        }

        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            this._action();
        }
    }
    internal class DeleteCommand : ICommand
    {
        private readonly Action _action;

        internal DeleteCommand(Action action)
        {
            this._action = action;
        }

        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            this._action();
        }
    }

PCLStorageについて

以下の記事が参考になります(おい) www.atmarkit.co.jp

今回はアプリ固有領域(Androidだとdata\data\プロジェクト名\filesかな?)の直下にファイルそのまま作成してます。

ファイル名は現在時刻を取得して拡張子つけたものにしてます。

CreateFileAsync()の第2引数CreationCollisionOptionは同名ファイルが存在するときの挙動を指定するもの。ReplaceExistingは既存のものと置きかえ。そのままですね。 今回だとファイル名は年月日時分秒までしか取得してないので、1秒間に何回もクリックすると同名ファイルが作成されては置き換わってます

WriteAllTextAsync("Date:"+time)はString型の引数の中身をファイルに書き込みます。なんかもうそのまんまだね。

ファイル削除はDeleteAsync()で出来ます。

Asyncと名前についてるとおり、ファイル取得も作成も削除も非同期メソッドです。async/awaitつけましょうってくらいか。

Commandについて

以下の記事が参考になります(おい) Xamarin.Formsで学ぶMVVM入門 MVVMって何よ?編 | 勝手にオザマリン

Commandを使わずボタンクリックイベントを扱う場合は以下のような感じでしょうか。 View(MainPage.xaml

 <Button Text="ファイル生成" Clicked="Button_Create"/>

コードビハインド(MainPage.xaml.cs)

public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void Button_Create(object sender, EventArgs e)
        {
            //ファイル作成処理
        }
    }

コードビハインドに処理を書くことになる(ViewModelでクリックイベントを購読するでも良いのかな)んですけど、それはMVVMじゃない、UIとロジックが分離できてないので美しくないと思います。今回はVMに全部書いてるんでそれはそれで美しくないですけども。 (オザマリンさんの記事だとV・VM・M・Commandまで細かくフォルダ分けしてるんでこれが本来あるべき姿でしょう。)

MVVMについて下手に語ると宗教戦争(しょうもない言い争い)になるのでこれ以上はやめときます。

Commandの使い方は

  1. ICommandを継承したカスタムCommandクラスを作る

  2. カスタムCommand(action)をVMのコンストラクタでインスタンス

  3. actionの中身をVM(M)で実装

  4. カスタムCommandをViewでBinding

まとめ

  • ファイル読み書きはPCLStorageを使うとプラットフォーム固有の実装しなくて済む
  • MVVMするならイベントじゃなくてCommand Binding
  • アイコンがピクルスはセンスある f:id:naritasamadayo:20180926024607p:plain

実はまだ序章。次はListViewの項目をタップするとファイルの中身を表示するページに切り替わるところをやりたいです。 そこまでできると簡易的なToDoリスト的なものができるかな。

ではでは。