Как переключать страницы в формах Xamarin?



Как вы переключаетесь между страницами в формах Xamarin? Моя главная страница-это ContentPage, и я не хочу переключаться на что-то вроде страницы с вкладками.



Я был в состоянии псевдо-сделать это, найдя родителей элементов управления, которые должны вызвать новую страницу, пока я не найду ContentPage, а затем поменять содержимое с элементами управления для новой страницы. Но это выглядит очень неаккуратно.



спасибо

2765   10  

10 ответов:

Xamarin.Forms поддержка нескольких навигационных хостов встроенный:

  • NavigationPage, где следующая страница слайд,
  • TabbedPage, тот, который вам не нравится
  • CarouselPage, что позволяет переключаться влево и вправо на следующие/предыдущие страницы.

кроме того, все страницы также поддерживают PushModalAsync() которые просто нажимают новую страницу поверх существующей.

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

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

в классе приложения вы можете установить главную страницу на страницу навигации и установить корневую страницу на страницу содержимого:

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

затем в вашем первом вызове ContentPage:

Navigation.PushAsync (new SecondContentPage ());

Если ваш проект был настроен как проект PCL forms (и, скорее всего, как общие формы, но я этого не пробовал), есть приложение класса.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());

переключение контента не идеально, так как у вас есть только одна большая страница и один набор событий страницы, таких как onappearing ect.

С помощью метода PushAsync() вы можете нажать и PopModalAsync() вы можете поп-страницы и из стека навигации. В моем примере кода ниже у меня есть страница навигации( корневая страница), и с этой страницы я нажимаю страницу содержимого, которая является страницей входа, как только я завершу свою страницу входа, я вернусь на корневую страницу

~~~ навигация может рассматриваться как последний вход, первый выход стек страницы objects.To перемещение с одной страницы на другую приложение будет толкать новую страницу в этот стек. Чтобы вернуться на предыдущую страницу, приложение выведет текущую страницу из стека. Это навигация в Xamarin.Формы обрабатываются интерфейсом INavigation

Xamarin.Forms имеет класс 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();
}

переход от одной страницы к другой в Xamarin.формы с использованием навигация свойства ниже пример кода

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

для перехода с одной страницы на другую страницу с ячейкой in view ниже кода Xamrian.формы

 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.Current.MainPage = new HomePage();

Если вы хотите снова включить функцию, просто использовать

Navigation.PushModalAsync(new HomePage())

звоните:

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

создать этот метод внутри приложения.код XAML.cs:

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

похоже, что эта тема очень популярна, и будет грустно не упомянуть здесь, что есть альтернативный способ - ViewModel First Navigation. Большинство фреймворков MVVM там используют его, однако, если вы хотите понять, что это такое, продолжайте читать.

все официальные Xamarin.Документация форм демонстрирует простое, но немного не чистое решение 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. Прежде всего, нам нужно место, где мы могли бы зарегистрировать все наши объекты и, возможно, определить их срок службы. Для этого вопроса мы можем использовать контейнер МОК, вы можете выбрать один самостоятельно. В этом примере я буду использовать 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 (вид) для конкретного ViewModel и наоборот. Второй случай может быть полезен в случае установки корневой/главной страницы приложения. Для этого мы должны договориться о простой конвенции, что все ViewModels должно быть . Другими словами 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.Для случая установки корневой страницы нам понадобится sort of 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.Формы электронная книга, которая подробно объясняет это и многие другие интересные темы.

страница 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());
        }

Comments

    Ничего не найдено.