手动触发 PostSharp 的 INotifyPropertyChanged 方面的 OnPropertyChanged

Trigger OnPropertyChanged in PostSharp's INotifyPropertyChanged aspect manually

我正在使用 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;
    }
}

正如我在上面代码中的评论中所述,当身份验证对话框完成时,我需要触发 IsNotAuthenticatedNotifyPropertyChanged 事件,因为对话框很可能更改了值。

正在阅读the documentation,看来应该很容易:

Target classes can customize the NotifyPropertyChangedAttribute aspect by implementing one or many of the members documented in the NotifyPropertyChangedAttributeTargetClass class.

并来自 NotifyPropertyChangedAttributeTargetClass.OnPropertyChanged 页面:

Raises the PropertyChanged event. If this method is already present in the target code, the NotifyPropertyChangedAttribute aspect will use it to raise the PropertyChanged event. Otherwise, the aspect will introduce the method into the target class.

太好了,所以我尝试在我的视图模型中实现一个空的 OnPropertyChanged 方法,以为它会被 PostSharp 的版本所取代,但是,唉,它反而破坏了所有通知逻辑。这是我替换 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);
    }
}

测试代码,第一种情况有效,第二种情况无效。所以我的空 OnPropertyChanged 实现导致该功能停止工作。

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

If this method is already present in the target code, the NotifyPropertyChangedAttribute aspect will use it to raise the PropertyChanged event." -- but the generated code above and my testing says otherwise.

我最终做的 "in the meantime",直到我弄清楚我是什么或 PostSharp 做错了什么,创建我自己的方面,就像这样:

[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 });
    }
}

然后在我的 class 中标记一个方法:

[PropertyChangedTrigger]
private void TriggerOnPropertyChanged(string propertyName)
{   
}

现在我可以调用它了,它又会为我触发 OnPropertyChanged

关于我在这里做错了什么有什么想法吗?有人得到这个为他们工作吗?

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

Raises the PropertyChanged event. If this method is already present in the target code, the NotifyPropertyChangedAttribute aspect will use it to raise the PropertyChanged event. Otherwise, the aspect will introduce the method into the target class.

如果该方法已存在于 class 中,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

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