主にプログラミング関連のメモ帳 ♪(✿╹ヮ╹)ノ
書いてあるコードは自己責任でご自由にどうぞ。記事本文の無断転載は禁止です。
2015/11/10
Xamarin.Forms(XAML) を使って、 NavigationPage + TabbedPage を作ってみます。
Twitter for iPhone や TweetBot みたいな感じの UI ですね。
ただ、
NavigationPage
|- TabbedPage
といった構成はよく見かけるものの、その逆は見かけません。
また、上のような構成は、非推奨となっているようです。
たとえば、ほとんどのプラットフォームでは NavigationPage の子ページとして TabbedPage を追加しないよう推奨されます。 https://msdn.microsoft.com/ja-jp/magazine/dn904669.aspx
そこで、その逆、 TabbedPage
の子ページとして NavigationPage
を追加してみます。
単純に書くとしたら、下のような XAML になるはず。
MainPage.xaml
<TabbedPage x:Class="Mikazuki.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Mikazuki">
<TabbedPage.Children>
<local:NavPage />
</TabbedPage.Children>
</TabbedPage>
NavPage.xaml
<NavigationPage x:Class="Mikazuki.NavPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<Label Text="Hello!"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand" />
</NavigationPage>
ただ、こうすると、
InvalidOperationException: NavigationPage must have a root Page before being used. Either call PushAsync with a valid Page, or pass a Page to the constructor before usage.
と、例外が発生してしまいます。
Root ページを設定しろと怒られているので、 NavigationPage(Page)
を呼び出す必要があります。
NavigationPage(Xamarin.Forms.Page) - Xamarin
ということで、書き換えます。
MainPage.xaml
<TabbedPage x:Class="Mikazuki.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Mikazuki">
<TabbedPage.Children>
<local:NavPage>
<x:Arguments>
<Label Text="Hello!"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand" />
</x:Arguments>
</local:NavPage>
</TabbedPage.Children>
</TabbedPage>
NavPage.xaml のコンテンツを、 x:Arguments
の子供として記述します。
NavPage.xaml
<NavigationPage x:Class="Mikazuki.NavPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<x:Arguments />
</NavigationPage>
NavPage.xaml.cs
using Xamarin.Forms;
namespace Natsuneko
{
public partial class NavPage : NavigationPage
{
public NavPage(Page page) : base(page)
{
InitializeComponent();
}
}
}
うえで示した、コンストラクタに対して、 MainPage.xaml のコンテンツを渡します。
こうすることで、TabbedPage
の子として NavigationPage
を追加できました。
でも、このままの状態だと、 MainPage.xaml が非常に大きくなります。
それはあれですので、多少使いやすいように改造します。
構造的にはこんな感じ。
MainPage (TabbedPage)
|- NavPage (NavigationPage)
| |- FirstPage (ContentPage)
|
|- ...
MainPage.xaml
<TabbedPage x:Class="Mikazuki.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Mikazuki">
<TabbedPage.Children>
<local:NavPage>
<x:Arguments>
<local:FirstPage />
</x:Arguments>
</local:NavPage>
</TabbedPage.Children>
</TabbedPage>
NavPage.xaml
<NavigationPage x:Class="Mikazuki.NavPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<x:Arguments />
</NavigationPage>
これは、さっきのものと変わりません。
NavPage.xaml.cs
using Xamarin.Forms;
namespace Mikazuki
{
public partial class NavPage : NavigationPage
{
public NavPage(Page page) : base(page)
{
InitializeComponent();
Title = page.Title;
Icon = page.Icon;
}
}
}
親である TabbedPage にアイコンとタブを表示するために、
Title
と Icon
を設定しておきます。
FirstPage.xaml
<ContentPage x:Class="Mikazuki.FirstPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<Label Text="Hello!"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand" />
</ContentPage>
これで、やりたいことを達成することができました。
あとは、前回書いた NavigationService
を、いい感じに切り替えることで、
自然に使えるのではないかと思っています。
参考
2015/11/10 追記
BindingContext
を使ってどうのこうのする場合は、
page.BindingContextChanged += (sender, args) =>
{
var viewModel = page.BindingContext as HogeViewModelBase; // Title, Icon プロパティがある。
if (viewModel == null)
return;
Title = viewModel.Title;
Icon = viewModel.Icon;
};
みたいなものを、 NavBar.xaml.cs のコンストラクタに追加すると幸せになれる気がします。