 
Xamarin逆引きTips
Xamarin.FormsでListViewのコンテキストアクションを使用するには?
リストの1つをスライド(iOS)もしくは長押し(Android)されたらメニューを表示する「コンテキストアクション」の基本的な使い方を説明する。
 Xamarin.Formsのバージョン1.3以降で追加された機能の1つにListViewのコンテキストアクションがある。コンテキストアクションを使用すると、リストの一つをスライド(Androidでは長押し)することでメニューを表示させることができる。
 今回は、このListViewビュー(=コントロール)のコンテキストアクションの利用方法について解説する。
- *1 なお本Tipsは、Windows上でVisual Studio 2013を使用してXamarin.Forms開発をすることを前提としている(※編集部注: Mac上のXamarin Studioでも同様の手順で、本稿の内容が実現できることは確認している)。使用しているXamarin.Formsのバージョンは、プロジェクト作成時に利用されている「1.3.1.6296」である。
1. シナリオ
最初に、簡単なテキストのみを表示するリストビューの画面を作成する。そして、そのリストビューにコンテキストアクションを追加する方法について解説する。
また、応用として、コンテキストアクションから、リストの項目を操作する例についても紹介する。
2. Xamarin.Formsプロジェクトを作成する
メニューバーの[ファイル]-[新規作成]-[プロジェクト]から表示したダイアログで、[テンプレート]-[Visual C#]-[Mobile Apps]-[Blank App (Xamarin.Forms Portable)]を選択し、名前を「ContextActionsSample」として[OK]ボタンを押す。
3. リストビューの表示
最初に、コンテキストアクションを追加するためにリストビューを作成する。
テキストのみが表示されたリストビューを表示するには、App.csファイルを以下のように修正する。
| using System.Linq; using Xamarin.Forms; namespace ContextActionsSample {   public class App : Application {     public App() {       MainPage = new MyPage();     }     ……省略……   }   class MyPage : ContentPage {     public MyPage() {       var listView = new ListView {  // <-1         ItemsSource = Enumerable.Range(0, 50).Select(n => "item-" + n), // <-2       };       Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0);       Content = listView;           // <-3     }   } } | 
 ListViewコントロール(1)を生成し、ItemsSourceプロパティに0から始まって50個の文字列配列を渡している(2)。
 ListViewは、定義したメイン画面(=MyPage)で唯一のコントロールであり、画面全体を占めることになる(3)。
このコードを実行すると次のような画面になる。
 
 
4. データテンプレート
 リストビューのコンテキストアクションは、各セルの描画を担任するViewCellオブジェクトのプロパティとして追加されるため、データテンプレートの実装が必須となる。
 先のコードでは、データテンプレートを指定していないため、デフォルトの動作としてカスタムセルのTextCellオブジェクトが動作していた。ここでは、このTextCellオブジェクトに相当するテンプレートをあらためて実装することにする。
データテンプレートを指定するには、App.csファイルを以下のように修正する。
| ……省略…… namespace ContextActionsSample {   ……省略……   class MyPage : ContentPage {     public MyPage() {       var listView = new ListView {         ItemsSource = Enumerable.Range(0, 50).Select(n => "item-" + n),         ItemTemplate = new DataTemplate(() => new MyCell(this)), // <-1       };       Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0);       Content = listView;     }   }   class MyCell : ViewCell {     // <-2     public MyCell(MyPage myPage) {       var label = new Label {   // <-3         VerticalOptions = LayoutOptions.CenterAndExpand,       };       label.SetBinding(Label.TextProperty, new Binding("."));    // <-4       View = new StackLayout {  // <-5         Padding = 10,         Children = { label }       };     }   } } | 
 まずは、ViewCellクラスを継承した、テンプレートクラス(ここではMyCellとした)を定義する(2)。
 MyCellでは、Labelコントロールを1つ生成し(3)、その表示内容をカレントデータにバインドした(4)。
 なお、Labelコントロールは、TextCellをまねて、パディング(Padding)を少し取って表示した(5)。
 最終的に、定義したMyCellクラスのインスタンスは、ListViewのItemTemplateプロパティにセットされている(1)。
現時点では、このコードを実行しても、表示は以前と変わらない。
5. コンテキストアクション
 続いて、このMyCellクラスに、コンテキストアクションを追加していく。
コンテキストアクションを追加するには、App.csファイルを以下のように修正する。
| ……省略…… namespace ContextActionsSample {   ……省略……   class MyCell : ViewCell {     public MyCell(MyPage myPage) {       ……省略……       var actionDelete = new MenuItem {       // <-1         Text = "Delete",         Command = new Command(p => myPage.DisplayAlert("Delete",p.ToString(),"OK")), // <-2         IsDestructive = true,                 // <-3       };       actionDelete.SetBinding(MenuItem.CommandParameterProperty, new Binding("."));  // <-4       ContextActions.Add(actionDelete);       // <-5       var actionAdd = new MenuItem {          // <-6         Text = "Add",       };       actionAdd.SetBinding(MenuItem.CommandParameterProperty, new Binding("."));     // <-7       actionAdd.Clicked += async (s, e) => {  // <-8         var itemMenu = ((MenuItem) s);         await myPage.DisplayAlert(itemMenu.Text, (string)itemMenu.CommandParameter, "OK");       };       ContextActions.Add(actionAdd);          // <-9       View = new StackLayout {         Padding = 10,         Children = { label }       };     }   } } | 
 コンテキストアクションの実体は、メニューアイテムである。ここでは、例として2つのアクションを追加するため、MenuItemクラスのインスタンスを2つ生成している(16)。
 MenuItemオブジェクトのIsDestructiveプロパティをtrueにセットすることで(3)、メニューアイテムの表示が赤色になる(※ただし、iOSのみ)。
 コンテキストメニューを選択した際の動作については、2つの実装方法がある。その1つ目は、MenuItemオブジェクトのCommandプロパティにCommandクラスのインスタンスを指定する方法である(2)。そして2つ目は、MenuItemオブジェクトのClickedイベントに追加する方法である(8)。場面に応じて使い分けてほしい。
 最後に、生成したMenuItemオブジェクトは、ViewCellクラス(を継承したMyCellクラスのインスタンス)のContextActionsプロパティに追加することで有効となる(59)。
 なお、アクション発生時に、パラメーターとして表示されているテキストを受け取るため、MenuItemクラスのCommandParameterPropertyフィールドに、カレントデータをバインドしている(4)。
このコードを実行すると次のような画面になる。
 
  
  
 
【コラム】Windows Phoneのコンテキストアクション
Xamarin.Formsは、Visual Studioで使用する場合、Windows Phoneのプロジェクトも同時に作成可能だが、今回紹介しているコンテキストアクションは、Windows Phoneでは、図4のように表示される。なお、Windows Phoneの場合も、Androidと同じく長押しによりコンテキストアクションが実行される。
 
 
6. アイテムの操作
最後に、応用として、コンテキストアクションに応じて、リストのアイテムを操作する方法を解説する。
ここまでに作成したサンプルにおける[Add]メニューでリストのアイテムを追加、[Delete]メニューで削除となるように実装するには、App.csファイルを以下のように修正する。
| using System.Collections.ObjectModel; ……省略…… namespace ContextActionsSample {   ……省略……   class MyPage : ContentPage {     private ObservableCollection<string> _ar = new ObservableCollection<string>(Enumerable.Range(0, 50).Select(n => "item-" + n)); // <-1     public MyPage()     {       var listView = new ListView{         //ItemsSource = Enumerable.Range(0, 50).Select(n => "item-" + n),         ItemsSource = _ar, // <-2         ItemTemplate = new DataTemplate(() => new MyCell(this)),       };       Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0);       Content = listView;     }     public async void Action(MenuItem item) {      // <-3       var text = item.CommandParameter.ToString(); // <-4       if (item.Text == "Add") {                    // <-5         _ar.Insert(_ar.IndexOf(text) + 1, text + "-Add");       } else if (item.Text == "Delete") {            // <-6         _ar.RemoveAt(_ar.IndexOf(text));       }     }   }   class MyCell : ViewCell{     public MyCell(MyPage myPage){       ……省略……       var actionDelete = new MenuItem{         Text = "Delete",         //Command = new Command(p => myPage.DisplayAlert("Delete",p.ToString(),"OK")),         IsDestructive = true,        };       actionDelete.SetBinding(MenuItem.CommandParameterProperty, new Binding("."));       actionDelete.Clicked += (s, a) => myPage.Action((MenuItem)s);  // <-7       ContextActions.Add(actionDelete);       var actionAdd = new MenuItem{         Text = "Add",       };       actionAdd.SetBinding(MenuItem.CommandParameterProperty, new Binding("."));       //actionAdd.Clicked += async (s, e) => {        //  var itemMenu = ((MenuItem) s);       //  await myPage.DisplayAlert(itemMenu.Text, (string)itemMenu.CommandParameter, "OK");       //};       actionAdd.Clicked+=(s, a) => myPage.Action((MenuItem)s);       // <-8       ContextActions.Add(actionAdd);       ……省略……     }   } } | 
 ItemsSourceプロパティに指定していたリストのデータは、操作が可能になるようクラス変数に変更し、ObservableCollectionクラスのインスタンスに変更した(12)。
 また、MyPageクラスでは、コンテキストアクションに応じて、リストを操作するActionメソッドを新たに定義した(3)。Actionメソッドは、MenuItemオブジェクトを引数に取るが、このMenuItemオブジェクトのTextプロパティの値がアクションへの表示文字列(本稿の例では[Add]や[Delete])と等しいかを確認し(56)、アクション別にCommandParameterプロパティの値がリストの表示文字列(4)と一致しているものを対象・基準として、そのリストビューのデータを操作している。
 一方、MyCellクラスの方では、先の例で使用した、Commandプロパティへの指定やClickedイベントへの処理をいったん削除し、新たに、Antcionメソッドを呼び出すコードに修正した(78)。
このコードを実行すると次のような画面になる。
 
 
7. まとめ
今回は、リストビューにコンテキストメニューを追加する方法について解説した。実行画面を見て分かる通り、Xamarin.Formsのコンテキストアクションは、表示も呼び出し方法もプラットフォームごとに大きく異なるので、使用に関してはよく検討する必要がありそうだ。
【お知らせ】Japan Xamarin User Group
Japan Xamarin User Group(以下JXUG)というのをご存じだろうか。JXUGでは、Xamarinに関する、最新情報の提供や、情報交換などが行われている。また、JXUG Conferenceと称して、定期的にカンファレンスも行われている。
そして次のカンファレンスは、dotNetConf 2015 Japan with JXUGと合同で、2015年4月11日(土)の午後に品川(日本マイクロソフトの品川オフィス・セミナールーム)で開催される。今回は、筆者もXamarin.Formsに関する話をさせていただく予定である。Xamarinの生の情報を得るためにも良い機会になると思うので、時間の許す方は、ぜひ参加してみてほしい。
【過去の開催の様子】※以下では、本稿の前後を合わせて5回分(第42回~第46回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
 
43. MvvmCrossでコマンドバインディングをするには?
MvvmCrossでは、画面でのイベント発生をViewModelに通知するためにコマンドバインディングを使用する。iOS/Androidにおける、その基本的な実装方法を説明する。
 
44. 【現在、表示中】≫ Xamarin.FormsでListViewのコンテキストアクションを使用するには?
リストの1つをスライド(iOS)もしくは長押し(Android)されたらメニューを表示する「コンテキストアクション」の基本的な使い方を説明する。
 
46. Xamarin.FormsでWebビューを使用するには?
外部のWebページやローカルに配置されたHTMLコンテンツを簡単に表示できるWebViewコントロールをXamarin.Formsで使う方法を説明する。





 
 
