Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating UI when a model property changes in an ObservableCollection?

I have a view that has a group of images I get from a web service I receive them in a list of this class:

 public class ImageModel 
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string imageUrl { get; set; }
    }

under each image I show an up-vote button, so I added another bool property to the model above:

 public bool UpVoted { get; set; }

the ListView that shows these images is bound to an ObservableCollection<ImageModel > , I want to change the voting icon through a converter that convert the value of UpVoted to the corresponding icon, when the user click the voting icon: a command execute this method:

    private void OnVoting(ImageModel image)
    {
        Images.Single(x => x.id == image.id).UpVoted = !image.UpVoted;
    }

the problem is that the UI is not updated, and to make sure that I understood the problem I turned the model to a View model and made the required changes to the UpVoted property (I'm using MVVM light library)

bool upVoted;
        public bool UpVoted
        {
            get { return upVoted; }
            set
            {
                Set(ref upVoted, value);
            }
        }

and it works now, so I need to bind the UpVoted to the UI, so it's updated whenever it changed

like image 284
mshwf Avatar asked Oct 18 '22 02:10

mshwf


2 Answers

first your model class must inherit from MvxNotifyPropertyChanged

public class ImageModel : MvxNotifyPropertyChanged
    {
        public int Id { get; set; }
        public string Name { get; set; }
        private bool upVoted ;
        public bool UpVoted 
        {
            get { return upVoted ; }
            set { upVoted = value; RaisePropertyChanged(() => UpVoted ); }
        }
    }

then with MvxValueConverter you ready to go

like image 107
Mustafa Avatar answered Oct 20 '22 23:10

Mustafa


Mustafa's answer mentions a class that is specific to MvvmCross library.
Another alternative is TinyMvvm.

If you wish to write your own MVVM (or understand how MVVM works), the general pattern is to implement INotifyPropertyChanged: Implement Property Change Notification, which I discuss here.

A convenient way to implement INotifyPropertyChanged, is to make a base class that does that implementation, then inherit from that base class. You can use the code in that sample as your base class. Or use a slightly different implementation, that avoids having to manually pass the property name as a string:

using System.ComponentModel;
using System.Runtime.CompilerServices;

// Use this as base class for all your "view model" classes.
// And possibly for your (domain) model classes.
// E.g.:  "public class MyLoginViewModel : HasNotifyPropertyChanged".
// OR     "public class MyLoginModel : HasNotifyPropertyChanged".
// Give it whatever name you want, for ViewModels I suggest "ViewModelBase".
public class HasNotifyPropertyChanged : INotifyPropertyChanged
{
    // --- This is pattern to use to implement each property. ---
    //     This works for any property type: int, Color, etc.
    //     What's different from a standard c# property, is the "SetProperty" call.
    //     You will often write an IValueConverter (elsewhere) to use in XAML to convert from string to your property type,
    //     or from your property type to a type needed in your UI.
    //     Comment out this example property if you don't need it.
    /// <summary>
    /// Set to "true" at end of your initialization.
    /// Then can use Property Trigger on Ready value=true in XAML to do something when your instance is ready for use.
    /// For example, load something from web, then trigger to update UI.
    /// </summary>
    private bool _ready;
    public bool Ready
    {
        get => _ready;
        set => SetProperty(ref _ready, value);
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void SetProperty<T>(ref T property, T value, [CallerMemberName] string propertyName = null)
    {
        if (property == null || !property.Equals(value))
        {
            property = value;
            RaisePropertyChanged(propertyName);
        }
    }

    protected void RaisePropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Again, an alternative to the above code is to use an existing MVVM library.

For another alternative, that doesn't require writing "SetProperty(..)" or "OnPropertyChanged(..)" in all of your property setters, google for info about using Fody/PropertyChanged. Then you wouldn't need any of the above code; your class would simply inherit from INotifyPropertyChanged. (And in app startup, you call a method that "injects" the needed logic into all properties of all INotifyPropertyChanged classes.)

Acknowledgement: The code pattern in example above is based on one of the open source libraries. It might be from TinyMvvm.

like image 42
ToolmakerSteve Avatar answered Oct 21 '22 00:10

ToolmakerSteve