简体   繁体   中英

NotifyPropertyChanged not firing event [PostSharp]

I'm new to PostSharp (just got my license) and I've been trying to use it in my app. I have a settings class a following:

[NotifyPropertyChanged]
public class Consts
{
    public string test2 {get; set;} = "foobar";

    public string test
    {
        get { return GetValue("test"); }
        set { UpdateSetting(nameof(test), value.ToString(CultureInfo.InvariantCulture)); }
    }

    [Pure]
    public static string GetValue(string s) => ConfigurationManager.AppSettings[nameof(s)];

    [Pure]
    private static void UpdateSetting(string key, string value)
    {
        var cfg = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

        cfg.AppSettings.Settings[key].Value = value;
        cfg.Save(ConfigurationSaveMode.Modified);

        ConfigurationManager.RefreshSection("appSettings");
    }
}

Then on my subscriber class:

var cst = new Consts();
Post.Cast<Consts, INotifyPropertyChanged>(cst).PropertyChanged +=
                (o, args) => Debug.Write("PropertyChanged fired");
cst.test = "test test"; // Gives no result
cst.test2 = "test test"; // Event firing correctly

The event doesn't fire when I use methods in my getters & setters, although marked pure, but works fine when it's a simple property.

I spent the last day scouring Google for answers, without luck; no thread solves my problem.

What am I missing ?

[NotifyPropertyChanged] aspect detects changes to fields of the class and then fires appropriate events based on detected dependencies (property value depending on that specific field).

In your case this is exactly what test2 property does and why aspect works on that property.

On the other hand test property cannot work automatically. The value of the property depends on ConfigurationManager.AppSettings.Item . First problem is that AppSettings is a static property, ie it is not possible to detect changes to it. If assumed that it never changes, then second problem is that NameValueCollection does not implement INotifyPropertyChanged , which means that there is no way of knowing that the value actually changed.

You are not getting any warnings because you have marked both methods as Pure which they are not in usual sense of the word. GetValue uses global mutable state. SetValue changes the global mutable state.

Since there is no way to hook to AppSettings in order to receive changes to the collection, you need to raise changed notification when the property is set. This can be done by calling NotifyPropertyChangedServices.SignalPropertyChanged method. Your code would then look like this:

[NotifyPropertyChanged]
public class Consts
{
    public string test2 { get; set; } = "foobar";

    public string test
    {
        get { return GetValue("test"); }
        set { UpdateSetting(nameof(test), value.ToString(CultureInfo.InvariantCulture)); }
    }

    [SafeForDependencyAnalysis]
    public string GetValue(string s) => ConfigurationManager.AppSettings[nameof(s)];

    private void UpdateSetting(string key, string value)
    {
        var cfg = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

        cfg.AppSettings.Settings[key].Value = value;
        cfg.Save(ConfigurationSaveMode.Modified);

        ConfigurationManager.RefreshSection("appSettings");
        NotifyPropertyChangedServices.SignalPropertyChanged(this, key);
    }
}

Note that if multiple instance of Consts class exist, they will not share changes there is no way to pass that information through ConfigurationManaged .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM