How do you properly bind and update the Xamarin.Forms ListView?

Using the MVVM template, I have a Model, ViewModel and View that contains a ListView. The ListView is bound to the ViewModel, which is an ObservableCollection of the Model class. I can make the binding for the initial display work and can update the properties of the Model class for the corresponding row when it affects the view, but I can’t get the view for the update by pulling data from the Model class into an ObservableCollection. The ListView class does not contain a method for canceling or force updating, which would solve my problem. How to get a ListView to update after updating the data on the model?

Here is a simple example of what I'm trying to do: Each line contains a button and a shortcut. After clicking the button, I can update the shortcut that will be displayed on the screen. I need to update the model, which, in turn, must force update the view. However, I cannot get this to work. In a real application, the model will be updated at the level of business logic, and not in the view, after which I need to force the ListView to be updated.

Code example:

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; } } } 
+6
source share
2 answers

I think your model needs to implement INotifyPropertyChanged. Thus, the user interface knows that the value in the model has changed

something like that:

 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"); } } } 
+9
source

Remove the "set" method from your ViewModelList property. Whenever you use an ObservableCollection, just modify the elements in this collection. Never replace it with another ObservableCollection.

+2
source

Source: https://habr.com/ru/post/972991/


All Articles