Xamarin Formsでページを切り替えるにはどうすればよいですか?私のメインページはContentPageであり、タブ付きページのようなものに切り替えたくありません。
ContentPageが見つかるまで新しいページをトリガーするコントロールの親を見つけて、新しいページのコントロールとContentを交換することで、疑似操作を行うことができました。しかし、これは本当にずさんなようです。
ありがとう
Xamarin.Forms
は、組み込みの複数のナビゲーションホストをサポートします。
NavigationPage
、次のページがスライドする場所、TabbedPage
、好きではないものCarouselPage
。これにより、左右のページを次/前のページに切り替えることができます。これに加えて、すべてのページはPushModalAsync()
もサポートします。これは、既存のページの上に新しいページをプッシュするだけです。
最後に、ユーザーが(ジェスチャーまたは戻るハードウェアボタンを使用して)前のページに戻れないようにする場合、同じPage
を表示したままにして、Content
を置き換えることができます。
ルートページを置き換える推奨オプションも同様に機能しますが、プラットフォームごとに異なる方法で処理する必要があります。
Appクラスでは、MainPageをナビゲーションページに設定し、ルートページをContentPageに設定できます。
public App ()
{
// The root page of your application
MainPage = new NavigationPage( new FirstContentPage() );
}
次に、最初のContentPage呼び出しで:
Navigation.PushAsync (new SecondContentPage ());
プロジェクトがPCLフォームプロジェクトとして設定されている場合(および共有フォームとして設定されている可能性が非常に高いですが、私は試していません)、次のようなクラスApp.csがあります。
public class App
{
public static Page GetMainPage ()
{
AuditorDB.Model.Extensions.AutoTimestamp = true;
return new NavigationPage (new LoginPage ());
}
}
GetMainPage
メソッドを変更して、新しいTabbedPagedまたはプロジェクトで定義した他のページを返すことができます
そこからコマンドまたはイベントハンドラを追加してコードを実行し、
// to show OtherPage and be able to go back
Navigation.PushAsync(new OtherPage());
// to show AnotherPage and not have a Back button
Navigation.PushModalAsync(new AnotherPage());
// to go back one step on the navigation stack
Navigation.PopAsync();
新しいページをスタックにプッシュしてから、現在のページを削除します。これにより、スイッチが発生します。
item.Tapped += async (sender, e) => {
await Navigation.PushAsync (new SecondPage ());
Navigation.RemovePage(this);
};
最初にナビゲーションページに移動する必要があります。
MainPage = NavigationPage(new FirstPage());
コンテンツの切り替えは、1つの大きなページとOnAppearing ectなどのページイベントのセットが1つしかないため、理想的ではありません。
前のページに移動したくない場合、つまり、認証が完了したらユーザーがログイン画面に戻らないようにする場合は、使用できます。
App.Current.MainPage = new HomePage();
バック機能を有効にしたい場合は、
Navigation.PushModalAsync(new HomePage())
PushAsync()メソッドを使用すると、PushとPopModalAsync()を使用して、ナビゲーションスタックとの間でページをポップできます。以下のコード例では、ナビゲーションページ(ルートページ)があり、このページからログインページが完成したら、ログインページであるコンテンツページをプッシュし、ルートページに戻ります
~~~ナビゲーションは、ページオブジェクトの先入れ先出しスタックと考えることができます。あるページから別のページに移動するには、アプリケーションが新しいページをこのスタックにプッシュします。前のページに戻るために、アプリケーションはスタックから現在のページをポップします。 Xamarin.Formsのこのナビゲーションは、INavigationインターフェイスによって処理されます
Xamarin.Formsには、このインターフェイスを実装し、ページのスタックを管理するNavigationPageクラスがあります。 NavigationPageクラスは、タイトルを表示するナビゲーションバーを画面の上部に追加し、前のページに戻るプラットフォームに適した[戻る]ボタンも備えています。次のコードは、アプリケーションの最初のページの周りにNavigationPageをラップする方法を示しています。
上記のコンテンツへの参照と、Xamarin Formsの詳細について確認する必要があるリンクは、ナビゲーションセクションを参照してください。
http://developer.xamarin.com/guides/cross-platform/xamarin-forms/introduction-to-xamarin-forms/
~~~
public class MainActivity : AndroidActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
Xamarin.Forms.Forms.Init(this, bundle);
// Set our view from the "main" layout resource
SetPage(BuildView());
}
static Page BuildView()
{
var mainNav = new NavigationPage(new RootPage());
return mainNav;
}
}
public class RootPage : ContentPage
{
async void ShowLoginDialog()
{
var page = new LoginPage();
await Navigation.PushModalAsync(page);
}
}
//簡単にするためにコードを削除し、ポップのみを表示
private async void AuthenticationResult(bool isValid)
{
await navigation.PopModalAsync();
}
このスレッドは非常に人気があり、別の方法-ViewModel First Navigation
があることは言うまでもありません。 MVVMフレームワークのほとんどはそれを使用していますが、それが何であるかを理解したい場合は、読み続けてください。
公式のXamarin.Formsのすべてのドキュメントは、MVVMの純粋なソリューションではありますが、単純ではありませんが簡単に説明しています。これは、Page
(View)がViewModel
について何も知らない必要があるためです。この違反の優れた例を次に示します。
// C# version
public partial class MyPage : ContentPage
{
public MyPage()
{
InitializeComponent();
// Violation
this.BindingContext = new MyViewModel();
}
}
// XAML version
<?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:viewmodels="clr-namespace:MyApp.ViewModel"
x:Class="MyApp.Views.MyPage">
<ContentPage.BindingContext>
<!-- Violation -->
<viewmodels:MyViewModel />
</ContentPage.BindingContext>
</ContentPage>
2ページのアプリケーションを使用している場合、このアプローチが適している可能性があります。ただし、大規模なエンタープライズソリューションに取り組んでいる場合は、ViewModel First Navigation
アプローチを使用することをお勧めします。 ViewModels
(Views)間のナビゲーションの代わりにPages
の間をナビゲートできるようにする、少し複雑ですがはるかにクリーンなアプローチです。懸念事項を明確に分離することの利点の1つは、次のViewModel
にパラメーターを簡単に渡すか、ナビゲーションの直後に非同期初期化コードを実行できることです。さて詳細に。
(すべてのコード例をできるだけ単純化するようにします)。
1。まず、すべてのオブジェクトを登録し、オプションでそれらのライフタイムを定義できる場所が必要です。この問題については、IOCコンテナを使用できます。自分でコンテナを選択できます。この例では、 Autofac (使用可能な最速の1つです)を使用します。 App
でそれへの参照を保持することができるので、グローバルに利用可能になります(良いアイデアではありませんが、単純化のために必要です):
public class DependencyResolver
{
static IContainer container;
public DependencyResolver(params Module[] modules)
{
var builder = new ContainerBuilder();
if (modules != null)
foreach (var module in modules)
builder.RegisterModule(module);
container = builder.Build();
}
public T Resolve<T>() => container.Resolve<T>();
public object Resolve(Type type) => container.Resolve(type);
}
public partial class App : Application
{
public DependencyResolver DependencyResolver { get; }
// Pass here platform specific dependencies
public App(Module platformIocModule)
{
InitializeComponent();
DependencyResolver = new DependencyResolver(platformIocModule, new IocModule());
MainPage = new WelcomeView();
}
/* The rest of the code ... */
}
2.特定のPage
のViewModel
(View)を取得するオブジェクトと、その逆のオブジェクトが必要です。 2番目のケースは、アプリのルート/メインページを設定する場合に役立ちます。そのためには、すべてのViewModels
がViewModels
ディレクトリにあり、Pages
(Views)がViews
ディレクトリにあるという単純な規則に同意する必要があります。つまり、ViewModels
は[MyApp].ViewModels
名前空間に、Pages
(Views)は[MyApp].Views
名前空間に存在する必要があります。それに加えて、WelcomeView
(Page)にはWelcomeViewModel
などを含める必要があることに同意する必要があります。マッパーのコード例を次に示します。
public class TypeMapperService
{
public Type MapViewModelToView(Type viewModelType)
{
var viewName = viewModelType.FullName.Replace("Model", string.Empty);
var viewAssemblyName = GetTypeAssemblyName(viewModelType);
var viewTypeName = GenerateTypeName("{0}, {1}", viewName, viewAssemblyName);
return Type.GetType(viewTypeName);
}
public Type MapViewToViewModel(Type viewType)
{
var viewModelName = viewType.FullName.Replace(".Views.", ".ViewModels.");
var viewModelAssemblyName = GetTypeAssemblyName(viewType);
var viewTypeModelName = GenerateTypeName("{0}Model, {1}", viewModelName, viewModelAssemblyName);
return Type.GetType(viewTypeModelName);
}
string GetTypeAssemblyName(Type type) => type.GetTypeInfo().Assembly.FullName;
string GenerateTypeName(string format, string typeName, string assemblyName) =>
string.Format(CultureInfo.InvariantCulture, format, typeName, assemblyName);
}
3.ルートページを設定する場合、ViewModelLocator
を自動的に設定するBindingContext
を並べ替える必要があります。
public static class ViewModelLocator
{
public static readonly BindableProperty AutoWireViewModelProperty =
BindableProperty.CreateAttached("AutoWireViewModel", typeof(bool), typeof(ViewModelLocator), default(bool), propertyChanged: OnAutoWireViewModelChanged);
public static bool GetAutoWireViewModel(BindableObject bindable) =>
(bool)bindable.GetValue(AutoWireViewModelProperty);
public static void SetAutoWireViewModel(BindableObject bindable, bool value) =>
bindable.SetValue(AutoWireViewModelProperty, value);
static ITypeMapperService mapper = (Application.Current as App).DependencyResolver.Resolve<ITypeMapperService>();
static void OnAutoWireViewModelChanged(BindableObject bindable, object oldValue, object newValue)
{
var view = bindable as Element;
var viewType = view.GetType();
var viewModelType = mapper.MapViewToViewModel(viewType);
var viewModel = (Application.Current as App).DependencyResolver.Resolve(viewModelType);
view.BindingContext = viewModel;
}
}
// Usage example
<?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:viewmodels="clr-namespace:MyApp.ViewModel"
viewmodels:ViewModelLocator.AutoWireViewModel="true"
x:Class="MyApp.Views.MyPage">
</ContentPage>
4.最後に、ViewModel First Navigation
アプローチをサポートするNavigationService
が必要になります。
public class NavigationService
{
TypeMapperService mapperService { get; }
public NavigationService(TypeMapperService mapperService)
{
this.mapperService = mapperService;
}
protected Page CreatePage(Type viewModelType)
{
Type pageType = mapperService.MapViewModelToView(viewModelType);
if (pageType == null)
{
throw new Exception($"Cannot locate page type for {viewModelType}");
}
return Activator.CreateInstance(pageType) as Page;
}
protected Page GetCurrentPage()
{
var mainPage = Application.Current.MainPage;
if (mainPage is MasterDetailPage)
{
return ((MasterDetailPage)mainPage).Detail;
}
// TabbedPage : MultiPage<Page>
// CarouselPage : MultiPage<ContentPage>
if (mainPage is TabbedPage || mainPage is CarouselPage)
{
return ((MultiPage<Page>)mainPage).CurrentPage;
}
return mainPage;
}
public Task PushAsync(Page page, bool animated = true)
{
var navigationPage = Application.Current.MainPage as NavigationPage;
return navigationPage.PushAsync(page, animated);
}
public Task PopAsync(bool animated = true)
{
var mainPage = Application.Current.MainPage as NavigationPage;
return mainPage.Navigation.PopAsync(animated);
}
public Task PushModalAsync<TViewModel>(object parameter = null, bool animated = true) where TViewModel : BaseViewModel =>
InternalPushModalAsync(typeof(TViewModel), animated, parameter);
public Task PopModalAsync(bool animated = true)
{
var mainPage = GetCurrentPage();
if (mainPage != null)
return mainPage.Navigation.PopModalAsync(animated);
throw new Exception("Current page is null.");
}
async Task InternalPushModalAsync(Type viewModelType, bool animated, object parameter)
{
var page = CreatePage(viewModelType);
var currentNavigationPage = GetCurrentPage();
if (currentNavigationPage != null)
{
await currentNavigationPage.Navigation.PushModalAsync(page, animated);
}
else
{
throw new Exception("Current page is null.");
}
await (page.BindingContext as BaseViewModel).InitializeAsync(parameter);
}
}
ご覧のとおり、BaseViewModel
-すべてのViewModels
の抽象基本クラスがあり、ナビゲーション直後に実行されるInitializeAsync
などのメソッドを定義できます。次に、ナビゲーションの例を示します。
public class WelcomeViewModel : BaseViewModel
{
public ICommand NewGameCmd { get; }
public ICommand TopScoreCmd { get; }
public ICommand AboutCmd { get; }
public WelcomeViewModel(INavigationService navigation) : base(navigation)
{
NewGameCmd = new Command(async () => await Navigation.PushModalAsync<GameViewModel>());
TopScoreCmd = new Command(async () => await navigation.PushModalAsync<TopScoreViewModel>());
AboutCmd = new Command(async () => await navigation.PushModalAsync<AboutViewModel>());
}
}
理解しているように、このアプローチはより複雑で、デバッグが難しく、混乱を招く可能性があります。ただし、多くの利点があり、さらにほとんどのMVVMフレームワークがすぐにサポートするため、実際に自分で実装する必要はありません。ここに示されているコード例は、 github で入手できます。ViewModel First Navigation
アプローチに関する多くの優れた記事があり、無料の Xamarin.Formsを使用したエンタープライズアプリケーションパターン この本や他の興味深いトピックを詳細に説明している電子書籍があります。
Navigation property以下のサンプルコードを使用したXamarin.formsでの1ページから別のページへのナビゲーション
void addClicked(object sender, EventArgs e)
{
//var createEmp = (Employee)BindingContext;
Employee emp = new Employee();
emp.Address = AddressEntry.Text;
App.Database.SaveItem(emp);
this.Navigation.PushAsync(new EmployeeDetails());
this.Navigation.PushModalAsync(new EmployeeDetails());
}
ビューセルの下のコードXamrian.formsを使用して、あるページから別のページに移動するには
private async void BtnEdit_Clicked1(object sender, EventArgs e)
{
App.Database.GetItem(empid);
await App.Current.MainPage.Navigation.PushModalAsync(new EmployeeRegistration(empid));
}
以下のような例
public class OptionsViewCell : ViewCell
{
int empid;
Button btnEdit;
public OptionsViewCell()
{
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (this.BindingContext == null)
return;
dynamic obj = BindingContext;
empid = Convert.ToInt32(obj.Eid);
var lblname = new Label
{
BackgroundColor = Color.Lime,
Text = obj.Ename,
};
var lblAddress = new Label
{
BackgroundColor = Color.Yellow,
Text = obj.Address,
};
var lblphonenumber = new Label
{
BackgroundColor = Color.Pink,
Text = obj.phonenumber,
};
var lblemail = new Label
{
BackgroundColor = Color.Purple,
Text = obj.email,
};
var lbleid = new Label
{
BackgroundColor = Color.Silver,
Text = (empid).ToString(),
};
//var lbleid = new Label
//{
// BackgroundColor = Color.Silver,
// // HorizontalOptions = LayoutOptions.CenterAndExpand
//};
//lbleid.SetBinding(Label.TextProperty, "Eid");
Button btnDelete = new Button
{
BackgroundColor = Color.Gray,
Text = "Delete",
//WidthRequest = 15,
//HeightRequest = 20,
TextColor = Color.Red,
HorizontalOptions = LayoutOptions.EndAndExpand,
};
btnDelete.Clicked += BtnDelete_Clicked;
//btnDelete.PropertyChanged += BtnDelete_PropertyChanged;
btnEdit = new Button
{
BackgroundColor = Color.Gray,
Text = "Edit",
TextColor = Color.Green,
};
// lbleid.SetBinding(Label.TextProperty, "Eid");
btnEdit.Clicked += BtnEdit_Clicked1; ;
//btnEdit.Clicked += async (s, e) =>{
// await App.Current.MainPage.Navigation.PushModalAsync(new EmployeeRegistration());
//};
View = new StackLayout()
{
Orientation = StackOrientation.Horizontal,
BackgroundColor = Color.White,
Children = { lbleid, lblname, lblAddress, lblemail, lblphonenumber, btnDelete, btnEdit },
};
}
private async void BtnEdit_Clicked1(object sender, EventArgs e)
{
App.Database.GetItem(empid);
await App.Current.MainPage.Navigation.PushModalAsync(new EmployeeRegistration(empid));
}
private void BtnDelete_Clicked(object sender, EventArgs e)
{
// var eid = Convert.ToInt32(empid);
// var item = (Xamarin.Forms.Button)sender;
int eid = empid;
App.Database.DeleteItem(empid);
}
}
コール:
((App)App.Current).ChangeScreen(new Map());
App.xaml.cs内でこのメソッドを作成します。
public void ChangeScreen(Page page)
{
MainPage = page;
}
XAMLページはこれを追加します
<ContentPage.ToolbarItems>
<ToolbarItem Text="Next" Order="Primary"
Activated="Handle_Activated"/>
</ContentPage.ToolbarItems>
cSページ
async void Handle_Activated(object sender, System.EventArgs e)
{
await App.Navigator.PushAsync(new PAGE());
}
In App.Xaml.Cs:
MainPage = new NavigationPage( new YourPage());
YourPageから次のページに移動したい場合:
await Navigation.PushAsync(new YourSecondPage());
Xamarin Formsナビゲーションの詳細については、こちらをご覧ください: https://docs.Microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/navigation/hierarchical
Microsoftにはこれに関する非常に優れたドキュメントがあります。
Shell
の新しい概念もあります。アプリケーションを構造化する新しい方法を可能にし、場合によってはナビゲーションを簡素化します。
イントロ: https://devblogs.Microsoft.com/xamarin/Shell-xamarin-forms-4-0-getting-started/
シェルの基本に関するビデオ: https://www.youtube.com/watch?v=0y1bUAcOjZY&t=3112s
ドキュメント: https://docs.Microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/Shell/