[.NET MAUI] サブ画面のモーダル表示
メイン画面(MainPage)からサブ画面をモーダル表示(メイン画面の上にサブ画面を重ねて表示)したい。
そんなときにMAUIではNavigation.PushModalAsync
が使える。ページ遷移に関するまとめは公式サイトのここ。
サブ画面のビューをAnswerPage.xaml
とすると、これだけモーダル表示で開くことができる。
await Navigation.PushModalAsync(new AnswerPage());
ところが、デフォルトの動作では、少なくともiOSでは下からスライドするように白い背景でサブ画面が現れるので、この動作は必ずしも良いとは思わない。PushModalAsync
のオーバーライドで第2引数animated
をfalse
にすれば、アニメーションを無効化できる。また、サブ画面のビューContentPage
にBackgroundColor="#aa000000"
を設定することで、半透明の背景でサブ画面を重ねて表示できる。
このあたりは、以下のredth.codesの記事が参考になった。
サブ画面の追加(読み込み中表示)
まずは [プロジェクト] > [新しい項目の追加] > [.NET MAUI ContentPage (XAML)] でAnswerPage
を追加する。
AnswerPage.xamlは#aa000000
で背景を半透明にして、メイン画面と大きさを合わせたGrid
をの中にActivityIndicator
を配置する。これは読み込み中を表すくるくるアイコンで、OSに依存した見た目となる。
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
BackgroundColor="#aa000000"
x:Class="RentaProto.AnswerPage"
Title="AnswerPage">
<Grid RowSpacing="0" ColumnSpacing="0"
AbsoluteLayout.LayoutFlags="PositionProportional,HeightProportional"
AbsoluteLayout.LayoutBounds="0.5,0,375,1">
<!-- ▼Loading表示 -->
<ActivityIndicator x:Name="LoadingIcon"
IsRunning="True" Color="White" Scale="2" />
</ContentPage>
メイン画面側はこのような感じ。
+ <Button Text="検索" FontSize="20" TextColor="Black"
...
+ Clicked="SubmitButton_Clicked" />
+ private async void SubmitButton_Clicked(Object sender, EventArgs e)
+ {
+ await Navigation.PushModalAsync(new AnswerPage(), false);
+ }
これで、検索ボタンをタップすると、半透明のモーダル表示でサブ画面が開き、くるくるアイコンが表示されるようになった!
サブ画面(読み込み完了)
読み込み完了と同時にくるくるアイコンを消してサーバのレスポンスに応じた内容を表示したい。今回は実際にサーバとの通信はせずに1秒のウェイト後に固定の内容を表示する。
AnswerPage.xamlの一番上の階層のGrid
内に固定の内容を追加する。下記コードは大幅に省略しているが、「×」の形の画像ファイルで閉じるボタンも配置する。画像ファイルはResources\Images
フォルダに入れること。
<Grid RowSpacing="0" ColumnSpacing="0"
AbsoluteLayout.LayoutFlags="PositionProportional,HeightProportional"
AbsoluteLayout.LayoutBounds="0.5,0,375,1">
<!-- ▼Loading表示 -->
<ActivityIndicator x:Name="LoadingIcon"
IsRunning="True" Color="White" Scale="2" />
+ <!-- ▼回答画面 -->
+ <Grid x:Name="AnswerContent" IsVisible="False"
+ Margin="30,100,30,30" Padding="14" WidthRequest="315"
+ Background="#F2F2F2">
...
+ <Image Source="x_button.png" HorizontalOptions="End"
+ WidthRequest="40" HeightRequest="40">
+ <Image.GestureRecognizers>
+ <TapGestureRecognizer NumberOfTapsRequired="1" Tapped="XButton_Tapped" />
+ </Image.GestureRecognizers>
+ </Image>
...
コードビハインド側は下記の通り。モーダル表示のサブ画面を閉じるのはNavigation.PopModalAsync
で。こちらも引数animated
はfalse
にする。
public AnswerPage()
{
InitializeComponent();
+ this.Loaded += AnswerPage_Loaded;
}
private async void AnswerPage_Loaded(object sender, EventArgs e)
{
+ this.Loaded -= AnswerPage_Loaded;
+ await Task.Delay(1000);
+ LoadingIcon.IsRunning = false;
+ AnswerContent.IsVisible = true;
}
+ private async void XButton_Tapped(object sender, EventArgs e)
+ {
+ await Navigation.PopModalAsync(false);
+ }
結果はこのような感じ!
BindingContextによるメイン画面とサブ画面の値受け渡し
メイン画面に入力した値をサブ画面に引き渡して表示してみる。ページナビゲーションにおけるデータ受け渡し方法はこちら。今回はBindingContext
を使ってみる。
若干やっつけだが、受け渡し用のRequestParams
クラスを作っておく。
+public class RequestParams
+{
+ public string carType { get; set; }
+ public string regionFrom { get; set; }
+ public string regionTo { get; set; }
+ public string gear { get; set; }
+ public string depMonth { get; set; }
+ public string depDay { get; set; }
+}
受け渡し方は以下の通り。
- await Navigation.PushModalAsync(new AnswerPage(), false);
+ var reqParams = new RequestParams
+ {
+ carType = CarTypeInput.InputText,
+ regionFrom = RegionFromInput.InputText,
+ regionTo = RegionToInput.InputText,
+ gear = GearSlide.GetCurrentText(),
+ depMonth = MonthEntry.Text,
+ depDay = DayEntry.Text
+ };
+ await Navigation.PushModalAsync(new AnswerPage
+ {
+ BindingContext = reqParams
+ }, false);
受け取り側のXAMLはBinding
から値を取り出してStringFormat
により書式を加工する。2つの値を取り出す場合はMultiBinding
を使う。
+ <Line Stroke="DarkBlue" StrokeDashArray="3,3" X2="300" />
+ <Label Text="<入力内容>" Margin="0,10" />
+ <Label Text="{Binding carType, StringFormat='車種 :{0}'}" />
+ <Label Text="{Binding regionFrom, StringFormat='乗車地:{0}'}" />
+ <Label Text="{Binding regionTo, StringFormat='降車地:{0}'}" />
+ <Label Text="{Binding gear, StringFormat='ギア :{0}'}" />
+ <Label>
+ <Label.Text>
+ <MultiBinding StringFormat="出発日:{0}月{1}日">
+ <Binding Path="depMonth" />
+ <Binding Path="depDay" />
+ </MultiBinding>
+ </Label.Text>
+ </Label>