代码之家  ›  专栏  ›  技术社区  ›  Alex

在PostSharp的INotifyPropertyChanged方面手动触发OnPropertyChanged

  •  0
  • Alex  · 技术社区  · 11 年前

    我正在使用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 的事件 IsNotAuthenticated ,因为对话框很可能更改了值。

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

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

    NotifyPropertyChangedAttributeTargetClass.OnPropertyChanged 第页:

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

    太好了,所以我尝试实现一个空 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 导致功能停止工作。

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

    如果此方法为 已存在 在目标代码中,NotifyPropertyChangedAttribute方面 将使用它 引发PropertyChanged事件。”——但上面生成的代码和我的测试表明并非如此。

    在我弄清楚我是谁或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 });
        }
    }
    

    然后用它标记类中的一个方法:

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

    现在我可以称之为,它反过来触发 OnPropertyChanged 为了我。

    你知道我做错了什么吗?有人用这个为他们工作吗?

    1 回复  |  直到 10 年前
        1
  •  3
  •   Daniel Balas    11 年前

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

    引发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
    

    注意,订单是这样的,因为 特性1 DoSomething(拒绝某事) 方法正在退出。

    推荐文章