shuhelohelo’s blog

Xamarin.Forms多めです.

ListViewの中でBindingContext内の他のプロパティをバインディングするには

docs.microsoft.com

例えば以下のように,ListView(CollectionView,CarouselViewなど)にバインディングするListとその他のプロパティがあるとします.

    public partial class MainPage : ContentPage
    {
        public int ImageWidth { get; set; }
        public int ImageHeight { get; set; }

        public List<Person> People { get; set; }

        public MainPage()
        {
            InitializeComponent();

            People = new List<Person>();

            for (int i = 0; i < 50; i++)
            {
                People.Add(new Person
                {
                    FirstName = $"FirstName{i}",
                    LastName = $"LastName{i}",
                    AvatarUri = $"avatar_men_{i}",
                });
            }

            ImageWidth = 10;
            ImageHeight = 10;

            BindingContext = this;
        }
    }

ListViewの項目の中でImageコントロールで画像を表示させるときに,画像の幅と高さを統一するためにImageWidthImageHeightを使いたいとします.

    <ListView HasUnevenRows="True" ItemsSource="{Binding People}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <StackLayout>
                        <Image
                            HeightRequest="{Binding ImageHeight}"
                            HorizontalOptions="Center"
                            Source="{Binding AvatarUri}"
                            VerticalOptions="Center"
                            WidthRequest="{Binding ImageWidth}" />
                        <Label Text="{Binding FirstName}" />
                        <Label Text="{Binding LastName}" />
                    </StackLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

それぞれ10としているのでかなり小さく表示されるはずですが,ごらんのとおりバインディングはきいていません.

f:id:shuhelohelo:20191212204320p:plain

これは当然,ListViewにバインディングされているのはPerson型のオブジェクトなので,その中にImageWidthとかいうプロパティがないからです.

このようにListViewの中で他のプロパティをバインディングしたい場合は,RelativeSourceを使って先祖(親とか親の親とか)にバインディングされているオブジェクトまで遡ってプロパティを指定します.

                        <Image
                            HeightRequest="{Binding ImageHeight, Source={RelativeSource AncestorType={x:Type ContentPage}}}"
                            HorizontalOptions="Center"
                            Source="{Binding AvatarUri}"
                            VerticalOptions="Center"
                            WidthRequest="{Binding ImageWidth, Source={RelativeSource AncestorType={x:Type ContentPage}}}" />

指定のバリエーションは色々あるので公式ドキュメントなどを参照するとして,今回の例で言えばContentPageのBindingContextにMainPageを入れているので,以下のようにContentPageまで遡ってImageWidthImageHeightを参照しています.

Source={RelativeSource AncestorType={x:Type ContentPage}}

f:id:shuhelohelo:20191212204410p:plain

もう一つの方法

調べてみたら当然のようにかずきさんのブログが見つかって,こっちのほうが直感的でわかりやすいと感じました.

blog.okazuki.jp

ContentPageに名前をつけて,そのBindingContextからプロパティを参照するという方法です.

<ContentPage
    x:Class="RelativeBindingTest.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    x:Name="Root"
    mc:Ignorable="d">

    <ListView HasUnevenRows="True" ItemsSource="{Binding People}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <StackLayout>
                        <Image
                            HeightRequest="{Binding Source={x:Reference Root}, Path=BindingContext.ImageHeight}"
                            HorizontalOptions="Center"
                            Source="{Binding AvatarUri}"
                            VerticalOptions="Center"
                            WidthRequest="{Binding Source={x:Reference Root}, Path=BindingContext.ImageWidth}" />
                        <Label Text="{Binding FirstName}" />
                        <Label Text="{Binding LastName}" />
                    </StackLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage>

相対指定よりもわかりやすいのでおすすめです.