1

I'm using mvvmcross and xamarin to bind an ObservableCollection to a UITableView. The collection is updated in place using the Add, Remove and Move methods. These calls correctly trigger INotifyCollectionChanged events and the TableView is updated as expected the first time the view containing the table is shown. If the user navigates away from the original view as part of the normal application flow but later returns the correct data is loaded into the table but calls to add, move and remove no longer update the table.

  • The INotifyCollectionChanged events are still being fired when the collection is updated
  • If I manually subscribe to these events in my subclass of MvxStandardTableViewSource and try and call ReloadData on the UITableView still does not update
  • My presenter is creating a new instance of the viewmodel and view each time the page is visited.

I'm also using Xamarin-Sidebar (https://components.xamarin.com/view/sidebarnavigation) for navigation in my application with a custom presenter to load the views but as far as I can tell the view is initialised via exactly the same code path whether it's the first or subsequent visit.

My presenters Show() method looks like this:

public override void Show(MvxViewModelRequest request)
{
    if (request.PresentationValues != null)
    {
        if(NavigationFactory.CheckNavigationMode(request.PresentationValues, NavigationFactory.ClearStack))
        {
            MasterNavigationController.ViewControllers = new UIViewController[0];
            base.Show(request);
        }
        else if(NavigationFactory.CheckNavigationMode(request.PresentationValues, NavigationFactory.LoadView))
        {
            var root = MasterNavigationController.TopViewController as RootViewController;
            var view = this.CreateViewControllerFor(request) as UIViewController;
            root.SidebarController.ChangeContentView(view);
        }
    }
    else
    {
        base.Show(request);
    }
}

The binding in my ViewController looks like this:

public override void ViewDidLoad()
{
    base.ViewDidLoad();
    View.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;

    var source = new TracksTableSource(TableView, "TitleText Title; ImageUrl ImageUrl", ViewModel);
    TableView.Source = source;

    var set = this.CreateBindingSet<TracksViewController, TracksViewModel>();
    set.Bind(source).To(vm => vm.PlaylistTable);
    set.Apply();
}

And my viewmodel is as below where PlaylistTable is a subclass of ObservableCollection with the Update method using add, move and remove to keep the collection up to date.

public class TracksViewModel : MvxViewModel
{
    private readonly IPlaylistService _playlistService;
    private readonly IMessengerService _messengerService;
    private readonly MvxSubscriptionToken _playlistToken;

    public PlaylistTable PlaylistTable { get; set; }

    public TracksViewModel(IPlaylistService playlistService, IMessengerService messengerService)
    {
        _playlistService = playlistService;
        _messengerService = messengerService;

        if (!messengerService.IsSubscribed<PlaylistUpdateMessage>(GetType().Name))
            _playlistToken = _messengerService.Subscribe<PlaylistUpdateMessage>(OnDirtyPlaylist, GetType().Name);
    }

    public void Init(NavigationParameters parameters)
    {
        PlaylistTable = new PlaylistTable(parameters.PlaylistId);
        UpdatePlaylist(parameters.PlaylistId);
    }

    public async void UpdatePlaylist(Guid playlistId)
    {
        var response = await _playlistService.Get(playlistId);
        PlaylistTable.Update(new Playlist(response));
    }

    private void OnDirtyPlaylist(PlaylistUpdateMessage message)
    {
        UpdatePlaylist(message.PlaylistId);
    }

}

This setup works perfectly the first time the view is initialised and updates the table correctly, it's only the second and subsequent times the view is initialised that the table fails to update. Can anyone explain why the binding fails when it appears the view is created using the same techniques in both instances?

I can post additional code if required but I believe the issue will be how I'm using the presenter since the code I've not posted from PlaylistTable functions correctly in unit tests and on first viewing.

  • Did you try with ```RaisePropertyChanged("PlaylistTable");``` on your Init/UpdatePlayList methods? – xleon Jan 10 '16 at 14:56
  • I did try raising a property changed in the UpdatePlaylist method - it had no effect. – Daniel Bardsley Jan 10 '16 at 20:58
  • Is the Init() method getting called on the second time ? – xleon Jan 10 '16 at 21:30
  • Init() is called both times and the table is correctly populated. I'm definitely getting the latest version of the table too since if I make changes to the data before returning to the screen these changes are displayed. – Daniel Bardsley Jan 10 '16 at 21:51
  • If the data gets reflected on the screen, what´s the problem then? I don´t get it – xleon Jan 10 '16 at 21:53
  • The state of the table is correct when it's first loaded but if I update the ObservableCollection whilst staying on the same screen the changes are not reflected until the screen is reloaded. This works fine the first time the table is shown but if I navigate away and then return updates to the collection are not shown in the UI despite the initial data displaying correctly. – Daniel Bardsley Jan 10 '16 at 21:59
  • Then I´m pretty sure that changes to the collection are not been well propagated. Check your custom collection code and make sure that adding, removing, etc are propagating changes and those can be intercepted by the view (ie: try adding an event listener from the view controller just to check this out). RaisePropertyChanged("PlaylistTable"); does not work because it is not the collection property that is changing but something inside the collection – xleon Jan 10 '16 at 22:07
  • In the ViewController I can subscribe to the collection changed event of PlaylistTable - it's triggered correctly both times. Even if I add a TableView.ReloadData() inside the collection changed handler the table still does not update. – Daniel Bardsley Jan 10 '16 at 22:13
  • That´s getting weird. Last thing I can think of, do the items of the collection implement INotifyPropertyChanged? – xleon Jan 10 '16 at 23:55
  • Ok - I've discovered my mistake. The presenter is creating a new viewmodel each time I visit the screen *but* in my ViewModel constructor I'm testing if I'm already subscribed to the MessengerService and not resubscribing. I was seeing the CollectionChanged notification on the first ViewModel and not the second. Removing the IsSubscribed test from the constructor resolved this. Thanks so much for taking the time to help me! – Daniel Bardsley Jan 11 '16 at 00:43
  • That also looks like a potential memory leak – xleon Jan 11 '16 at 01:09
  • Agreed. The subscription needs to be cleaned up when I'm leaving the view. – Daniel Bardsley Jan 11 '16 at 02:40

0 Answers0