Як ви перемикаєте сторінки у формах Xamarin.Forms?


99

Як ви переходите між сторінками у формах Xamarin?

Моя головна сторінка - це ContentPage, і я не хочу переходити на щось на зразок сторінки з вкладками.

Мені вдалося виконати псевдо-пошук, знайшовши батьків органів управління, які повинні запускати нову сторінку, поки я не знайду ContentPage, а потім замінюю вміст елементами управління на нову сторінку. Але це здається справді неохайним.


Там були вже багато відповідей на це питання, щоб побачити , як це може бути зроблено з допомогою структурної схеми MVVM, зверніться до цього stackoverflow.com/a/37142513/9403963
Аліреза Sattari

Відповіді:


67

Xamarin.Forms підтримує кілька вбудованих хостів навігації:

  • NavigationPage, де слайд наступної сторінки,
  • TabbedPage, той, який вам не подобається
  • CarouselPage, що дозволяє переходити вліво та вправо на наступну / попередню сторінки.

Крім цього, всі сторінки також підтримують, PushModalAsync()які просто натискають нову сторінку поверх існуючої.

Зрештою, якщо ви хочете переконатися, що користувач не може повернутися до попередньої сторінки (за допомогою жесту або кнопки задньої апаратури), ви можете зберегти те ж саме Pageта замінити його Content.

Запропоновані варіанти заміни кореневої сторінки також працюють, але вам доведеться це по-різному обробляти для кожної платформи.


PushModalAsync, здається, є частиною Навігації, правда? Не можу зрозуміти, як дістатися до об’єкта / класу Навігації. Я припускаю, що мені потрібно отримати доступ до чогось, що реалізує INavigation, але що?
Ерік

Якщо ваша сторінка міститься на сторінці навігації, ви повинні мати доступ до властивості навігації зсередини своєї сторінки
Jason

1
Як тільки я почав користуватися NavigationPage, все стало на свої місця. Спасибі
Ерік

1
@stephane ласка скажіть , якщо моя перша сторінка CarouselPage і моя друга сторінка masterDetailPage то як я можу перемкнути сторінку stackoverflow.com/questions/31129845 / ...
Атул Dhanuka

64

У класі App ви можете встановити MainPage на сторінку навігації та встановити кореневу сторінку на свою ContentPage:

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

Потім у своєму першому дзвінку на ContentPage:

Navigation.PushAsync (new SecondContentPage ());

Я це зробив, але все ж головна сторінка - це сторінка за замовчуванням, яка відкривається. Будь-яка сторінка, яку я встановив на головну сторінку, не впливає. Я просто відкриваю першу сторінку, яку встановив. В чому проблема?
Бехзад

Visual Studio пропонує імпортувати Android.Content.Resдля навігації. Це не здається правильним, звідки мені це імпортувати?
Крістіан

41

Якщо ваш проект створений як проект форми PCL (і, швидше за все, як Shared Forms, але я цього не пробував), існує клас 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
Це не переключається між сторінками. Це лише змінює, яка сторінка завантажується спочатку.
dakamojo

Ваше запитання стосувалося головної сторінки. дивіться оновлену відповідь для прикладів навігації
Sten Petrov

Що за чорт Navigationу цьому прикладі? - Це об’єкт, який ви десь створили? - Я не бачу цього в зразку коду.
BrainSlugs83

Навігація є власністю на сторінці
Стен Петров

Дякую; FTR PushAsync()не працював для мене, в той час як PushModalAsync()зробив
knocte

23

Натисніть на нову сторінку на стек, а потім видаліть поточну сторінку. Це призводить до перемикання.

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

Потрібно спочатку бути на сторінці навігації:

MainPage = NavigationPage(new FirstPage());

Переключення вмісту не є ідеальним, оскільки у вас є лише одна велика сторінка та один набір подій сторінки, як-от OnAppearing ect.


Navigation.RemovePage();не підтримується на Android.
Rohit Vipin Mathews

1
Navigation.RemovePage (сторінка); працює в Android, спочатку потрібно опинитися на навігаційній сторінці.
Даніель Робертс

Я широко використовую його у своєму проекті на формах 1.4.2. Можливо, вони виправили помилку, або мені просто пощастило і ще не вдарив.
Даніель Робертс

Я перебуваю в останній версії і можу її повторити. Тож я вважаю, що вам надто пощастило.
Rohit Vipin Mathews

2
Підказка зручна - щоб видалити переходи під час зміни сторінки, додайте помилку як другий параметр:await Navigation.PushAsync(new SecondPage(),false);
Damian Green

8

Якщо ви не хочете переходити на попередню сторінку, тобто не дозволяєте користувачеві повертатися до екрана входу після завершення авторизації, ви можете використовувати;

 App.Current.MainPage = new HomePage();

Якщо ви хочете включити функціональність назад, просто використовуйте

Navigation.PushModalAsync(new HomePage())

4

Здається, ця нитка дуже популярна, і буде сумно не згадувати тут, що існує альтернативний спосіб - 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(Перегляди). Однією з переваг, крім чіткого розділення проблем, є те, що ви можете легко передати параметри наступномуViewModel або виконати код ініціалізації асинхронізації відразу після навігації. Тепер до деталей.

(Я спробую максимально спростити всі приклади коду).

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(View) для конкретного, ViewModelі навпаки. Другий випадок може бути корисним у випадку встановлення кореневої / головної сторінки програми. Для цього ми повинні домовитись про просту конвенцію про те, що все 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.Зазвичай нам знадобиться 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, яка детально пояснює це та багато інших цікавих тем.


3

Використовуючи метод PushAsync (), ви можете натискати, а PopModalAsync () ви можете переходити сторінки до та з навігаційного стеку. У наведеному нижче прикладі коду я маю сторінку навігації (Root Page), і з цієї сторінки я натискаю сторінку вмісту, яка є сторінкою входу, коли я закінчую свою сторінку входу, я повертаюсь на кореневу сторінку

~~~ Навігацію можна розглядати як останню, першу стек об'єктів Сторінки. Для переміщення з однієї сторінки на іншу програма додасть нову сторінку на цей стек. Щоб повернутися до попередньої сторінки, додаток виведе поточну сторінку зі стека. Ця навігація в Xamarin.Forms обробляється інтерфейсом INavigation

Xamarin.Forms має клас NavigationPage, який реалізує цей інтерфейс і керує стеком Pages. Клас 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();
}

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

    }

2

Виклик:

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

Створіть цей метод всередині App.xaml.cs:

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

2
In App.Xaml.Cs:

MainPage = new NavigationPage( new 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/

Відео про основи Shell: https://www.youtube.com/watch?v=0y1bUAcOjZY&t=3112s

Документи: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/shell/


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

0

Після PushAsyncвикористання PopAsyncthis) для видалення поточної сторінки.

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

0

У Xamarin у нас є сторінка під назвою NavigationPage. Він містить стек ContentPages. NavigationPage має такі методи, як PushAsync () та PopAsync (). PushAsync додає сторінку у верхній частині стека, тоді ця сторінка стане поточно активною сторінкою. Метод PopAsync () видалить сторінку вгорі стека.

У App.Xaml.Cs ми можемо встановити як.

MainPage = нова навігаційна сторінка (нова ваша сторінка ());

чекайте навігації.PushAsync (нова нова сторінка ()); цей метод додасть newPage у верхній частині стека. Наразі nePage буде активною сторінкою.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.