Cagaya Coper
Cagaya Coper

Reputation: 75

How to make MVVM property changed when my settings also changed?

I have a ViewModel like this

Public class AboutPageViewModel
{
  public AboutPageViewModel()
  {
    AppName = Settings.MyAppName;
  }

  private string _appName;
  public string AppName
  {
    get{return _appName;}
    set{_appName = value; RaisePropertyChanged("AppName");}
  }
}

Now in a static class

public static class Settings
{
   public static string MyAppName{get;set;} = "LOL"
}

How do I notify the ViewModel everytime MyAppName is changed, and update it to the Binded UI?

Thanks!

Upvotes: 0

Views: 444

Answers (3)

As you define it in your question, Settings isn't a static class (ah, I see in comments that was a typo, and it's static in your code). It should not be static. PropertyChanged notifications on a static class are theoretically possible but it's not worth your time to mess with, and there's no need to bother.

Have Settings implement INotifyPropertyChanged, just like your viewmodel. When MyAppName changes, Settings should raise PropertyChanged, just as AboutPageViewModel does when its own AppName property changes.

Now give Settings a static property called Instance:

public static Settings Instance { get; private set; }

static Settings()
{
    Instance = new Settings();
}

And handle its PropertyChanged event in AboutPageViewModel:

public AboutPageViewModel()
{
    AppName = Settings.Instance.MyAppName;
    Settings.Instance.PropertyChanged += (s,e) =>
    {
        //  If you're in C#6:
        //if (e.PropertyName == nameof(Settings.MyAppName))
        if (e.PropertyName == "MyAppName")
        {
            AppName = Settings.Instance.MyAppName;
        }
    }
}

Option Number Two

Arguably a better option; I've done it this way more than once.

In comments, @MikeEason makes the very good point that this could also be done with a custom *Changed event such as MyAppNameChanged, which has two advantages: It lets you go back to a static class, and it lets you skip the check on the property name, which is extra code and also a "magic string". Working with INotifyPropertyChanged we get a little bit numb to the danger of magic strings, but they are in fact bad. If you're in C#6, you can and absolutely should use the nameof() operator, but not all of us are in C#6 just yet. My main responsibility at work is an application that we're hoping to migrate to C#6 this summer.

public static event EventHandler<String> MyAppNameChanged;

private static String _myAppName = "";
public static String MyAppName {
    get { return _myAppName; }
    set {
        if (_myAppName != value)
        {
            _myAppName = value;
            //  C#6 again. Note (thanks OP!) you can't pass this for sender 
            //  in a static property. 
            MyAppNameChanged?.Invoke(null, value);
        }
    }
}

The drawback of this is that, well, this class is called Settings, not Setting. Maybe it's got a dozen properties changing here and there. That gets to be a real thicket of distinct property-changed events ("so what?" you may ask -- and you may have a point). My tendency is to stick with PropertyChanged if there's a whole sheaf of them, and to add an event if the class has only one or two important properties that somebody needs to keep an eye on. Either way is annoying in my view; try both and you'll eventually settle on a preference.

Upvotes: 4

Sinatr
Sinatr

Reputation: 21999

You don't need to store value in ViewModel if you already have it somewhere (I assume what you are not going to change it in ViewModel itself):

public class AboutPageViewModel : INotifyPropertyChanged
{
    public string AppName => Settings.MyAppName;
}

And as for View to know when this property is changed you need 2 things: 1) there should be a way to inform ViewModel when value is changed 2) rise PropertyChanged(nameof(AppName)) (notice INotifyPropertyChanged).

Several possibilities to make it:

  1. Settings should rise event when MyAppName value is changed, ViewModel subscribe to it and rises PropertyChanged;
  2. Store initial value, check periodically if value is changed;
  3. Use another type which implement INotifyPropertyChanged, bind to that type property instead, this will update view automatically if that type rises PropertyChanged.

Upvotes: 2

solujic
solujic

Reputation: 1004

You have to implement INotifyPropertyChanged interface on Settings class!

then use the same piece of code like this:

  private string _myAppName;
  public string MyAppName
  {
    get{return _myAppName;}
    set{_appName = value; RaisePropertyChanged("MyAppName");}
  }

Upvotes: 0

Related Questions