takataka430’s blog

Xamarin.Formsを中心に勉強したことのまとめを書きます

【Xamarin.Forms】ページ間での値の受け渡し方法を考えてみた(コードビハインド編)

最終更新:2019年3月19日

Xamarin.Formsでページ間で値を受け渡しするいい方法がないかと色々考えてみました。今回は次のような簡単な状況を想定します。

最初のページ(MainPage)でEntryに値を入力する

次のページ(SecondPage)で、最初のページのEntryに入力した値をLabelに表示する
  
考えた方法は以下の3通りです。MainPageからSecondPageのどこに値を渡すかで場合分けしています。

1.コンストラクタに値を渡す
2.プロパティに値を渡す
3.メソッドに値を渡す
4.遷移先のページのBindingContextに渡す(2019/3/19 追記)

なお、1~3の画面は共通なので先に掲載します。

MainPage.xaml(値を渡すページ)

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:local="clr-namespace:XF_NavigationPage" 
             x:Class="XF_NavigationPage.MainPage">
    <StackLayout VerticalOptions="Center">
        <Entry x:Name="entry"
               HorizontalOptions="FillAndExpand" />
        <Button x:Name="button"
               Text="To Second Page"
               HorizontalOptions="Center"
               Clicked="Handle_Clicked" />
    </StackLayout>
</ContentPage>

     

SecondPage.xaml(値を受け取るページ)

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="XF_NavigationPage.SecondPage">
    <ContentPage.Content>
        <StackLayout VerticalOptions="Center">
            <Label Text="前のページから受け取った値"
                   HorizontalOptions="Center"/>
            <Label x:Name="label"
                   HorizontalOptions="Center"/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

1.コンストラクタに値を渡す

コードビハインドに以下のように記述します。
  
MainPage.xaml.cs

using System;
using Xamarin.Forms;

namespace XF_NavigationPage
{
    public partial class MainPage : ContentPage
    {

        public MainPage()
        {
            InitializeComponent();
        }

        void Handle_Clicked(object sender, EventArgs e)
        {
            var name = entry.Text;

            Navigation.PushAsync(new SecondPage(name));
        }
    }
}

  
SecondPage.xaml.cs

using Xamarin.Forms;

namespace XF_NavigationPage
{
    public partial class SecondPage : ContentPage
    {
        public SecondPage(string name)
        {
            InitializeComponent();

            label.Text = name;
        }
    }
}

NavigationPageでSecondPageのインスタンスを作る時にコンストラクタに値を渡しています。
それでは動きを見てみましょう。

f:id:takataka430:20190227224120g:plain:w200

うまくいっていますね!・・がこの方法では困ることがあります。SecondPageのXAMLのプレビューがエラーになるのです。

f:id:takataka430:20190227224330p:plain:w200

これでは画面を作る時に不便なので、この方法はやめたほうが良さそうです。

2.プロパティに値を渡す

それならSecondPageにプロパティを定義して、インスタンスを作る時にプロパティに値を渡せば良いのでは?と考えました。

MainPage.xaml.cs

using System;
using Xamarin.Forms;

namespace XF_NavigationPage
{
    public partial class MainPage : ContentPage
    {

        public MainPage()
        {
            InitializeComponent();
        }

        void Handle_Clicked(object sender, EventArgs e)
        {
            var name = entry.Text;

            //SecondPageクラスのNameプロパティに値を渡す
            Navigation.PushAsync(new SecondPage {Name=name});
        }
    }
}

  
SecondPage.xaml.cs

using Xamarin.Forms;

namespace XF_NavigationPage
{
    public partial class SecondPage : ContentPage
    {
        //コンストラクタ
        public SecondPage()
        {
            InitializeComponent();
        }

        //画面が表示される時に実行
        protected override void OnAppearing()
        {
            NameSet(Name);
        }
        
        //プロパティを追加
        public string Name { get; set; }

        //ラベルのテキストを設定するメソッドを追加
        public void NameSet(string name)
        {
            label.Text = name;
        }
    }
}

最初はOnAppearingなしで、コンストラクタにNameSetメソッドを入れていましたが、それでは値を受け取れませんでした。この対策のためOnAppearingを使っています。
それでは動きを確認してみましょう。

f:id:takataka430:20190227230014g:plain:w200

一応、値は受け渡しできていますが、SecondPageで受け取った値が遅れて表示されるのでちょっと微妙ですね。

3.メソッドに値を渡す

それならメソッドに直接値を渡してしまえばいいのでは?と思ってコードを以下のようにしてみました。

MainPage.xaml.cs

using System;
using Xamarin.Forms;

namespace XF_NavigationPage
{
    public partial class MainPage : ContentPage
    {

        public MainPage()
        {
            InitializeComponent();
        }

        void Handle_Clicked(object sender, EventArgs e)
        {
            var name = entry.Text;

            var secondPage = new SecondPage();
            Navigation.PushAsync(secondPage);
            secondPage.NameSet(name);
        }
    }
}

  
SecondPage.xaml.cs

using Xamarin.Forms;

namespace XF_NavigationPage
{
    public partial class SecondPage : ContentPage
    {
        public SecondPage()
        {
            InitializeComponent();
        }

        public void NameSet(string name)
        {
            label.Text = name;
        }
    }
}

先ほどよりもコードがスッキリしました。動きを確認してみましょう。
  
f:id:takataka430:20190227230923g:plain:w200

いい感じです。今回行った方法の中で一番良さそうです。   

4.遷移先のページのBindingContextに渡す(2019/3/19 追記)

以下のようにSecondPageのBindingContextにMainPageのentryを渡し、SecondPageのLabelにバインドする方法でもできました。

MainPage.xaml.cs

using System;
using Xamarin.Forms;

namespace XF_NavigationPage
{
    public partial class MainPage : ContentPage
    {

        public MainPage()
        {
            InitializeComponent();
        }

        void Handle_Clicked(object sender, EventArgs e)
        {
            Navigation.PushAsync(new SecondPage()
            {
                BindingContext = entry
            });
        }
    }
}

  
SeconaPage.xaml

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="XF_NavigationPage.SecondPage">
    <ContentPage.Content>
        <StackLayout VerticalOptions="Center">
            <Label Text="前のページから受け取った値"
                   HorizontalOptions="Center"/>
            <Label Text="{Binding Text}"
                   HorizontalOptions="Center"/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

動きは3と同じだったので省略します。

まとめ

今回のような簡単な場合はコードビハインドに書くだけでいいのかもしれませんが、やはりしっかりやるならMVVMを使ってやった方がいいのではないかと思いました。それについては調べてみようと思います。