Adding to ObservableCollection does not refresh bound view

martinwhitman1 326 Reputation points
2021-04-02T18:07:58.47+00:00

I have an ObservableCollection bound to a view. Adding to the collection from another ViewModel doesn't change the view after addition. Closing the app and reopening does, though.

Here's the View:

<CollectionView Grid.Row="2" x:Name="ProjectsCollectionView" x:DataType="vm:LandingViewModel"
                ItemsSource="{Binding Projects}"
                SelectionMode="None">
                <CollectionView.ItemTemplate>
                    <DataTemplate>
                    <Frame BorderColor="Black" CornerRadius="10" HasShadow="False" Padding="5">
                        <StackLayout Orientation="Vertical" Padding="5" x:DataType="model:MProject">
                            <Label Text="{Binding projectname}" 
                            LineBreakMode="NoWrap" 
                            Style="{DynamicResource ListItemTextStyle}" 
                            FontSize="16" />
                            <Label Text="{Binding facilityname}" 
                            LineBreakMode="NoWrap"
                            Style="{DynamicResource ListItemDetailTextStyle}"
                            FontSize="13" />
                            <StackLayout.GestureRecognizers>
                                <TapGestureRecognizer 
                                NumberOfTapsRequired="1"
                                Command="{Binding Source={RelativeSource AncestorType={x:Type vm:LandingViewModel}}, Path=ItemTapped}"      
                                CommandParameter="{Binding .}">
                                </TapGestureRecognizer>
                            </StackLayout.GestureRecognizers>
                        </StackLayout>
                    </Frame>
                </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>
Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,291 questions
0 comments No comments
{count} votes

Accepted answer
  1. martinwhitman1 326 Reputation points
    2021-04-02T19:22:43.093+00:00

    I "solved" it by doing the following: switching the BindingContext declaration from XAML to code behind, and overriding the OnAppearing method to LoadProjects(). I still feel like I'm missing something.

    private LandingViewModel _viewModel = new LandingViewModel();
            public LandingPage()
            {
                InitializeComponent();
                BindingContext = _viewModel;
            }
    
            protected async override void OnAppearing()
            {
                base.OnAppearing();
                _viewModel.LoadProjects();
            }
    
    0 comments No comments

2 additional answers

Sort by: Most helpful
  1. martinwhitman1 326 Reputation points
    2021-04-02T18:08:16.363+00:00

    Here's the ViewModel for that view:

    public class LandingViewModel : BaseViewModel
        {
            private static LandingViewModel _instance = new LandingViewModel();
            public static LandingViewModel Instance { get { return _instance; } }
            private MProject _selectedMProject;
            //private ObservableCollection<MProject> _projects;
            public Command<MProject> ProjectTapped { get; }
    
            public ObservableCollection<MProject> Projects { get; set; }
    
            public ICommand OpenWebCommand { get; }
            public Command AddProjectCommand { get; set; }
            public LandingViewModel()
            {
                Projects = new ObservableCollection<MProject>();
                Title = "Landing";
                OpenWebCommand = new Command(async () => await Browser.OpenAsync("https://aka.ms/xamarin-quickstart"));
                ProjectTapped = new Command<MProject>(OnProjectSelected);
                AddProjectCommand = new Command(OnAddProject);
                LoadProjects();
            }
    
            public async void LoadProjects()
            {
                LocalDB localDB = await LocalDB.Instance;
                var list = await localDB.GetProjectsAsync();
                Projects.Clear();
                foreach(var k in list)
                {
                    Projects.Add(k);
                }
            }
    
            public void OnAppearing()
            {
                IsBusy = true;
                SelectedMProject = null;
            }        }
    
    0 comments No comments

  2. martinwhitman1 326 Reputation points
    2021-04-02T18:09:06.61+00:00

    Here's the code from the other viewmodel that adds the new item to the ObservableCollection but doesn't trigger a view refresh:

    private async void OnSave()
            {
                MProject newProject = new MProject()
                {
                    ///assignments here
                };
    
                //await DataStore.AddProjectAsync(newProject);
                LocalDB localDB = await LocalDB.Instance;
                await localDB.SaveProjectAsync(newProject,true);
                //LandingViewModel landingViewModel = new LandingViewModel();
                //landingViewModel.Projects.Clear();
                //foreach(var k in await localDB.GetProjectsAsync())
                //{
                //    landingViewModel.Projects.Add(k);
                //}
    
                LandingViewModel.Instance.Projects.Add(newProject);
                // This will pop the current page off the navigation stack
                await Shell.Current.GoToAsync("..");}
    

    I feel like I'm missing something obvious. Should I create my observablecollection in the BaseViewModel, instead of using the instance variable to access it from the other ViewModel?

    0 comments No comments