listview内のitem内のイベントをbindingで扱う方法
MVVMパターンでアプリケーションを作っている場合,イベントに対する処理をViewModelでCommandを使って定義して,それをバインディングして使う.
ListViewやCollectionView内の各アイテムが選択されたときのCommandをバインディングするときに,以下のように指定するとViewModelのCommandにはバインドされない.
<?xml version="1.0" encoding="utf-8" ?> <ContentPage x:Class="XFCollectionViewCommand.MainPage" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:d="http://xamarin.com/schemas/2014/forms/design" xmlns:local="clr-namespace:XFCollectionViewCommand" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Name="Root" mc:Ignorable="d"> <ContentPage.BindingContext> <local:MainPageViewModel /> </ContentPage.BindingContext> <Grid> <CollectionView ItemsSource="{Binding People}" SelectionMode="Single"> <CollectionView.ItemsLayout> <LinearItemsLayout Orientation="Vertical" /> </CollectionView.ItemsLayout> <CollectionView.ItemTemplate> <DataTemplate> <StackLayout> <StackLayout.GestureRecognizers> <TapGestureRecognizer Command="{Binding TappedCommand}"/> </StackLayout.GestureRecognizers> <Label Text="{Binding .}" /> </StackLayout> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </Grid> </ContentPage>
これは,ListView内のアイテムにバインドされるのはListViewにバインドされたコレクションの中の1アイテムだから.
CommandはViewModel内に定義されている.
ListViewのアイテムからViewModelのCommandをバインドする方法は2つある.
1つはBindingのSourceでContextPageのBindingContextを参照するようにする.この場合は,ContextPageのx:Nameで名前を設定(ここではx:Name="Root"とした)して,それをReferenceで参照する.
<?xml version="1.0" encoding="utf-8" ?> <ContentPage x:Class="XFCollectionViewCommand.MainPage" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:d="http://xamarin.com/schemas/2014/forms/design" xmlns:local="clr-namespace:XFCollectionViewCommand" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Name="Root" mc:Ignorable="d"> <ContentPage.BindingContext> <local:MainPageViewModel /> </ContentPage.BindingContext> <Grid> <CollectionView ItemsSource="{Binding People}" SelectionMode="Single"> <CollectionView.ItemsLayout> <LinearItemsLayout Orientation="Vertical" /> </CollectionView.ItemsLayout> <CollectionView.ItemTemplate> <DataTemplate> <StackLayout> <StackLayout.GestureRecognizers> <TapGestureRecognizer Command="{Binding Path=BindingContext.TappedCommand, Source={Reference Root}}" /> </StackLayout.GestureRecognizers> <Label Text="{Binding .}" /> </StackLayout> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </Grid> </ContentPage>
class MainPageViewModel { public List<string> People { get; set; } public MainPageViewModel() { People = new List<string>(); People.AddRange(new[] { "person1", "person2", "person3", "person4", }); TappedCommand = new Command(OnTapped); } private void OnTapped(object obj) { //タップされたときの処理 } public Command TappedCommand { get; } }
Xamarin.Forms 4.3以降であれば,RelativeSource
を使える.
<StackLayout.GestureRecognizers> <!--<TapGestureRecognizer Command="{Binding Path=BindingContext.TappedCommand, Source={Reference Root}}" />--> <TapGestureRecognizer Command="{Binding Source={RelativeSource AncestorType={x:Type local:MainPageViewModel}}, Path=TappedCommand}" /> </StackLayout.GestureRecognizers>
なるほど.