Issue
I am developing an android app with three pages. The first is the HomePage. From this page you can click on the desired object and you will be sent to the object page, with related images, title and description. By clicking on the image you are taken to a page that shows the enlarged image that responds to the user's gestures (zoom and pan).
So far everything works. The problem arises when from the image page I want to go back to the previous page (containing the object information). This page needs the object id to show the features and consequently, from the image page I run the following code to go back:
await Shell.Current.GoToAsync($"..?ObjectId={ObjectId}");
But, despite having registered all the pages (views) in the AppShell.xaml.cs file when I try to go back I get this exception:
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'
Could you please help me out with this problem? I have (almost) searched all over the web but I couldn't find any useful answer. Thanks in advance!
UPDATE:
This is the HomePageViewModel page, from which I select the object in a collectionview. The OnObjectTap method is called when the user clicks on an object and brings it to the tab with information about the object. This piece of code works
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}");
}
}
This is the ObjectViewModel page. The 'ObjectId' parameter is passed to this page. If the user clicks on the image of the object, he is automatically taken to the FullScreenImagePage from which he can enlarge the image.This piece of code 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}");
}
}
This is the FullScreenImageViewModel page. The 'ObjectId' and the 'imagePath' parameters are passed to this page from the function 'OnImageTap' above. The problem here is in the
[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}");
}
In the 'OnBackButtonPressed' function above, the application throws an exception with either method.
In the first case, the exception is this:
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'
And in the second case it is this (they are practically identical. In the second there is in addition '?ObjectId=ff2dcfd1eecf7877' before 'match not found'):
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'
Solution
From Xamarin.Shell Pass data, you can use await Shell.Current.GoToAsync($"elephantdetails?name={elephantName}"); to navigate to another with parameter.
Firstly, I create two contentPage, one is HomePage, with CollectionView to display List data. Another page is Objectdetailpage, to display detailed info.
<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);
}
}
}
Note: need to implement INotifyPropertyChanged to notify selecteditem data changed.
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));
}
}
}
}
Navigating to detailedpage. The IQueryAttributable interface specifies that the implementing class must implement the ApplyQueryAttributes method. This method has a query argument, of type IDictionary<string, string>, that contains any data passed during navigation.
<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));
}
}
If you want to go to back by clicking BackButton, you don't need to add any code, just click BackButton, it will fire ApplyQueryAttributes method to get parameter.
So you can remove OnBackButtonPressed, just click BackButton to go to back.
Answered By - Cherry Bu - MSFT
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.