6

Using the MVVM pattern, I have a Model, ViewModel and View, which contains a ListView. The ListView is bound to a member of the ViewModel that is an ObservableCollection of the Model class. I can get the binding for the initial display to work and can update properties on the Model class for the appropriate row upon acting on the view, but I cannot get the view to refresh, pulling data from the Model class in the ObservableCollection. The ListView class does not contain method to invalidate or force a refresh, which would address my issue. How do I get the ListView to refresh after updating data on the Model?

Here is a simple example of what I am trying to do: Each row contains a Button and Label. Upon clicking a button, I can update the label, which will be reflected on screen. What I need to do is update the Model, which in turn should force an update of the view. However, I cannot get this to work. In an actual application, updating of the Model will be in a business logic layer, not in the view, after which I need to force refresh of the ListView.

Example Code:

using System;
using System.Collections.ObjectModel;
using Xamarin.Forms;

namespace ListViewTest
{   
    public class Model
    {
        public static int ids = 0;
        public Model(string count) 
        {
            Id = +1;
            Count = count;
        }
        public int Id { get; set; }
        public string Count { get; set; }
    }

    public class ModelList : ObservableCollection<Model>
    {
    }

    public class ViewModel
    {
        ModelList list = new ModelList();
        public ModelList ViewModelList
        {
            get { return list; }
            set 
            { 
                list = value; 
            }
        }
    }

    public partial class MainPage : ContentPage
    {   
        public ViewModel viewModel;

        public class DataCell : ViewCell
        {
            public DataCell()
            {
                var Btn = new Button();
                Btn.Text = "Click";
                var Data = new Label();
                Data.SetBinding(Label.TextProperty,"Count");
                Btn.Clicked += (object sender, EventArgs e) => 
                {
                    Model model = (Model)(((Button)sender).Parent.BindingContext);
                    int count = Convert.ToInt32(model.Count);
                    count++;
                    model.Count = count.ToString();

                    // Need to refresh ListView from data source here... How???
                };

                StackLayout s = new StackLayout();
                s.Orientation = StackOrientation.Horizontal;
                s.Children.Add(Btn);
                s.Children.Add(Data);
                this.View = s;
            }
        }

        public MainPage ()
        {
            viewModel = new ViewModel();
            viewModel.ViewModelList.Add(new Model("0"));
            viewModel.ViewModelList.Add(new Model("0"));
            InitializeComponent();
        }

        public void InitializeComponent()
        {
            ListView listView = new ListView
            {
                ItemsSource = viewModel.ViewModelList,
                ItemTemplate = new DataTemplate(() =>
                    {
                        return new DataCell();
                    })
            };
            Content = listView;
        }
    }
}

2 Answers 2

9

i think your model needs to implement INotifyPropertyChanged. So the UI knows that a value in model has changed

something like this :

public class Model : INotifyPropertyChanged
{
    // boiler-plate
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    protected bool SetField<T>(ref T field, T value, string propertyName)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    // props
    private string _count;
    public string Count 
    {
        get { return _count; }
        set { SetField(ref _count, value, "Count"); }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

2

Remove the "set" method from your ViewModelList property. Whenever you use an ObservableCollection, just change the items in that one collection. Never replace it with another ObservableCollection.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.