C#Xamarin.Formsでスマホ開発#10画面遷移のやり方

当サイトではアフィリエイト広告を利用しています。

Xamarin.Formsでスマホアプリ開発

画面遷移とは?

画面遷移とは,今表示している画面から,別のページに切り替える動作の事です。Xamarin.Formsでは,次の3つの方法で画面遷移をすることができますが,最初に紹介する方法は基本的には使いません。2番目と3番目のやり方で画面遷移をすることになると思いますが,おすすめは,タイトルバーと戻るキーを表示するモーダレスでの画面遷移です。

画面遷移のやり方

画面遷移の仕様

Xamarin.Formsのプロジェクトを作成すると,MainPage.xamlというメインページが作成され,デフォルトで起動されるようになっています。今回はそのMainPageから別のページ,今回は「BPage」というPageを作成し,「NextPage」ボタンを押下すると「BPage」に遷移するという仕様を考えてみましょう。

MainPage.xamlにボタンを置きましょう。そのボタンのTextは「Next page!」とし,名前を「NextButton」にしましょう。コントロールを縦方向に並べるためにStackLayout上に配置します。Xamlにすると次のようになります。

<StackLayout>
    <Button x:Name="NextButton"
            Text="Next page!"/>
</StackLayout>

さぁこれでMainPageにはNextPageができました。ここで一度実行してみてもいいですが,先に進めましょう。 ボタンのクリックイベントを作成しましょう。Xamlで作成してもいいのですが,コードの自動生成がうまく動作しないので,コードビハインドからクリックイベントを作成します。Xmarin.Formsではクリックイベントは「Clicked」と呼ばれています。MainPage.xaml.csに次のように書いて「Clicked」イベントを作成しましょう。

NextButton.Clicked += NextButton_Clicked;

「NextButton.」と打ち込んでもインテリセンスが反応しないかもしれません。私が今実行している環境でも反応していません。そういうときは一度プロジェクトを右クリックして「ビルド」を選択してみてください。もう一度「NextButton.」と打ち込むと今度はインテリセンスが反応してくれているはずです。Xamarinの開発環境は何となくこういった場面に遭遇します。といっても2年ほど前に使っていた時よりはだいぶ良くなっている印象です。「NextButton.Clicked += NextButton_Clicked」と全部打ち込まなくても「NextButton.Clicked +=」まで打ち込んで「TAB」キーを押下すると,自動生成でClickedイベントが生成されるはずです。生成後のコードは次のようになっているはずです。

public MainPage()
{
InitializeComponent();
    NextButton.Clicked += NextButton_Clicked;
}

private void NextButton_Clicked(object sender, EventArgs e)
{
throw new NotImplementedException();
}

Clickedイベントの「throw new NotImplementedException();」は未実装の例外なので,削除します。ここにページを遷移するための記述をするのですが,その前に遷移するためのページを先に作りましょう。

遷移するページの作成

MainPageのNextButtonをタップされたときに遷移するページを作っておきましょう。名前は「BPage」とします。プロジェクトを右クリックして「追加」を選択し,「新しい項目」を選択します。

「ContentPage」を選択し,名前を「BPage」にして「追加」ボタンを押します。

「BPage.xaml」が作成されたら,デフォルトで作成されているLabelの文言を一部変更します。次のようにBPageであることがわかる文言に変更します。

<StackLayout>
    <Label Text="B page!!"
        VerticalOptions="CenterAndExpand" 
        HorizontalOptions="CenterAndExpand" />
</StackLayout>

これで遷移するページの準備ができました。次からは実際に画面遷移のプログラミングをしていきましょう。

MainPageを入れ替える

やり方の一つ目はお勧めしないやり方ですが,Application.Current.MainPageを入れ替えるというやり方です。Xamarin.Formsのプロジェクトを作成して,何も気にせず実行すると,MainPage.xamlのページが起動しますよね。あれはApp.xaml.csファイルに書かれている「MainPage = new X010.MainPage();」(X010の部分はプロジェクトを作成したときのネームスペース次第で変わります)が書かれているためです。この左辺の「MainPage」が「Application.Current.MainPage」です。このアプリケーションの現在のページという事です。このMainPageを別のページに変更してあげると表示されるページは変わりますが,アンドロイドなどの実機には「戻る」キーなどがあるため,それで戻られると,前のページに通常戻るはずが,MainPageを入れ替えてしまっているので,そのままではうまく戻れません。上手に制御すればできるのでしょうが,Xamarin.Formsでは,もっと簡単に,しっかりと画面遷移をする方法が用意されています。よってそちらを使えばいいので,ここまで説明してなんですが,この方法は使う必要がありません。知っていて困る知識ではないので,画面遷移の理屈として聞き流してください。

MainPage.xamlのコードビハインド側に作ったClickedイベントに次のように記述します。

private void NextButton_Clicked(object sender, EventArgs e)
{
    Application.Current.MainPage = new BPage();
}

これでApplication.Current.MainPageはBPageに変更されるため,NextButtonをクリックするとBPageが表示されるようになります。一度実行してみましょう。

最初はMainPageが起動されるので「NEXT PAGE!」とかかれたボタンが書くにできます。

そのボタンを押下すると,「B page!!」というページが出てきます。画面遷移ができていることが確認できます。ただしこの状態で,アンドロイドの「戻るキー」を押すとMainPageには戻らず,アンドロイドのディスクトップが表示されてしまいました。先ほども言った通り,戻るキーのタイミングでうまくMainPageに戻してあげればうまく動くのでしょうが,Xamarin.Formsではそんなことをしなくてもうまく動く仕組みが用意されているため,迷わずそちらを使いましょう。

モーダル

2番目のやり方を紹介しましょう。モーダル表示でページを遷移するやり方です。モーダルとは,呼び出された画面が処理を終えるまでは,呼び出した側に制御が移らない作りです。メッセージボックスなどの応答を必要とする画面などは一般的にモーダル表示されています。アンドロイドで表示しているページは常に1ページしか表示されないので,モーダルでもモーダレスでもあまり関係ない気がしますが,モーダルで表示する場合は「Navigation」の「PushModalAsync」というメソッドで画面遷移を行います。再びMainPageのClickedイベントに次のように記述します。

private void NextButton_Clicked(object sender, EventArgs e)
{
    Navigation.PushModalAsync(new BPage());
}

この状態で実行しましょう。

MainPageのNextButtonをクリックすると先ほどと同じようにBPageが表示されます。ただし,先ほどとは違い,この状態で,アンドロイドの戻るキーを押下しても,ちゃんとMainPageに戻ってくれます。この動作なら問題ないことがわかります。

モーダレス

画面遷移の3番目はモーダレスで表示する方法です。この場合は「Navigation」の「PushAsync」というメソッドを使います。先ほどのモーダル表示と異なり,この方法でページを表示すると,デフォルトでタイトルバーと,戻るキーが表示されます。次のようにMainPageのClickedイベントを変更し,実行してみましょう。

private void NextButton_Clicked(object sender, EventArgs e)
{
    Navigation.PushAsync(new BPage());
}

実行すると次のような例外が発生します。 これは想定通りですから驚かないでください。「Navigation」の「PushAsync」を使うときは,必ずNavigationPageから遷移する必要があるという決まりがあります。なので,PushAsyncでナビゲーションを利用する場合は,最初のMainPageの起動からNavigationPageで表示しておく必要があるのです。やり方はApp.xaml.csのMainPageを設定しているところを次のように編集します。

public App()
{
    InitializeComponent();

    MainPage = new NavigationPage(new X010.MainPage());
}

元々はMainPageのインスタンスをそのまま設定していましたが,今回はNavigationPageのインスタンスを設定するようにしました。この状態で実行してみましょう。

MainPageが表示されましたが,ページの上部にブルーのタイトルバーが表示されています。NextButtonで次のページに遷移すると,BPageが表示されますが,こちらにもタイトルバーが表示されています。しかもBPageのほうには「戻るキー」もついています。これがナビゲーションページでの画面遷移になります。

タイトルバーの非表示

せっかく表示されているタイトルバーですが,表示したくないページもあると思います。例えばMainPageには表示しないようにする場合は次のように書きます。タイトルバーを表示したくない場合は,他のページでも同じように書いて非表示にしてください。

public MainPage()
{
	InitializeComponent();

    NextButton.Clicked += NextButton_Clicked;
    NavigationPage.SetHasNavigationBar(this, false);
}

「NavigationPage.SetHasNavigationBar(this, false);」の部分でタイトルバーを非表示にしています。

タイトルバーに文字を表示

ナビゲーションページのタイトルバーに文字を出したいという気持ちになると思います。もちろんできます。次のように,「Title=”タイトルの文字”」と書いている部分に好きな文言を入れてください。

<?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="X010.BPage"
             Title="タイトルの文字">
    <ContentPage.Content>
        <StackLayout>
            <Label Text="B page!!"
                VerticalOptions="CenterAndExpand" 
                HorizontalOptions="CenterAndExpand" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

タイトルバーの背景色と文字色を変更する

タイトルバーの背景色を変更することができます。タイトルバーの色を変えることで,アプリケーションのイメージは結構変わると思います。ここがブルーかピンクかで,全然違うイメージになるでしょう。タイトルバーの色を変更する場合は「App.xaml.cs」に次のように記述します。

public App ()
{
	InitializeComponent();

    var page = new NavigationPage(new X010.MainPage());
    page.BarBackgroundColor = Color.Green;
    page.BarTextColor = Color.Yellow;
    MainPage = page;
}

NavigationPageのインスタンスに対して「BarBackgroundColor」「BarTextColor」を設定しています。これで好きな背景色と文字の色にできます。 ここまでで一度実行してみましょう。

MainPageはタイトルバーが表示されなくなっているのが確認できます。BPageはタイトルバーが緑になって,タイトルの文字は黄色になっていますね。

戻る方法

ページを遷移した後は,結構な確率で呼び出し元に戻る必要があると思います。ページを戻ろうと思ったらいくつか方法がありますが,まず一つ目はデバイスの「戻るキー」を押すことです。この方法ならいままで見てきた通り,呼び出し元に戻れます。Application.Current.MainPageを変更している場合はうまく戻らないのは前述のとおりです。PushAsyncを使ったモーダレスの表示であればタイトルバーの戻るキーでも戻れます。

それ以外にも,画面にボタンなどを設置して,クリック時に戻りたい場合や,保存処理が終わったタイミングで呼び出し元に戻りたい場合もあると思います。そういう場合は,独自の方法で戻る必要があります。戻る場合に注意する点は1つです。モーダルで呼び出した場合はモーダルで戻る。モーダレスで呼び出した場合はモーダレスで戻るという事です。要するに「PushAsync」で遷移しているときは「 PopAsync 」を呼び出して戻る必要があり, 「PushModalAsync」で遷移しているときは「 PopModalAsync 」で戻る必要があります。

実際にやってみましょう。「BPage」に戻るボタン「ReturnButton」を設置し,Clickedイベントで戻るように実装します。

<StackLayout>
    <Label Text="B page!!"
        VerticalOptions="CenterAndExpand" 
        HorizontalOptions="CenterAndExpand" />
    <Button Text="return"
            x:Name="ReturnButton"/>
</StackLayout>
public BPage ()
{
	InitializeComponent ();
    ReturnButton.Clicked += ReturnButton_Clicked;
}

private void ReturnButton_Clicked(object sender, EventArgs e)
{
    Navigation.PopAsync(true);
}

PopAsyncの引数のTrueは,アニメーションを有効にするかどうかです。切り替えて動作を確認してみてください。この状態で実行すると次のようなRETURNボタンが表示され,押下するとページが戻ることが確認できます。

サンプルコード全体

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Xamarin.Forms;

namespace X010
{
	public partial class App : Application
	{
		public App ()
		{
			InitializeComponent();

            //MainPage = new X010.MainPage();
            var page = new NavigationPage(new X010.MainPage());
            page.BarBackgroundColor = Color.Green;
            page.BarTextColor = Color.Yellow;
            MainPage = page;
        }

        protected override void OnStart ()
		{
			// Handle when your app starts
		}

		protected override void OnSleep ()
		{
			// Handle when your app sleeps
		}

		protected override void OnResume ()
		{
			// Handle when your app resumes
		}
	}
}
<?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="X010.BPage"
             Title="タイトルの文字">
    <ContentPage.Content>
        <StackLayout>
            <Label Text="B page!!"
                VerticalOptions="CenterAndExpand" 
                HorizontalOptions="CenterAndExpand" />
            <Button Text="return"
                    x:Name="ReturnButton"/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace X010
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class BPage : ContentPage
    {
        public BPage ()
        {
            InitializeComponent ();
            ReturnButton.Clicked += ReturnButton_Clicked;
        }

        private void ReturnButton_Clicked(object sender, EventArgs e)
        {
            Navigation.PopAsync(true);
        }
    }
}
<?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:X010"
             x:Class="X010.MainPage">


    <StackLayout>
        <Button x:Name="NextButton"
                Text="Next page!"/>
    </StackLayout>

</ContentPage>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace X010
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();

            NextButton.Clicked += NextButton_Clicked;
            NavigationPage.SetHasNavigationBar(this, false);
        }

        private void NextButton_Clicked(object sender, EventArgs e)
        {
            //Application.Current.MainPage = new BPage();
            //Navigation.PushModalAsync(new BPage());
            Navigation.PushAsync(new BPage());
        }
    }
}