Xamarin.FormsでもItemsControlを!
あすかです。
昨日指をやけどしてしまい、長時間プログラミングや文章が打てないので、今日は小話でお茶を濁します。
MVVMを勉強している方なら一度は考えたであろう、ItemsControlはXamarin.Formsで使えないかというおはなしです。
なんと、matatabi_ux様という、ネコ大好きなイケメンが作成したコードがあります。
今回は、上のソースを使わしていただき、勝手に改良もしてみます。
とっても便利なものなので、みなさん絶対使ってください!
早速使ってみる
MainViewModel.cs
こういうコレクションとコマンドを作ります。
MainPage.xaml
ItemsControlをXAMLから使ってみます。
Visual Studio 2017から、Xamarin.Formsプロジェクト作成時にMainPageをコードではなくXAMLで作ってくれるようになったのはありがたいところです。
実行!
おお、動いてますね!並んでる!きれーい!
ItemsSourceのCollectionChangedについて
これ、「追加」ボタンがを押しても項目が増えないです。
コマンドのBindingはうまくいっているのになぜだ?と思ったのですが、ItemsControlのOnCollectionChangedメソッド内にある
この行がnullを返すようです。
つまり、ObservableCollection<string>
と、ObservableCollection<object>
の変換がうまくいっていなかったようです。
あと、INotifyCollectionChanged
インターフェースが実装され、かつObservableCollection
ではないコレクション(例:ObservableCollection
のラッパ)をバインディングする時にも困る可能性があります。
そこで、キャスト先をObservableCollection
から、共変性が認められており、かつforeach
でも使えるIEnumerable
に変えてみます。
IndexOf
メソッドはないのでゴリ押しでいきます。
メソッド全体を以下のように変更してみます。元のところからの変更箇所は// ★
をつけています。
もののみごと動きました。
1ページ内に複数のItemsControlが置けない
まず状況を作ってみます。
ビューモデルで、2つのコレクションを作ります。
XAMLのほうも、2つのコレクションを同時に表示してみます。ItemsControl
が2つあります。
これを実行してみますと、
2つ目のコレクションだけが画面に表示されていますね。なんということでしょう。
これ、原因ですが、ItemsPanel
の初期値となるStackLayout
を、static変数の中でnewしているためです。
ラムダ式ではないので、ItemsControlのクラスがロードされた時点でnewされたインスタンスをそのまま使いまわすことになります。
そこで、ここをおもいきってnullにしてみます。
ItemsSource
のデフォルト値もnullにしたいところですが、OnItemsPanelChanged
とOnItemsSourceChanged
でお互いのインスタンスを参照しているため、NREが発生します。
なので、ItemsSource
のデフォルト値はそのままにして、コンストラクタで両方をnewします。
これを実行してみたら、2つのリストが表示できました!わーいわーい!ぱちぱち!
まとめ
ItemsControlはいいぞ!使え!使え!オラ!
ItemsControl
を作ってくださった方に感謝。