Xamarinフォームのページをどのように切り替えますか?
私のメインページはContentPageであり、タブ付きページなどに切り替えたくありません。
ContentPageが見つかるまで新しいページをトリガーするコントロールの親を見つけ、それからContentを新しいページのコントロールと交換することで、疑似操作を行うことができました。しかし、これは本当にずさんなようです。
Xamarinフォームのページをどのように切り替えますか?
私のメインページは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 ());
Android.Content.Res
、ナビゲーション用にインポートすることをお勧めします。それは正しくないようです、どこからインポートする必要がありますか?
プロジェクトが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();
Navigation
この例では一体何ですか?-それはあなたがどこかに作成したオブジェクトですか?-このコードサンプルには表示されません。
PushAsync()
は機能しませんでしたが、機能しましPushModalAsync()
た
新しいページをスタックにプッシュしてから、現在のページを削除します。これにより切り替えが発生します。
item.Tapped += async (sender, e) => {
await Navigation.PushAsync (new SecondPage ());
Navigation.RemovePage(this);
};
最初にナビゲーションページにいる必要があります。
MainPage = NavigationPage(new FirstPage());
大きなページが1つとOnAppearing ectなどのページイベントが1セットしかないため、コンテンツの切り替えは理想的ではありません。
Navigation.RemovePage();
Androidではサポートされていません。
await Navigation.PushAsync(new SecondPage(),false);
このスレッドは非常に人気があるようで、代替方法があることは言うまでもありません。 ViewModel First Navigation
です。MVVMフレームワークのほとんどはそれを使用していますが、それが何であるかを理解したい場合は、読み続けてください。
Xamarin.Formsの公式ドキュメントはすべて、MVVMの純粋なソリューションですが、シンプルではありませんが、わずかなものです。これは、Page
(ビュー)がについて何も知らないためで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
間のナビゲーションの代わりにナビゲートできるようにする、少し複雑ですがよりクリーンなアプローチです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
およびその逆のオブジェクトが必要です。2番目のケースは、アプリのルート/メインページを設定する場合に役立ちます。そのため、すべてのViewModels
がViewModels
ディレクトリにあり、Pages
(ビュー)がディレクトリにあるという単純な規則に同意する必要がありますViews
。つまりViewModels
、[MyApp].ViewModels
名前空間とPages
(ビュー)名前空間に存在する必要があり[MyApp].Views
ます。それに加えて、WelcomeView
(ページ)には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.最後に、私たちはアプローチNavigationService
をサポートする必要がありますViewModel First Navigation
:
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 eBookを使用した無料のエンタープライズアプリケーションパターンがあり、これと他の多くの興味深いトピックを詳細に説明しています。
PushAsync()メソッドを使用してプッシュすることができ、PopModalAsync()はナビゲーションスタックとの間でページをポップできます。以下のコード例には、ナビゲーションページ(ルートページ)があり、このページから、ログインページが完成したら、ログインページであるコンテンツページをプッシュします。ルートページに戻ります。
~~~ナビゲーションは、ページオブジェクトの後入れ先出しスタックと考えることができます。あるページから別のページに移動するには、アプリケーションが新しいページをこのスタックにプッシュします。前のページに戻るために、アプリケーションはスタックから現在のページをポップします。Xamarin.Formsのこのナビゲーションは、INavigationインターフェイスによって処理されます
Xamarin.Formsには、このインターフェイスを実装し、ページのスタックを管理するNavigationPageクラスがあります。NavigationPageクラスは、タイトルを表示する画面の上部にナビゲーションバーを追加し、前のページに戻るプラットフォームに適した[戻る]ボタンも備えています。次のコードは、アプリケーションの最初のページをNavigationPageでラップする方法を示しています。
上記のコンテンツへの参照とXamarinフォームの詳細について確認する必要があるリンクについては、ナビゲーションセクションを参照してください。
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();
}
Navigationプロパティを使用したXamarin.formsのあるページから別のページへのナビゲーションサンプルコードの下
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());
}
ビューセル内の1つのページを別のページに移動するにはコードの下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;
}
In App.Xaml.Cs:
MainPage = new NavigationPage( new YourPage());
YourPageから次のページに移動する場合は、次のようにします。
await Navigation.PushAsync(new YourSecondPage());
Xamarinフォームナビゲーションの詳細については、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/
XamarinにはNavigationPageというページがあります。ContentPageのスタックを保持します。NavigationPageには、PushAsync()およびPopAsync()のようなメソッドがあります。PushAsyncはスタックの最上部にページを追加します。そのとき、そのページページは現在アクティブなページになります。PopAsync()メソッドは、スタックの最上部からページを削除します。
App.Xaml.Csでは、次のように設定できます。
MainPage = new NavigationPage(new YourPage());
await Navigation.PushAsync(new newPage()); このメソッドは、スタックの一番上にnewPageを追加します。この時点で、nePageは現在アクティブなページになります。