Microsoft Band の接続サンプルを作る上で、ToolBarItem をタップしたら同一画面内で表示遷移させたくなったので挑戦
TabbedPage とかだと同じ Template の表示しかできないので、もっと自由度の高い ToolBar を利用することにしたというわけです
ソースコードの一式は下記にあります!
※ 順次改修していく予定なので、この記事の内容が現時点のソースより古い可能性があります
まずは 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:cv="clr-namespace:XamarinBandSample.Converters;assembly=XamarinBandSample" xmlns:prismmvvm="clr-namespace:Prism.Mvvm;assembly=XamarinBandSample" prismmvvm:ViewModelLocator.AutoWireViewModel="true" x:Class="XamarinBandSample.Views.TopPage"> <ContentPage.Resources> <ResourceDictionary> <cv:NegativeConverter x:Key="NegativeConverter"/> </ResourceDictionary> </ContentPage.Resources> <ContentPage.Padding> <OnPlatform x:TypeArguments="Thickness" iOS="0,20,0,0"/> </ContentPage.Padding> <ContentPage.ToolbarItems> <ToolbarItem Text="Basics" Command="{Binding SelectBasicsCommand}" /> <ToolbarItem Text="Senseor" Command="{Binding SelectSensorsCommand}"/> </ContentPage.ToolbarItems> <Grid> <!-- Basics Settings Pain--> <ContentView IsVisible="{Binding ShowBasics}"> <StackLayout Orientation="Vertical" Padding="10" Spacing="10"> <Label Text="Band Test" FontSize="Large" HorizontalOptions="Center" VerticalOptions="Center"/> <StackLayout Orientation="Vertical" Spacing="5"> <Button Text="Connect Band" FontSize="Medium" HorizontalOptions="Start" VerticalOptions="Center" IsEnabled="{Binding IsConnected, Converter={StaticResource NegativeConverter}}" Command="{Binding ConnectCommand}"/> <Grid Padding="20,0"> <Label Text="{Binding ConnectMessage}" HeightRequest="20" Opacity="0.6" FontSize="Small"/> </Grid> </StackLayout> <StackLayout Orientation="Vertical" Spacing="5"> <Label Text="Device Name:" FontSize="Medium"/> <Grid Padding="20,0"> <Label Text="{Binding BandName}" HeightRequest="20" FontSize="Small"/> </Grid> </StackLayout> <StackLayout Orientation="Vertical" Spacing="5"> <Label Text="Hardware Version:" FontSize="Medium"/> <Grid Padding="20,0"> <Label Text="{Binding HardwareVersion}" HeightRequest="20" FontSize="Small"/> </Grid> </StackLayout> <StackLayout Orientation="Vertical" Spacing="5"> <Label Text="Firmware Version:" FontSize="Medium"/> <Grid Padding="20,0"> <Label Text="{Binding FirmwareVersion}" HeightRequest="20" FontSize="Small"/> </Grid> </StackLayout> </StackLayout> </ContentView> <!-- Sensor Info Pain--> <ContentView IsVisible="{Binding ShowSensors}"> <Grid Padding="10" RowSpacing="10" ColumnSpacing="10"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="100"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Label Text="Detecting" Grid.Column="0" Grid.Row="0" VerticalOptions="Center" FontSize="Medium"/> <Switch IsToggled="{Binding IsSensorDetecting}" Grid.Column="1" Grid.Row="0" /> <StackLayout Orientation="Horizontal" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1" Spacing="5"> <Label Text="Accelometer:" FontSize="Medium"/> <Label Text="" FontSize="Small"/> </StackLayout> </Grid> </ContentView> </Grid> </ContentPage>
ContentPage.ToolBarItems に2つの ToolBarItem を追加し、同一の画面内に2つの ContentView を持たせました
仕組みとしては ShowBasics や ShowSensors の値によって 2つの ContentView の表示有無が切り替わるようになっています
この画面に合わせて ViewModel を改修
/// <summary> /// トップ画面の ViewModel /// </summary> public class TopPageViewModel : BindableBase { ~ 中略 ~ #region ShowBasics /// <summary> /// 基本設定表示フラグ /// </summary> private bool showBasics = true; /// <summary> /// 基本設定表示フラグ /// </summary> public bool ShowBasics { get { return this.showBasics; } set { this.SetProperty(ref this.showBasics, value); } } #endregion //ShowBasics #region ShowSensors /// <summary> /// センサー情報表示フラグ /// </summary> private bool showSensors = false; /// <summary> /// センサー情報表示フラグ /// </summary> public bool ShowSensors { get { return this.showSensors; } set { this.SetProperty(ref this.showSensors, value); } } #endregion //ShowSensors ~ 中略 ~ #region SelectBasicsCommand /// <summary> /// 基本設定表示選択コマンド /// </summary> public ICommand SelectBasicsCommand { get; private set; } #endregion //SelectBasicsCommand #region SelectSensorsCommand /// <summary> /// センサー情報表示選択コマンド /// </summary> public ICommand SelectSensorsCommand { get; private set; } #endregion //SelectSensorsCommand ~ 中略 ~ /// <summary> /// コンストラクタ /// </summary> /// <param name="manager">Microsoft Band デバイス管理クラス</param> [InjectionConstructor] public TopPageViewModel(IBandManager manager) { this.manager = manager; this.SelectBasicsCommand = new DelegateCommand(this.SelectBasics, () => { return !this.ShowBasics; }); this.SelectSensorsCommand = new DelegateCommand(this.SelectSensors, () => { return !this.ShowSensors; }); this.ConnectCommand = DelegateCommand.FromAsyncHandler(this.Connect); } /// <summary> /// 基本設定表示切替 /// </summary> private void SelectBasics() { this.ShowSensors = false; this.ShowBasics = true; ((DelegateCommand)this.SelectBasicsCommand).RaiseCanExecuteChanged(); ((DelegateCommand)this.SelectSensorsCommand).RaiseCanExecuteChanged(); } /// <summary> /// センサー情報表示切替 /// </summary> private void SelectSensors() { this.ShowBasics = false; this.ShowSensors = true; ((DelegateCommand)this.SelectBasicsCommand).RaiseCanExecuteChanged(); ((DelegateCommand)this.SelectSensorsCommand).RaiseCanExecuteChanged(); } ~ 中略 ~ }
ToolBarItem を選択したときの処理と選択可能かどうかを DelegateCommand という Prism.Mvvm のクラスで制御しています
DelegateCommand の場合、new するときの第一引数にコマンド実行するときの処理、第二引数に実行可能かどうかの判定処理を指定することでデータバインディングだけでもこの制御ができるようになります
SelectBasics() や SelectSensors() の選択時の処理のメソッドでは、選択可能状態の更新を ToolBarItem に伝えるために DelegateCommand の RaiseCanExecuteChanged を呼び出しています
これがないと選択状態が更新されないはず
最後に App.cs を変更
/// <summary> /// アプリケーション基盤クラス /// </summary> public class App : Application { /// <summary> /// DI コンテナ /// </summary> public static UnityContainer Container = new UnityContainer(); /// <summary> /// ナビゲーション画面 /// </summary> public static NavigationPage Navigation = null; /// <summary> /// コンストラクタ /// </summary> static App() { } /// <summary> /// コンストラクタ /// </summary> public App() { // ViewModel をインスタンス化するデフォルトメソッドを指定 ViewModelLocationProvider.SetDefaultViewModelFactory((type) => Container.Resolve(type)); Navigation = new NavigationPage(new TopPage()); this.MainPage = Navigation; } ~ 中略 ~ }
ToolBar を表示する場合には ContentPage.ToolBarItems に ToolBarItem を追加するだけでは不十分で、App.MainPage に NavigationPage を指定しないといけません
これが気づきづらかったり
これで動くはずなので実行してみました
Windows Phone のアイコンが × になるのは、Windows Runtime の OnPlatform タグ対応が来ればなんとかなるからよいとしても・・・iOS だけ選択状態が切り替わらない!!
これたぶん不具合ですかね・・・ToolBarItem は IsEnabled プロパティがないので、Command で選択可能状態を制御するしかないのに機能しないので;
仕方がないので Xamarin Forums で質問しておきました