shuhelohelo’s blog

Xamarin.Forms多めです.

listview内のitem内のイベントをbindingで扱う方法

https://blog.verslu.is/xamarin/xamarin-forms-xamarin/databinding-scope-in-xamarin-forms/?utm_source=rss&utm_medium=rss&utm_campaign=databinding-scope-in-xamarin-forms

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>

docs.microsoft.com

なるほど.

ソースコード

github.com