Как вы переключаете страницы в 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/.