Pettanord
Pettanord

Reputation: 117

Binding data in a maui content view

In my maui app I have this content view that I use as a custom control. In it I have a collectionview that I want to bind to a list of data.

I use DI for my services and have a BaseService with all the httpclient requests and I then inherit from that BaseService depending of model.

I then have a viewmodel where I inject the service and retreive data from an api.


private readonly LightCastArchiveService _lightCastArchiveService;
private readonly LightCastChannelService _lightCastChannelService;
   
public LatestViewModel(LightCastArchiveService lightCastArchiveService,
        LightCastChannelService lightCastChannelService)
{
    _lightCastArchiveService = lightCastArchiveService;
    _lightCastChannelService = lightCastChannelService;
}

And in the viewmodel I then have a method to get the data:

public async Task LoadVideos()
    {
        if (IsLoading) return;
        try
        {
            string url = EndPoints.ArchiveEndpoint + $"&count=50";
            IsLoading = true;
            if (Videos.Any()) Videos.Clear();
            var videos = new List<LCVideo>();
            videos = await _lightCastArchiveService.Get(url);
            foreach (var video in videos)
            {
                Videos.Add(video);
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"Unable to get the videos: {ex.Message}");
            await Shell.Current.DisplayAlert("Error", "Failed to retrieve list of videos.", "Ok");
        }
        finally
        {
            IsLoading = false;
            IsRefreshing = false;
        }
    }

In an content page I then can use the OnAppearing method to retrieve the videos:

 protected override async void OnAppearing()
    {
        base.OnAppearing();
        await _homeViewModel.GetCarouselItems(1025, 10);
        await _homeViewModel.LoadLiveChannels();
        await _homeViewModel.GetItems(1009, 20);
        await _homeViewModel.LoadVideosForAllChannels();
        
    }

but how can I do it in an content view (I tried the approach below but it didn't work)?

public partial class Latest : ContentView
{
    private readonly LatestViewModel _latestViewModel;
    private readonly LightCastArchiveService _lightCastArchiveService;
    private readonly LightCastChannelService _lightCastChannelService;
    private readonly HttpClient _client;
    public Latest()
    {
        InitializeComponent();
        .BindingContextChanged += OnBindingContextChanged;

        _lightCastArchiveService = new LightCastArchiveService(_client);
        _lightCastChannelService = new LightCastChannelService(_client);

        this.BindingContextChanged += OnBindingContextChanged;

        _latestViewModel = new LatestViewModel(_lightCastArchiveService, _lightCastChannelService);
        BindingContext = _latestViewModel;
    }

    private async void OnBindingContextChanged(object sender, EventArgs e)
    {
        if (BindingContext is LatestViewModel viewModel)
        {
            await viewModel.LoadVideos();
        }
    }

    // private async void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
    // {
    //     if (e.PropertyName == nameof(_latestViewModel.Videos))
    //     {
    //         latestVideos.BindingContext = await _latestViewModel.LoadVideos();
    //     }
    // }
}
```

Upvotes: 0

Views: 389

Answers (1)

Riccardo Minato
Riccardo Minato

Reputation: 1807

Unless it's a very complex custom control, you probably want to avoid a viewmodel for a ContentView.

Let me justify this. I'm not saying it's completely wrong, nor impossible, but as you've seen, many features you have in Pages viewmodels are not there for ContentViews ones (injected services, appearing events...). Someone could actually say that you shouldn't use OnAppearing() to call viewmodel methods, but I'm not such a purist.

If you think about MAUI controls, they don't have their own viewmodel, but they inherit the ancestor's BindingContext (probably the one belonging to the Page where they are used) and provide bindable properties you can bind to a viewmodel property. You should do that too.

Take a look at how BindableProperties work, if you don't know what I'm talking about.


If you really want to keep your viewmodel, you could instantiate it inside the ContentPage code-behind and set the BindingContext of LatestView there. This way, you can call LatestViewModel methods inside OnAppearing. Dirty workaround though.

Upvotes: 2

Related Questions