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で使う方法を説明する。