Xamarin.FormsでもItemsControlを!

あすかです。

昨日指をやけどしてしまい、長時間プログラミングや文章が打てないので、今日は小話でお茶を濁します。

MVVMを勉強している方なら一度は考えたであろう、ItemsControlはXamarin.Formsで使えないかというおはなしです。

なんと、matatabi_ux様という、ネコ大好きなイケメンが作成したコードがあります。

matatabi-ux.hateblo.jp

今回は、上のソースを使わしていただき、勝手に改良もしてみます。
とっても便利なものなので、みなさん絶対使ってください!

早速使ってみる

MainViewModel.cs

こういうコレクションとコマンドを作ります。

MainPage.xaml

ItemsControlをXAMLから使ってみます。
Visual Studio 2017から、Xamarin.Formsプロジェクト作成時にMainPageをコードではなくXAMLで作ってくれるようになったのはありがたいところです。

実行!

f:id:kmynews:20170326104926p:plain

おお、動いてますね!並んでる!きれーい!

ItemsSourceのCollectionChangedについて

これ、「追加」ボタンがを押しても項目が増えないです。
コマンドのBindingはうまくいっているのになぜだ?と思ったのですが、ItemsControlのOnCollectionChangedメソッド内にある

この行がnullを返すようです。
つまり、ObservableCollection<string>と、ObservableCollection<object>の変換がうまくいっていなかったようです。
あと、INotifyCollectionChangedインターフェースが実装され、かつObservableCollectionではないコレクション(例:ObservableCollectionのラッパ)をバインディングする時にも困る可能性があります。

そこで、キャスト先をObservableCollectionから、共変性が認められており、かつforeachでも使えるIEnumerableに変えてみます。
IndexOfメソッドはないのでゴリ押しでいきます。

メソッド全体を以下のように変更してみます。元のところからの変更箇所は// ★をつけています。

もののみごと動きました。

f:id:kmynews:20170326110922p:plain

1ページ内に複数のItemsControlが置けない

まず状況を作ってみます。
ビューモデルで、2つのコレクションを作ります。

XAMLのほうも、2つのコレクションを同時に表示してみます。ItemsControlが2つあります。

これを実行してみますと、

f:id:kmynews:20170326111413p:plain

2つ目のコレクションだけが画面に表示されていますね。なんということでしょう。

これ、原因ですが、ItemsPanelの初期値となるStackLayoutを、static変数の中でnewしているためです。

ラムダ式ではないので、ItemsControlのクラスがロードされた時点でnewされたインスタンスをそのまま使いまわすことになります。
そこで、ここをおもいきってnullにしてみます。

ItemsSourceのデフォルト値もnullにしたいところですが、OnItemsPanelChangedOnItemsSourceChangedでお互いのインスタンスを参照しているため、NREが発生します。
なので、ItemsSourceのデフォルト値はそのままにして、コンストラクタで両方をnewします。

これを実行してみたら、2つのリストが表示できました!わーいわーい!ぱちぱち!

f:id:kmynews:20170326112501p:plain

まとめ

ItemsControlはいいぞ!使え!使え!オラ!
ItemsControlを作ってくださった方に感謝。