手动触发PostSharp的INotifyPropertyChanged方面的PropertyChanged

时间:2015-02-16 09:28:54

标签: c# inotifypropertychanged postsharp

我使用PostSharp的NotifyPropertyChanged方面。

我遇到需要手动触发PropertyChanged事件的情况,因为我的用例对于PostSharp来说似乎太复杂了。以下是我的视图模型中的相关摘录:

private RelayCommand _authenticateCommand;
[IgnoreAutoChangeNotificationAttribute]
public ICommand AuthenticateCommand
{
    get { return _authenticateCommand ?? (_authenticateCommand = new RelayCommand(Authenticate, _ => IsNotAuthenticated)); }
}

private void Authenticate(object obj)
{
    var result = _dialogService.OpenAuthenticationDialog();
    if (result.HasValue && result.Value)
    {
        _mainViewModel.Update();
    }
    // At this point I need to trigger NotifyPropertyChanged for IsNotAuthenticated,
    // because the authentication dialog causes changes
    // to the _authenticationViewModel's IsLoggedIn property.
}

public bool IsNotAuthenticated
{
    get
    {
        return !_authenticationViewModel.IsLoggedIn;
    }
}

如上面代码中的评论所述,当验证对话框完成后,我需要触发NotifyPropertyChanged的{​​{1}}事件,因为对话框可能会更改值。

阅读the documentation,似乎应该很容易:

  

目标类可以通过实现NotifyPropertyChangedAttributeTargetClass类中记录的一个或多个成员来自定义NotifyPropertyChangedAttribute方面。

NotifyPropertyChangedAttributeTargetClass.OnPropertyChanged页面:

  

引发PropertyChanged事件。如果此方法已存在于目标代码中,则NotifyPropertyChangedAttribute方面将使用它来引发PropertyChanged事件。否则,方面将把方法引入目标类。

很好,所以我尝试在我的视图模型中实现一个空的IsNotAuthenticated方法,认为它将被PostSharp的版本取代,但唉,它只是打破了所有通知逻辑。这是在我替换OnPropertyChanged之前生成的代码的外观:

OnPropertyChanged

这就是它的照顾方式:

protected void OnPropertyChanged(string propertyName)
{
    this.<>z__aspect0.OnPropertyChanged(propertyName);
}

测试代码,它适用于第一种情况,而不是第二种情况。因此,我执行空protected void OnPropertyChanged(string propertyName) { this.<>z__aspect0.OnMethodEntry(null); try { } finally { this.<>z__aspect0.OnMethodExit(null); } } 会导致该功能停止工作。

我猜文档在撒谎?它明确地说(强调我的)

  

如果目标代码中的此方法已存在,则NotifyPropertyChangedAttribute方面将使用它来引发PropertyChanged事件。&#34; - 但上面生成的代码和我的测试说不然。

我在此期间做了什么&#34;在此期间,直到我弄清楚我是什么或者PostSharp做错了,创造我自己的方面,就像这样:

OnPropertyChanged

然后在我的班级用它标记一个方法:

[Serializable]
class PropertyChangedTriggerAttribute : MethodInterceptionAspect
{
    public override void OnInvoke(MethodInterceptionArgs args)
    {
        var inpc = args.Method.DeclaringType;
        if (inpc == null) throw new InvalidOperationException("PropertyChangedTriggerAttribute used on an object that doesn't have an OnPropertyChanged(System.String) method.");
        var opc = inpc.GetMethod("OnPropertyChanged", BindingFlags.Instance | BindingFlags.NonPublic);
        var value = (string) args.Arguments.GetArgument(0);
        opc.Invoke(args.Instance, new object[] { value });
    }
}

现在我可以打电话给它,然后它会为我触发[PropertyChangedTrigger] private void TriggerOnPropertyChanged(string propertyName) { }

关于我在这里做错了什么的任何想法?有谁让这个为他们工作?

1 个答案:

答案 0 :(得分:3)

我认为您误解了以下内容:

  

引发PropertyChanged事件。如果此方法已存在于目标代码中,则NotifyPropertyChangedAttribute方面将使用它来引发PropertyChanged事件。否则,方面将把方法引入目标类。

如果该方法已存在于类中,NotifyPropertyChangedAttribute将使用它。这意味着该方法需要执行实际通知,因为方面正在使用它而不是它自己的实现。因此,您需要引发PropertyChanged事件。这意味着您需要自己实现INotifyPropertyChanged接口:

[NotifyPropertyChanged]
public class TestClass : INotifyPropertyChanged
{
    public int Property1 { get; set; }
    public int Property2 { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;

    public void DoSomething()
    {
        this.Property1 = 42;
        this.OnPropertyChanged( "Property2" );
    }

    protected void OnPropertyChanged( string propertyName )
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if ( handler != null )
            handler( this, new PropertyChangedEventArgs(propertyName) );
    }
}

class Program
{
    static void Main(string[] args)
    {
        TestClass test = new TestClass();
        INotifyPropertyChanged inpc = Post.Cast<TestClass, INotifyPropertyChanged>(test);

        inpc.PropertyChanged += ( s, ea ) =>
        {
            Console.WriteLine("Notification received for {0}", ea.PropertyName);
        };

        test.DoSomething();
    }
}

这样,它可以按照您的意愿工作,即输出为:

Notification received for Property2
Notification received for Property1

请注意,顺序是这样的,因为当 DoSomething 方法退出时,会自动(通过方面)引发 Property1 更改。