Cannot pass ObservableCollection<Video> from one page to another

94 Views Asked by At

I can't pass an ObservableCollection<Video> from one page to another. I use this same pattern all over my project, so I'm not sure why it's failing here. I'm using the MVVM pattern.

Here's my code:

On the "throw" side

I declare the ObservableCollection<Video> and instantiate it in the constructor:

[ObservableProperty]
private ObservableCollection<Video> videoList;

//...

public Constructor()
{   
    VideoList = new ObservableCollection<Video>();
}

The method that sets up the pass:

[RelayCommand]
private async Task CreateCase()
{
    if (0 == VideoList.Count)
    {
        await App.Current.MainPage.DisplayAlert("No video(s) selected", "You must have at least one video selected to add to your new Case.", "", "OK");
        return;
    }

    Dictionary<string, object> param = new Dictionary<string, object>
    {
        { "VideoList", VideoList }
    };

    await Shell.Current.GoToAsync($"//{nameof(CreateCasePage)}", param);
}

The ObservableCollection<Video> makes it into this method and into the Dictionary. Here's a screenshot just to confirm. In the code, I chose three objects, and lo and behold they are in the list:
enter image description here

On the "Catch" side

The ObservableCollection<Video> is declared, and the QueryProperty is used. The same name is used on both the 'throw' page and 'catch' page:

[QueryProperty("VideoList", "VideoList")]
public partial class CreateCasePageViewModel : ObservableObject
{
    #region Properties
    [ObservableProperty]
    private ObservableCollection<Video> videoList;

    //...

    public Constructor()
    {
        VideoList = new ObservableCollection<Video>();
    }
}

I'm not sure if I should 'new' the ObservableCollection<Video> on the 'catch' side, but I've tried newing and and not newing it and have the same results.

Results

The ObservableCollection<Video> is always null on the 'catch' side, regardless of what I try. The thing is, I use this same code elsewhere in my project and it works.

I'm not sure what else to look at to debug.

EDIT: Using ApplyQueryAttributes but not working

I was advised to use the ApplyQueryAttributes interface, and this has gotten me closer, but it's not quite working.

My "throw" side code is the same, that is, I simply pass the ObservableObject<object> to the "catch" side with a dictionary:

    [RelayCommand]
    private async Task CreateCase()
    {
        Dictionary<string, object> param = new()
        {
            { "VideoList", VideoList }
        };

        await Shell.Current.GoToAsync($"//{nameof(CreateCasePage)}", param);
    }

On the 'catch' side, I add the IQueryAttributable interface:

public partial class CreateCasePageViewModel : ObservableObject, IQueryAttributable
{
    [ObservableProperty]
    private ObservableCollection<Video> videoList;

    public void ApplyQueryAttributes(IDictionary<string, object> query)
    {
        VideoList = query["VideoList"] as ObservableCollection<Video>;
    }
}

This almost works. When I put a breakpoint inside the interface, VideoList is populated, and looks good.
enter image description here

However, when I attempt to access VideoList elsewhere in the ViewModel, I get an exception:

enter image description here

This is confounding.

1

There are 1 best solutions below

6
Julian On

You're mixing the string-based query parameters and object-based navigation data approaches here. You can check the documentation for more information.

Your receiving ViewModel should implement the IQueryAttributable interface instead of using a QueryProperty:

public partial class CreateCasePageViewModel : ObservableObject, IQueryAttributable
{
    [ObservableProperty]
    private ObservableCollection<Video> videoList;

    public void ApplyQueryAttributes(IDictionary<string, object> query)
    {
        VideoList = query["VideoList"] as ObservableCollection<Video>;
    }
}

This is necessary, because only simple types and strings can be passed as string-based query parameters.