1

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'
3
  • is the ObjectId null? also you could try await Shell.Current.GoToAsync(".."); Commented Aug 16, 2021 at 23:06
  • Please provide more relevant code . Commented Aug 17, 2021 at 8:20
  • @FabriBertani, yea, I found out that ObjectId was null. Now i have corrected the issue but it gives me the same error. And I also have updated the post and added the relevant code Commented Aug 17, 2021 at 13:16

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

2 Comments

I think I understand what's wrong, but I don't know why it happens. Basically the back buttons (both the one in the navigation bar and the one at the bottom which is present by system default) only work on the page following the HomePage. After that, if from the second page you navigate to further pages, that point no longer works. I am attaching a video example showing the problem. The "Publish Exercise" button, when clicked, navigates to a new page. Then when I try to go back it doesn't work. Here is the link: streamable.com/w3asj3
I found what was the problem and I can0t believe it was so easy. All the code i wrote was right, I only had to delete some duplicated routes in the AppShell.xaml.cs file... I have no words 😀

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.