Как вы переключаете страницы в Xamarin Forms?

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

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

Спасибо

Ответ 1

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

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

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

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

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

Ответ 2

В классе App вы можете настроить MainPage на страницу навигации и установить корневую страницу в ContentPage:

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

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

Navigation.PushAsync (new SecondContentPage ());

Ответ 3

Если ваш проект был настроен как проект форм 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();

Ответ 4

Вставьте новую страницу в стек, затем удалите текущую страницу. Это приводит к переключению.

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

Сначала вы должны быть на странице навигации:

MainPage = NavigationPage(new FirstPage());

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

Ответ 5

Если вы не хотите идти на предыдущую страницу, то есть не позволяйте пользователю вернуться к экрану входа в систему после авторизации, вы можете использовать;

 App.Current.MainPage = new HomePage();

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

Navigation.PushModalAsync(new HomePage())

Ответ 6

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

~~~ Навигацию можно рассматривать как стек последнего объекта, который находится в первом порядке. Чтобы перейти с одной страницы на другую, приложение переместит новую страницу в этот стек. Чтобы вернуться к предыдущей странице, приложение выведет текущую страницу из стека. Эта навигация в 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();
}

Ответ 7

Похоже, эта тема очень популярна, и будет грустно не упомянуть, что есть альтернативный способ - 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 (представление) для определенной ViewModel и наоборот. Второй случай может быть полезен в случае установки корневой/главной страницы приложения. Для этого мы должны договориться о простом соглашении, что все ViewModels должны быть в каталоге ViewModels а Pages (Views) должны быть в каталоге Views. Другими словами, ViewModels должны [MyApp].ViewModels пространстве имен [MyApp].ViewModels а Pages (Views) в пространстве имен [MyApp].Views. В дополнение к этому мы должны согласиться, что WelcomeView (Page) должен иметь WelcomeViewModel и т.д. Вот пример кода 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, которая подробно объясняет эту и многие другие интересные темы.

Ответ 8

Навигация с одной страницы на другую в 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);
        }

    }

Ответ 9

Вызов:

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

Создайте этот метод внутри App.xaml.cs:

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

Ответ 10

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

Ответ 11

Я Али Мизани Оскуи, исследователь, новатор и футурист, который увлечен миром криптовалюты и блокчейна. Я начал свой собственный бизнес в 1999 году (когда мне было 24 года), основав CITEX Co., специализирующейся на разработке программного обеспечения для автоматизации делопроизводства, систем учета времени и посещаемости и систем автоматизации ресторанов.

Ответ 12

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/.