Ошибка Оболочки Xamarin При Выполнении Обратной Навигационной Системы.Исключение аргумента

#xamarin #xamarin.forms #xamarin.android

Вопрос:

Я разрабатываю приложение для Android с тремя страницами. Первый — это домашняя страница. С этой страницы вы можете нажать на нужный объект, и вы будете отправлены на страницу объекта с соответствующими изображениями, названием и описанием. Нажав на изображение, вы попадете на страницу, на которой показано увеличенное изображение, реагирующее на жесты пользователя (масштабирование и панорамирование).

Пока все работает. Проблема возникает, когда со страницы изображения я хочу вернуться на предыдущую страницу (содержащую информацию об объекте). Этой странице нужен идентификатор объекта для отображения функций, и, следовательно, со страницы изображения я запускаю следующий код, чтобы вернуться:

 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, на которой я выбираю объект в представлении коллекции. Метод OnObjectTap вызывается, когда пользователь нажимает на объект и выводит его на вкладку с информацией об объекте. Этот фрагмент кода работает

 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’ передается на эту страницу. Если пользователь нажимает на изображение объекта, он автоматически переходит на страницу полноэкранного изображения, с которой он может увеличить изображение.Этот фрагмент кода работает

 [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}amp;ImagePath={imagePath}");

    }
}
 

Это страница FullScreenImageViewModel. Параметры «ObjectId» и «Путь к изображению» передаются на эту страницу из функции «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'
 

И во втором случае это так (они практически идентичны. Во втором есть дополнительно»? 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'
 

Комментарии:

1. является ObjectId ли значение нулевым? также вы могли бы попробовать await Shell.Current.GoToAsync("..");

2. Пожалуйста, предоставьте более подходящий код .

3. @FabriBertani, да, я узнал, что ObjectId это было пусто. Теперь я исправил проблему, но она выдает мне ту же ошибку. И я также обновил сообщение и добавил соответствующий код

Ответ №1:

Из Xamarin.Оболочка передает данные, которые вы можете использовать await Shell.Current.GoToAsync($"elephantdetails?name={elephantName}"); для перехода к другому с параметром.

Во-первых, я создаю две страницы контента, одна из которых является домашней страницей, с CollectionView для отображения данных списка. Другая страница-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 уведомление об изменении выбранных данных.

 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 Интерфейс указывает, что реализующий класс должен реализовать 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));
    }
}
 

Если вы хотите вернуться, нажав кнопку «Назад», вам не нужно добавлять какой-либо код, просто нажмите кнопку «Назад», он запустит ApplyQueryAttributes метод для получения параметра.

Чтобы вы могли удалить OnBackButtonPressed , просто нажмите кнопку «Назад», чтобы перейти к «Назад».

Комментарии:

1. Мне кажется, я понимаю, в чем дело, но я не знаю, почему это происходит. В основном кнопки «Назад» (как на панели навигации, так и внизу, которые присутствуют по умолчанию в системе) работают только на странице, следующей за домашней страницей. После этого, если со второй страницы вы перейдете на другие страницы, этот пункт больше не будет работать. Я прилагаю видео-пример, показывающий проблему. Кнопка «Опубликовать упражнение» при нажатии переходит на новую страницу. Затем, когда я пытаюсь вернуться, это не работает. Вот ссылка: streamable.com/w3asj3

2. Я нашел, в чем была проблема, и не могу поверить, что это было так просто. Весь код, который я написал, был правильным, мне нужно было только удалить некоторые дублированные маршруты в AppShell.xaml.cs файле… У меня нет слов 😀