Xamarin Forms에서 페이지 간을 전환하는 방법은 무엇입니까?
내 메인 페이지는 ContentPage이고 탭 페이지와 같은 것으로 전환하고 싶지 않습니다.
ContentPage를 찾을 때까지 새 페이지를 트리거해야하는 컨트롤의 부모를 찾은 다음 콘텐츠를 새 페이지에 대한 컨트롤로 교체하여 의사 작업을 수행 할 수있었습니다. 하지만 이것은 정말 조잡 해 보입니다.
Xamarin Forms에서 페이지 간을 전환하는 방법은 무엇입니까?
내 메인 페이지는 ContentPage이고 탭 페이지와 같은 것으로 전환하고 싶지 않습니다.
ContentPage를 찾을 때까지 새 페이지를 트리거해야하는 컨트롤의 부모를 찾은 다음 콘텐츠를 새 페이지에 대한 컨트롤로 교체하여 의사 작업을 수행 할 수있었습니다. 하지만 이것은 정말 조잡 해 보입니다.
답변:
Xamarin.Forms
기본 제공되는 여러 탐색 호스트를 지원합니다.
NavigationPage
, 다음 페이지가 들어가는 곳,TabbedPage
, 당신이 좋아하지 않는CarouselPage
, 왼쪽과 오른쪽을 다음 / 이전 페이지로 전환 할 수 있습니다.또한 모든 페이지는 PushModalAsync()
기존 페이지 위에 새 페이지를 푸시하는 기능도 지원합니다 .
마지막에 사용자가 이전 페이지로 돌아갈 수 없도록하려면 (제스처 또는 하드웨어 뒤로 버튼 사용) 동일한 항목을 계속 Page
표시하고 Content
.
루트 페이지 교체에 대해 제안 된 옵션도 작동하지만 각 플랫폼에 대해 다르게 처리해야합니다.
App 클래스에서 MainPage를 탐색 페이지로 설정하고 루트 페이지를 ContentPage로 설정할 수 있습니다.
public App ()
{
// The root page of your application
MainPage = new NavigationPage( new FirstContentPage() );
}
그런 다음 첫 번째 ContentPage 호출에서 :
Navigation.PushAsync (new SecondContentPage ());
Android.Content.Res
탐색을 위해 가져 오기 를 제안 합니다. 옳지 않은 것 같습니다. 어디서 가져와야합니까?
프로젝트가 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();
Navigation
이 예에서 ? -어딘가에서 만든 물건입니까? -이 코드 샘플에서는 볼 수 없습니다.
PushAsync()
동안, 나를 위해 일을하지 않았다 PushModalAsync()
않았다
새 페이지를 스택에 넣은 다음 현재 페이지를 제거합니다. 이로 인해 전환이 발생합니다.
item.Tapped += async (sender, e) => {
await Navigation.PushAsync (new SecondPage ());
Navigation.RemovePage(this);
};
먼저 탐색 페이지에 있어야합니다.
MainPage = NavigationPage(new FirstPage());
OnAppearing과 같은 하나의 큰 페이지와 하나의 페이지 이벤트 세트가 있기 때문에 컨텐츠 전환은 이상적이지 않습니다.
Navigation.RemovePage();
Android에서는 지원되지 않습니다.
await Navigation.PushAsync(new SecondPage(),false);
이 스레드는 매우 인기있는 것처럼 보이며 여기에 다른 방법이 있다는 것을 언급하지 않아도 슬플 것 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. 먼저 모든 객체를 등록하고 선택적으로 수명을 정의 할 수있는 장소가 필요합니다. 이 문제를 위해 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
특정에 대한 (View) 검색을 담당하는 개체가 필요 ViewModel
하며 그 반대의 경우도 마찬가지입니다. 두 번째 경우는 앱의 루트 / 메인 페이지를 설정하는 경우 유용 할 수 있습니다. 이를 위해 우리는 모든가 디렉터리에 ViewModels
있어야 ViewModels
하고 Pages
(보기)가 디렉터리에 있어야 한다는 간단한 규칙에 동의해야합니다 Views
. 즉 ViewModels
, [MyApp].ViewModels
네임 스페이스에 있고 Pages
(Views)가 [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. 루트 페이지를 설정하는 경우 자동으로 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에서 사용할 수 있습니다 . 접근 방식
에 대한 좋은 기사가 많이 있으며 Xamarin.Forms eBook을 사용ViewModel First Navigation
하는 무료 엔터프라이즈 애플리케이션 패턴이 있으며이 항목과 기타 흥미로운 주제를 자세히 설명합니다.
PushAsync () 메서드를 사용하여 푸시 및 PopModalAsync ()를 사용하여 탐색 스택에서 페이지를 팝할 수 있습니다. 아래 코드 예제에는 탐색 페이지 (루트 페이지)가 있으며이 페이지에서 로그인 페이지가 완료되면 로그인 페이지 인 콘텐츠 페이지를 푸시하면 루트 페이지로 다시 팝업됩니다.
~~~ 탐색은 페이지 개체의 후입 선출 스택으로 생각할 수 있습니다. 한 페이지에서 다른 페이지로 이동하기 위해 응용 프로그램은이 스택에 새 페이지를 푸시합니다. 이전 페이지로 돌아 가기 위해 애플리케이션은 스택에서 현재 페이지를 팝합니다. Xamarin.Forms의이 탐색은 INavigation 인터페이스에서 처리됩니다.
Xamarin.Forms에는이 인터페이스를 구현하고 페이지 스택을 관리하는 NavigationPage 클래스가 있습니다. NavigationPage 클래스는 또한 제목을 표시하는 화면 상단에 탐색 모음을 추가하고 이전 페이지로 돌아가는 플랫폼에 적합한 뒤로 단추도 포함합니다. 다음 코드는 애플리케이션의 첫 페이지 주위에 NavigationPage를 래핑하는 방법을 보여줍니다.
위에 나열된 콘텐츠에 대한 참조와 Xamarin Forms에 대한 자세한 내용을 검토해야하는 링크는 탐색 섹션을 참조하세요.
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.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);
}
}
요구:
((App)App.Current).ChangeScreen(new Map());
App.xaml.cs 내에이 메서드를 만듭니다.
public void ChangeScreen(Page page)
{
MainPage = page;
}
In App.Xaml.Cs:
MainPage = new NavigationPage( new YourPage());
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/
Xamarin에는 NavigationPage라는 페이지가 있습니다. ContentPages 스택을 보유합니다. NavigationPage에는 PushAsync () 및 PopAsync ()와 같은 메서드가 있습니다. PushAsync는 스택 맨 위에 페이지를 추가하면 해당 페이지가 현재 활성 페이지가됩니다. PopAsync () 메서드는 스택 맨 위에서 페이지를 제거합니다.
App.Xaml.Cs에서 다음과 같이 설정할 수 있습니다.
MainPage = new NavigationPage (new YourPage ());
await Navigation.PushAsync (new newPage ()); 이 메서드는 스택 맨 위에 newPage 를 추가 합니다. 이때 nePage는 현재 활성 페이지입니다.