C#Xamarin.Formsでスマホ開発#29 Pickerでデータバインディングする方法

当サイトではアフィリエイト広告を利用しています。

Xamarin.Formsでスマホアプリ開発

Pickerのデータバインディングとは?

今回も前回に引き続き,Pickerに関する解説をしていきます。前回は文字列のリストを表示して選択するという実装をしていました。しかしそれでは実際の開発現場ではほとんど使えないのではないかなと思います。今回の例でいうとTel,Fax,Mailを選択するPickerですが,これをデータベースなどでデータを保存する場合は,多くの場合「文字」ではなく数値等の「区分」で保存されることになると思います。要するにTelなら「1」,Faxなら「2」,Mailなら「3」という感じで,値に意味を持たせ,データベースには区分のみを保存して,表示するときは,アプリケーションで加工して「Tel」などの文字にするという設計をします。そうしておけば,「Tel」は「電話」と表現することに変更されたとしても,内部での比較は「1」で行うコーディングを変更する必要はありません。

というわけで,コンボボックス的なこのPickerというコントロールに表示される文字も,表示は文字でよいのですが,内部的には区分を保持し,その区分で処理ができるようになっていたほうが良いと考えるのが開発者としては当然の流れかと思います。

準備

今回はPickerBindablePageというコンテンツページを作成して検証していきたいと思います。ここまで順番に解説をよんでいただいている方は,PickerBindablePage という新規のコンテンツページを作って,ControlsPageから呼び出せるようにしてください。ここから初めて読んでいる方は,Xamarin.Formsのデフォルトで作成されるMainPageにPickerBindablePageの実装をして問題ありません。

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

using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using XSample.Objects;

namespace XSample.Pages
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class ControlsPage : ContentPage
    {
        public ControlsPage ()
        {
            InitializeComponent ();

            var items = new List<MenuDto>();
            items.Add(new MenuDto("SwitchPage", "Switchの使い方", "mail.png"));
            items.Add(new MenuDto("PickerBindablePage", "PickerをIDで識別などのためのバインディング", "mail.png"));
            MyListView.ItemsSource = items;
        }

        private void MyListView_ItemTapped(object sender, ItemTappedEventArgs e)
        {
            var item = e.Item as MenuDto;
            if(item.Title == "SwitchPage")
            {
                Navigation.PushAsync(new SwitchPage());
            }
            else if (item.Title == "PickerBindablePage")
            {
                Navigation.PushAsync(new PickerBindablePage());
            }
        }
    }
}

PickerBindablePageの追加

Pagesフォルダーに新規追加でPickerBindablePageという名前のコンテンツページを作成してください。作成されたらデフォルトのラベルを削除して,次のように記述します。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XSample.Pages.PickerBindablePage">
    <ContentPage.Content>
        <StackLayout>

            <Picker Title="連絡方法"
                    TextColor="Blue"
                    ItemDisplayBinding="{Binding Name}"
                    x:Name="MyPicker"
                    SelectedIndexChanged="MyPicker_SelectedIndexChanged"/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

Title

タイトルに「連絡方法」という文字を入れています。これで,Pickerに何も入力していないときに薄いグレーで「連絡方法」と表示されます。ユーザーはこのエリアに何を入力するかがわかるので非常に有効な表示になります。

TextColor

リストで選択された文字列の色を指定します。黒以外がよければ指定してください。

ItemDisplayBinding

この後コードビハインド側でデータバインドをします。そこでは「ID」と「Name」のクラスをバインドするのですが,そのクラスの中で,何をリストの文字列として表示するかを設定します。今回はNameを表示したいので「Binding Name」としています。

Name

コードビハインド側で操作するために名前をMyPickerとしています。

コードビハインド側の実装

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

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace XSample.Pages
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class PickerBindablePage : ContentPage
    {
        private ObservableCollection<PickerDto> _dtos = new ObservableCollection<PickerDto>();
        public PickerBindablePage()
        {
            InitializeComponent();

            _dtos.Add(new PickerDto(1, "TEL"));
            _dtos.Add(new PickerDto(2, "FAX"));
            _dtos.Add(new PickerDto(3, "Mail"));
            MyPicker.ItemsSource = _dtos;
        }

        private async void MyPicker_SelectedIndexChanged(object sender, EventArgs e)
        {
            var item = MyPicker.SelectedItem as PickerDto;
            if(item != null)
            {
                await DisplayAlert("Id", item.Id.ToString(), "OK");
                await DisplayAlert("Name", item.Name.ToString(), "OK");
            }
        }
    }

    public class PickerDto
    {
        public PickerDto(int id, string name)
        {
            Id = id;
            Name = name;
        }

        public int Id { get; set; }
        public string Name { get; set; }
    }
}

PickerDto

まずは「PickerDto」というクラスを作ります。今回はレクチャー用なので間借りして「PickerBindablePage」に書いていますが,通常は1クラス1ファイルがコーディングルールになっているチームが多いと思うので,こういう書き方はNGです。本来は意味のあるフォルダーにPickerDtoは入れましょう。ここではIdとNameだけの簡単なクラスにしています。Nameには「Tel」や「Fax」などの文字を入れる想定で,Idにはそれを識別する区分を設定します。

ObservableCollection

最初に「_dtos」という名前のObservableCollectionを宣言しています。型は「PickerDto」です。この_dtosにPickerDtoをリストしていきます。リストを作る処理はコンストラクタで行っています。「TEL」は区分「1」というようにPickerDtoを作成してAddしています。通常のList<T>ではなくObservableCollection<T>にしているのは,リストの中身が変更したときにデータバインドしているコントロールに逐一反応させるためです。List<T>をデータバインドすると,Listの中身が変更してもコントロールには反映されません。

ItemsSource

PickerDtoのリストを作成したらデータバインドするためにPickerのItemsSourceにリストを設定します。これでデータバインドされます。

SelectedIndexChanged

この状態で実行してもリストは表示されますが,正しくIdとNameを識別できているかを確認するためにSelectedIndexChangedを実装し,そこでメッセージを表示しています。PickerのSelectedItemに選択されているPickerDtoが格納されますが,型がobjectになっているため「as」キーワードを使ってPickerDtoに変換しています。うまく変換できなかったらnullが返却されるのでnullチェックをしたうえでPickerDtoの処理を行います。今回はメッセージでIdとNameを表示しています。これで選択された文字列と紐づくIdを識別できていることが確認できます。

【IdとNameが正しく取得できている】