如何在Xamarin Forms中切换页面?

时间:2014-08-06 16:05:46

标签: xamarin xamarin.forms

如何在Xamarin Forms中切换页面?我的主页是ContentPage,我不想切换到标签页。

我已经能够通过找到应触发新页面的控件的父级来伪造它,直到我找到ContentPage,然后将内容与控件换成新页面。但这看起来真的很草率。

由于

13 个答案:

答案 0 :(得分:59)

Xamarin.Forms支持内置多个导航主机:

  • NavigationPage,其中下一页滑入,
  • TabbedPage,你不喜欢的那个
  • CarouselPage,允许左右切换到下一页/上一页。

除此之外,所有页面还支持PushModalAsync(),只需在现有页面之上推送新页面。

最后,如果您想确保用户无法返回上一页(使用手势或后退硬件按钮),您可以保持显示相同的Page替换其Content

替换根页的建议选项也适用,但您必须为每个平台处理不同的方式。

答案 1 :(得分:52)

在App类中,您可以将MainPage设置为导航页面并将根页面设置为您的ContentPage:

public App ()
{
    // The root page of your application
    MainPage = new NavigationPage( new FirstContentPage() );
}

然后在您的第一个ContentPage调用中:

Navigation.PushAsync (new SecondContentPage ());

答案 2 :(得分:36)

如果您的项目已经设置为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();

答案 3 :(得分:22)

将新页面推入堆栈,然后删除当前页面。这导致切换。

.unit

您需要首先进入导航页面:

item.Tapped += async (sender, e) => {
    await Navigation.PushAsync (new SecondPage ());
    Navigation.RemovePage(this);
};

切换内容并不理想,因为您只有一个大页面和一组页面事件,如OnAppearing等。

答案 4 :(得分:6)

如果您不想转到上一页,即在授权完成后不让用户返回登录屏幕,那么您可以使用;

 App.Current.MainPage = new HomePage();

如果您想启用后退功能,请使用

Navigation.PushModalAsync(new HomePage())

答案 5 :(得分:3)

通过使用PushAsync()方法,您可以推送PopModalAsync(),您可以在导航堆栈中弹出页面。在我的下面的代码示例中,我有一个导航页面(根页面),在我完成登录页面后,我会从这个页面推送一个登录页面的内容页面,然后返回到根页面

~~~导航可以被认为是Page对象的后进先出堆栈。要从一个页面移动到另一个页面,应用程序会将新页面推送到此堆栈。要返回上一页,应用程序将从堆栈中弹出当前页面。 Xamarin.Forms中的此导航由INavigation接口

处理

Xamarin.Forms有一个NavigationPage类,它实现了这个接口,并将管理Pages的堆栈。 NavigationPage类还将在屏幕顶部添加一个显示标题的导航栏,并且还有一个适合平台的Back按钮,该按钮将返回到上一页。以下代码显示如何围绕应用程序中的第一页包装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();
}

答案 6 :(得分:3)

似乎这个帖子非常受欢迎,如果有另一种方法 - 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之间导航,而不是在Pages(视图)之间导航。明确区分关注点的一个优点是,您可以轻松地将参数传递给下一个ViewModel或在导航后立即执行异步初始化代码。现在详细说明。

(我将尝试尽可能简化所有代码示例)。

1.首先,我们需要一个可以注册所有对象并可选择定义其生命周期的地方。对于这个问题,我们可以使用IOC容器,您可以自己选择一个。在这个例子中,我将使用Autofac(它是速度最快的之一)。我们可以在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的{​​{1}}(视图)的对象,反之亦然。第二种情况可能在设置应用程序的根/主页面时很有用。为此,我们应该就一个简单的约定达成一致,即所有ViewModel都应该在ViewModels目录中,ViewModels(视图)应该在Pages目录中。换句话说,Views应该位于ViewModels命名空间中的[MyApp].ViewModelsPages(视图)中。除此之外,我们应该同意[MyApp].Views(Page)应该有WelcomeView等。以下是映射器的代码示例:

WelcomeViewModel

3.对于设置根页面的情况,我们需要一些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); } 来自动设置ViewModelLocator

BindingContext

4.最后,我们需要一个支持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> 方法的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

如您所知,这种方法更复杂,更难调试,可能会让人感到困惑。然而,由于大多数MVVM框架都支持开箱即用,因此有许多优点,而且您实际上无需自己实现它。此处演示的代码示例可在github上找到。

有很多关于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>()); } } 方法的好文章,还有一本免费的Enterprise Application Patterns using Xamarin.Forms电子书,它详细解释了这个以及许多其他有趣的主题。

答案 7 :(得分:2)

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());
        }

使用视图单元格将一个页面导航到另一个页面下面的代码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);
        }

    }

答案 8 :(得分:1)

呼叫:

((App)App.Current).ChangeScreen(new Map());

在App.xaml.cs中创建此方法:

public void ChangeScreen(Page page)
{
     MainPage = page;
}

答案 9 :(得分:0)

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());
        }

答案 10 :(得分:0)

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/

有关Shell基础的视频:https://www.youtube.com/watch?v=0y1bUAcOjZY&t=3112s

文档:https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/shell/

答案 11 :(得分:0)

PushAsync之后,使用PopAsync(带有this)删除当前页面。

await Navigation.PushAsync(new YourSecondPage());
this.Navigation.PopAsync(this);

答案 12 :(得分:0)

在Xamarin中,我们有一个名为NavigationPage的页面。它包含ContentPages堆栈。 NavigationPage具有类似PushAsync()和PopAsync()的方法。 PushAsync在堆栈顶部添加一个页面,届时该页面将成为当前活动页面。 PopAsync()方法从堆栈顶部删除页面。

在App.Xaml.Cs中,我们可以设置为

MainPage = new NavigationPage(new YourPage());

等待Navigation.PushAsync(new newPage());此方法将在堆栈顶部添加 newPage 。此时,nePage将是当前活动页面。