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,只需单击返回按钮即可返回。