Xamarin Shell 向后导航时出错 System.ArgumentException
Xamarin Shell Error While Doing Backwards Navigation System.ArgumentException
我正在开发一个包含三个页面的 android 应用程序。第一个是主页。在此页面上,您可以单击所需的 object,您将被转到 object 页面,其中包含相关图片、标题和说明。通过单击图像,您将转到一个页面,该页面显示响应用户手势(缩放和平移)的放大图像。
到目前为止一切正常。当我想从图像页面返回到上一页(包含 object 信息)时,问题就出现了。此页面需要 object id 来显示功能,因此,我从图像页面 运行 返回以下代码:
await Shell.Current.GoToAsync($"..?ObjectId={ObjectId}");
但是,尽管在 AppShell.xaml.cs 文件中注册了所有页面(视图),但当我尝试返回时,我得到了这个异常:
System.ArgumentException: 'Ambiguous routes matched for: //D_FAULT_TabBar11/D_FAULT_Tab6/HomePage/ObjectPage?ObjectId= matches found: //D_FAULT_TabBar11/D_FAULT_Tab6/HomePage/ObjectPage,//D_FAULT_TabBar11/D_FAULT_Tab6/HomePage/ObjectPage. Parameter name: uri'
你能帮我解决这个问题吗?我(几乎)在网上搜索过,但找不到任何有用的答案。提前致谢!
更新:
这是 HomePageViewModel 页面,我从中 select 收集视图中的 object。 OnObjectTap 方法在用户单击 object 并将其带到包含有关 object 的信息的选项卡时被调用。
这段代码有效
class HomePageViewModel : BindableObject {
public ICommand ObjectTapCollectionView { get; }
public HomePageViewModel () {
ObjectTapCollectionView = new Command(OnObjectTap);
}
private static ObservableCollection<MyObject> objectList = new ObservableCollection<MyObject>();
public ObservableCollection<MyObject> ObjectList {
get { return objectList ; }
set {
objectList = value;
OnPropertyChanged();
}
}
private MyObject selectedObject;
public MyObject SelectedObject {
get { return selectedObject; }
set { selectedObject = value; }
}
private async void OnObjectTap() {
await Shell.Current.GoToAsync($"{nameof(ObjectPage)}?ObjectId={selectedObject.Object_Id}");
}
}
这是 ObjectViewModel 页面。 'ObjectId' 参数传递到此页面。如果用户单击 object 的图像,他会自动转到 FullScreenImagePage,从中他可以放大 image.This 代码 works
[QueryProperty(nameof(ObjectId), nameof(ObjectId))]
class ObjectPage : BindableObject {
public ICommand ImageTapCarouselView { get; }
public ObjectViewModel () {
ImageTapCarouselView = new Command(OnImageTap);
}
private MyObject myObj = new MyObject ();
public MyObject MyObj {
get { return myObj ; }
set {
myObj = value;
OnPropertyChanged();
}
}
private String objectId = String.Empty;
public String ObjectId {
get { return objectId ; }
set {
objectId = Uri.UnescapeDataString(value ?? string.Empty);
//Trovo l'esercizio selezionato
MyObject = ObjectListService.objects.Find(x => x.Object_Id == ObjectId);
OnPropertyChanged();
}
}
private ObservableCollection<String> objectImageList = new ObservableCollection<String>();
public ObservableCollection<String> ObjectImageList {
get { return objectImageList ; }
set {
objectImageList = value;
OnPropertyChanged();
}
}
private async void OnImageTap(object obj) {
String imagePath = (String)obj;
await Shell.Current.GoToAsync($"{nameof(FullScreenImagePage)}?ObjectId={MyObj.Object_Id}&ImagePath={imagePath}");
}
}
这是全屏图像视图模型页面。 'ObjectId' 和 'imagePath' 参数从上面的函数 'OnImageTap' 传递到此页面。这里的问题在
[QueryProperty(nameof(ObjectId ), nameof(ObjectId ))]
[QueryProperty(nameof(ImagePath), nameof(ImagePath))]
class FullScreenImageViewModel : BindableObject {
public FullScreenImageViewModel () { }
private String objectId = String.Empty;
public String ObjectId {
get { return objectId ; }
set {
objectId = value;
}
}
private String imagePath = String.Empty;
public String ImagePath {
get { return imagePath; }
set {
imagePath = value;
OnPropertyChanged();
}
}
public async void OnBackButtonPressed() {
//await Shell.Current.GoToAsync("..");
await Shell.Current.GoToAsync($"..?ObjectId={ObjectId}");
}
在上面的 'OnBackButtonPressed' 函数中,应用程序使用任一方法都会抛出异常。
第一种情况,异常是这样的:
System.ArgumentException: 'Ambiguous routes matched for: //D_FAULT_TabBar9/D_FAULT_Tab6/UserHomePage/ClientExercisePage matches found: //D_FAULT_TabBar9/D_FAULT_Tab6/UserHomePage/ClientExercisePage,//D_FAULT_TabBar9/D_FAULT_Tab6/UserHomePage/ClientExercisePage. Parameter name: uri'
在第二种情况下是这样的(它们实际上是相同的。在第二种情况下 'match not found' 之前还有 '?ObjectId=ff2dcfd1eecf7877':
System.ArgumentException: 'Ambiguous routes matched for: //D_FAULT_TabBar11/D_FAULT_Tab6/HomePage/ObjectPage?ObjectId=ff2dcfd1eecf7877 matches found: //D_FAULT_TabBar11/D_FAULT_Tab6/HomePage/ObjectPage,//D_FAULT_TabBar11/D_FAULT_Tab6/HomePage/ObjectPage. Parameter name: uri'
从Xamarin.ShellPass data,您可以使用 await Shell.Current.GoToAsync($"elephantdetails?name={elephantName}");
导航到另一个带参数的
首先,我创建了两个contentPage,一个是HomePage,CollectionView用来显示List数据。另一个页面是Objectdetailpage,显示详细信息。
<CollectionView
x:Name="collectionview1"
ItemsSource="{Binding objectList}"
SelectedItem="{Binding selecteditem}"
SelectionMode="Single">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding objectId}" />
<Label Text="{Binding name}" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
public class HomePageViewModel:ViewModelBase
{
public ObservableCollection<MyObject> objectList { get; set; }
private MyObject _selecteditem;
public MyObject selecteditem
{
get { return _selecteditem; }
set
{
_selecteditem = value;
RaisePropertyChanged("selecteditem");
if(_selecteditem!=null)
{
Shell.Current.GoToAsync($"objectdetailpage?objectId={selecteditem.objectId}");
}
}
}
public ICommand ObjectTap { get; set; }
public HomePageViewModel()
{
objectList = new ObservableCollection<MyObject>();
ObjectTap = new Command(gotomethod);
for (int i=0;i<10; i++)
{
MyObject myobject = new MyObject() { objectId = i, name = "object " + i };
objectList.Add(myobject);
}
}
}
注意:需要实现INotifyPropertyChanged
通知selecteditem数据改变。
public class MyObject
{
public int objectId { get; set; }
public string name { get; set; }
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
导航到详细页面。 IQueryAttributable
接口指定实现 class 必须实现 ApplyQueryAttributes
方法。此方法有一个 IDictionary<string, string>
类型的查询参数,其中包含导航期间传递的任何数据。
<StackLayout>
<Label Text="{Binding myobject.objectId}" />
<Label Text="{Binding myobject.name}" />
</StackLayout>
public partial class ObjectDetailPage : ContentPage
{
public ObjectDetailPage()
{
InitializeComponent();
this.BindingContext = new objectviewmodel();
}
}
public class objectviewmodel : IQueryAttributable, INotifyPropertyChanged
{
private MyObject _myobject;
public MyObject myobject
{
get { return _myobject; }
set
{
_myobject = value;
OnPropertyChanged("myobject");
}
}
public void ApplyQueryAttributes(IDictionary<string, string> query)
{
string objectId = HttpUtility.UrlDecode(query["objectId"]);
LoadObject(objectId);
}
private void LoadObject(string objectId)
{
if(objectId!=null)
{
myobject = (new HomePageViewModel()).objectList.FirstOrDefault(a => a.objectId ==int.Parse(objectId));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
如果你想通过点击BackButton返回,你不需要添加任何代码,只需要点击BackButton,它会触发ApplyQueryAttributes
方法来获取参数。
因此您可以删除 OnBackButtonPressed
,只需单击返回按钮即可返回。
我正在开发一个包含三个页面的 android 应用程序。第一个是主页。在此页面上,您可以单击所需的 object,您将被转到 object 页面,其中包含相关图片、标题和说明。通过单击图像,您将转到一个页面,该页面显示响应用户手势(缩放和平移)的放大图像。
到目前为止一切正常。当我想从图像页面返回到上一页(包含 object 信息)时,问题就出现了。此页面需要 object id 来显示功能,因此,我从图像页面 运行 返回以下代码:
await Shell.Current.GoToAsync($"..?ObjectId={ObjectId}");
但是,尽管在 AppShell.xaml.cs 文件中注册了所有页面(视图),但当我尝试返回时,我得到了这个异常:
System.ArgumentException: 'Ambiguous routes matched for: //D_FAULT_TabBar11/D_FAULT_Tab6/HomePage/ObjectPage?ObjectId= matches found: //D_FAULT_TabBar11/D_FAULT_Tab6/HomePage/ObjectPage,//D_FAULT_TabBar11/D_FAULT_Tab6/HomePage/ObjectPage. Parameter name: uri'
你能帮我解决这个问题吗?我(几乎)在网上搜索过,但找不到任何有用的答案。提前致谢!
更新:
这是 HomePageViewModel 页面,我从中 select 收集视图中的 object。 OnObjectTap 方法在用户单击 object 并将其带到包含有关 object 的信息的选项卡时被调用。 这段代码有效
class HomePageViewModel : BindableObject {
public ICommand ObjectTapCollectionView { get; }
public HomePageViewModel () {
ObjectTapCollectionView = new Command(OnObjectTap);
}
private static ObservableCollection<MyObject> objectList = new ObservableCollection<MyObject>();
public ObservableCollection<MyObject> ObjectList {
get { return objectList ; }
set {
objectList = value;
OnPropertyChanged();
}
}
private MyObject selectedObject;
public MyObject SelectedObject {
get { return selectedObject; }
set { selectedObject = value; }
}
private async void OnObjectTap() {
await Shell.Current.GoToAsync($"{nameof(ObjectPage)}?ObjectId={selectedObject.Object_Id}");
}
}
这是 ObjectViewModel 页面。 'ObjectId' 参数传递到此页面。如果用户单击 object 的图像,他会自动转到 FullScreenImagePage,从中他可以放大 image.This 代码 works
[QueryProperty(nameof(ObjectId), nameof(ObjectId))]
class ObjectPage : BindableObject {
public ICommand ImageTapCarouselView { get; }
public ObjectViewModel () {
ImageTapCarouselView = new Command(OnImageTap);
}
private MyObject myObj = new MyObject ();
public MyObject MyObj {
get { return myObj ; }
set {
myObj = value;
OnPropertyChanged();
}
}
private String objectId = String.Empty;
public String ObjectId {
get { return objectId ; }
set {
objectId = Uri.UnescapeDataString(value ?? string.Empty);
//Trovo l'esercizio selezionato
MyObject = ObjectListService.objects.Find(x => x.Object_Id == ObjectId);
OnPropertyChanged();
}
}
private ObservableCollection<String> objectImageList = new ObservableCollection<String>();
public ObservableCollection<String> ObjectImageList {
get { return objectImageList ; }
set {
objectImageList = value;
OnPropertyChanged();
}
}
private async void OnImageTap(object obj) {
String imagePath = (String)obj;
await Shell.Current.GoToAsync($"{nameof(FullScreenImagePage)}?ObjectId={MyObj.Object_Id}&ImagePath={imagePath}");
}
}
这是全屏图像视图模型页面。 'ObjectId' 和 'imagePath' 参数从上面的函数 'OnImageTap' 传递到此页面。这里的问题在
[QueryProperty(nameof(ObjectId ), nameof(ObjectId ))]
[QueryProperty(nameof(ImagePath), nameof(ImagePath))]
class FullScreenImageViewModel : BindableObject {
public FullScreenImageViewModel () { }
private String objectId = String.Empty;
public String ObjectId {
get { return objectId ; }
set {
objectId = value;
}
}
private String imagePath = String.Empty;
public String ImagePath {
get { return imagePath; }
set {
imagePath = value;
OnPropertyChanged();
}
}
public async void OnBackButtonPressed() {
//await Shell.Current.GoToAsync("..");
await Shell.Current.GoToAsync($"..?ObjectId={ObjectId}");
}
在上面的 'OnBackButtonPressed' 函数中,应用程序使用任一方法都会抛出异常。
第一种情况,异常是这样的:
System.ArgumentException: 'Ambiguous routes matched for: //D_FAULT_TabBar9/D_FAULT_Tab6/UserHomePage/ClientExercisePage matches found: //D_FAULT_TabBar9/D_FAULT_Tab6/UserHomePage/ClientExercisePage,//D_FAULT_TabBar9/D_FAULT_Tab6/UserHomePage/ClientExercisePage. Parameter name: uri'
在第二种情况下是这样的(它们实际上是相同的。在第二种情况下 'match not found' 之前还有 '?ObjectId=ff2dcfd1eecf7877':
System.ArgumentException: 'Ambiguous routes matched for: //D_FAULT_TabBar11/D_FAULT_Tab6/HomePage/ObjectPage?ObjectId=ff2dcfd1eecf7877 matches found: //D_FAULT_TabBar11/D_FAULT_Tab6/HomePage/ObjectPage,//D_FAULT_TabBar11/D_FAULT_Tab6/HomePage/ObjectPage. Parameter name: uri'
从Xamarin.ShellPass data,您可以使用 await Shell.Current.GoToAsync($"elephantdetails?name={elephantName}");
导航到另一个带参数的
首先,我创建了两个contentPage,一个是HomePage,CollectionView用来显示List数据。另一个页面是Objectdetailpage,显示详细信息。
<CollectionView
x:Name="collectionview1"
ItemsSource="{Binding objectList}"
SelectedItem="{Binding selecteditem}"
SelectionMode="Single">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding objectId}" />
<Label Text="{Binding name}" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
public class HomePageViewModel:ViewModelBase
{
public ObservableCollection<MyObject> objectList { get; set; }
private MyObject _selecteditem;
public MyObject selecteditem
{
get { return _selecteditem; }
set
{
_selecteditem = value;
RaisePropertyChanged("selecteditem");
if(_selecteditem!=null)
{
Shell.Current.GoToAsync($"objectdetailpage?objectId={selecteditem.objectId}");
}
}
}
public ICommand ObjectTap { get; set; }
public HomePageViewModel()
{
objectList = new ObservableCollection<MyObject>();
ObjectTap = new Command(gotomethod);
for (int i=0;i<10; i++)
{
MyObject myobject = new MyObject() { objectId = i, name = "object " + i };
objectList.Add(myobject);
}
}
}
注意:需要实现INotifyPropertyChanged
通知selecteditem数据改变。
public class MyObject
{
public int objectId { get; set; }
public string name { get; set; }
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
导航到详细页面。 IQueryAttributable
接口指定实现 class 必须实现 ApplyQueryAttributes
方法。此方法有一个 IDictionary<string, string>
类型的查询参数,其中包含导航期间传递的任何数据。
<StackLayout>
<Label Text="{Binding myobject.objectId}" />
<Label Text="{Binding myobject.name}" />
</StackLayout>
public partial class ObjectDetailPage : ContentPage
{
public ObjectDetailPage()
{
InitializeComponent();
this.BindingContext = new objectviewmodel();
}
}
public class objectviewmodel : IQueryAttributable, INotifyPropertyChanged
{
private MyObject _myobject;
public MyObject myobject
{
get { return _myobject; }
set
{
_myobject = value;
OnPropertyChanged("myobject");
}
}
public void ApplyQueryAttributes(IDictionary<string, string> query)
{
string objectId = HttpUtility.UrlDecode(query["objectId"]);
LoadObject(objectId);
}
private void LoadObject(string objectId)
{
if(objectId!=null)
{
myobject = (new HomePageViewModel()).objectList.FirstOrDefault(a => a.objectId ==int.Parse(objectId));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
如果你想通过点击BackButton返回,你不需要添加任何代码,只需要点击BackButton,它会触发ApplyQueryAttributes
方法来获取参数。
因此您可以删除 OnBackButtonPressed
,只需单击返回按钮即可返回。