C# 轻松地将通知传播到WPF中的UI

C# 轻松地将通知传播到WPF中的UI,c#,wpf,mvvm,attributes,inotifypropertychanged,C#,Wpf,Mvvm,Attributes,Inotifypropertychanged,因此,我看到了一些对类似问题的回答,但我想知道我所想到的某种范式在C#中是否可行。首先,我将阐述这个问题: 我有一个用C#开发的MVVM应用程序。模型的特性会发生更改,当模型中的单个特性发生更改时,它通常会影响视图模型中的多个特性。因此,视图模型将侦听模型上的更改。视图将侦听视图模型上的更改 在我的视图模型中,我最终得到如下代码: private void OnModelPropertyChanged(object sender, System.ComponentModel.PropertyCh

因此,我看到了一些对类似问题的回答,但我想知道我所想到的某种范式在C#中是否可行。首先,我将阐述这个问题:

我有一个用C#开发的MVVM应用程序。模型的特性会发生更改,当模型中的单个特性发生更改时,它通常会影响视图模型中的多个特性。因此,视图模型将侦听模型上的更改。视图将侦听视图模型上的更改

在我的视图模型中,我最终得到如下代码:

private void OnModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    string prop_name = e.PropertyName;
    if (prop_name.Equals("some_property_on_the_model"))
    {
        NotifyPropertyChanged("some_property_on_the_view_model");
        NotifyPropertyChanged("some_property_on_the_view_model");
        NotifyPropertyChanged("some_property_on_the_view_model");
        NotifyPropertyChanged("some_property_on_the_view_model");
        NotifyPropertyChanged("some_property_on_the_view_model");
    }
    else if (...)
    {
        ... etc ...
    }
}
[ListenToModelProperty("some_property_on_the_model")]
[OnPropertyChanged("MyButtonVisibility")]
public Visibility MyButtonVisibility
{
    get
    {
        if (model.some_property_on_the_model == true)
        {
            return Visibility.Visible;
        }
        else
        {
            return Visibility.Hidden;
        }
    }
}

private void OnModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    prop_name = e.PropertyName;

    foreach (var property in view_model)
    {
        var attributes = property.GetCustomAttributes(typeof(ListenToModelPropertyAttribute));
        var descriptions = attributes.Select(x => x.Description);
        if (descriptions.Contains(prop_name))
        {
            notification_to_make = property.GetCustomAttributes(typeof(OnPropertyChangedAttribute));
            string notification_string = notification_to_make[0].Description;
            NotifyPropertyChanged(notification_string);
        }
    }
}
using System;
using System.Collections.Generic;

namespace TestPropagationOfPropertyChanges
{
    [AttributeUsage(AttributeTargets.All)]
    public class ListenForModelPropertyChangedAttribute : System.Attribute
    {
        public List<string> ModelPropertyNames = new List<string>();

        public ListenForModelPropertyChangedAttribute (string [] propertyNames)
        {
            ModelPropertyNames.AddRange (propertyNames);
        }
    }
}
这很烦人,因为它看起来很凌乱。如果在视图模型中添加新属性后忘记编辑此函数,则很容易导致错误

这就是我想做的,但我不知道这是否可行。所以我希望你们中的一位能帮助我理解这是否可能

如果我能使用C#的“属性”特性来处理属性更改传播,那将非常酷

所以可能是这样的:

private void OnModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    string prop_name = e.PropertyName;
    if (prop_name.Equals("some_property_on_the_model"))
    {
        NotifyPropertyChanged("some_property_on_the_view_model");
        NotifyPropertyChanged("some_property_on_the_view_model");
        NotifyPropertyChanged("some_property_on_the_view_model");
        NotifyPropertyChanged("some_property_on_the_view_model");
        NotifyPropertyChanged("some_property_on_the_view_model");
    }
    else if (...)
    {
        ... etc ...
    }
}
[ListenToModelProperty("some_property_on_the_model")]
[OnPropertyChanged("MyButtonVisibility")]
public Visibility MyButtonVisibility
{
    get
    {
        if (model.some_property_on_the_model == true)
        {
            return Visibility.Visible;
        }
        else
        {
            return Visibility.Hidden;
        }
    }
}

private void OnModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    prop_name = e.PropertyName;

    foreach (var property in view_model)
    {
        var attributes = property.GetCustomAttributes(typeof(ListenToModelPropertyAttribute));
        var descriptions = attributes.Select(x => x.Description);
        if (descriptions.Contains(prop_name))
        {
            notification_to_make = property.GetCustomAttributes(typeof(OnPropertyChangedAttribute));
            string notification_string = notification_to_make[0].Description;
            NotifyPropertyChanged(notification_string);
        }
    }
}
using System;
using System.Collections.Generic;

namespace TestPropagationOfPropertyChanges
{
    [AttributeUsage(AttributeTargets.All)]
    public class ListenForModelPropertyChangedAttribute : System.Attribute
    {
        public List<string> ModelPropertyNames = new List<string>();

        public ListenForModelPropertyChangedAttribute (string [] propertyNames)
        {
            ModelPropertyNames.AddRange (propertyNames);
        }
    }
}

请注意,上面的代码并不是真正的代码。它肯定不会编译,也不会工作。但是我想看看在C#中是否可以实现上述功能。是否可以使用属性执行类似的操作?或者有没有一个库可以让这样的事情成为可能?

我已经想出了如何做到这一点!这相当简单。我将在这里发布相关代码,感兴趣的人可以在我刚刚制作的github存储库中找到所有代码:

首先,我创建了一个自定义属性类。它是一个简单的类,将字符串数组作为其构造函数的参数。这允许您侦听模型上的多个属性以进行更改。看起来是这样的:

private void OnModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    string prop_name = e.PropertyName;
    if (prop_name.Equals("some_property_on_the_model"))
    {
        NotifyPropertyChanged("some_property_on_the_view_model");
        NotifyPropertyChanged("some_property_on_the_view_model");
        NotifyPropertyChanged("some_property_on_the_view_model");
        NotifyPropertyChanged("some_property_on_the_view_model");
        NotifyPropertyChanged("some_property_on_the_view_model");
    }
    else if (...)
    {
        ... etc ...
    }
}
[ListenToModelProperty("some_property_on_the_model")]
[OnPropertyChanged("MyButtonVisibility")]
public Visibility MyButtonVisibility
{
    get
    {
        if (model.some_property_on_the_model == true)
        {
            return Visibility.Visible;
        }
        else
        {
            return Visibility.Hidden;
        }
    }
}

private void OnModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    prop_name = e.PropertyName;

    foreach (var property in view_model)
    {
        var attributes = property.GetCustomAttributes(typeof(ListenToModelPropertyAttribute));
        var descriptions = attributes.Select(x => x.Description);
        if (descriptions.Contains(prop_name))
        {
            notification_to_make = property.GetCustomAttributes(typeof(OnPropertyChangedAttribute));
            string notification_string = notification_to_make[0].Description;
            NotifyPropertyChanged(notification_string);
        }
    }
}
using System;
using System.Collections.Generic;

namespace TestPropagationOfPropertyChanges
{
    [AttributeUsage(AttributeTargets.All)]
    public class ListenForModelPropertyChangedAttribute : System.Attribute
    {
        public List<string> ModelPropertyNames = new List<string>();

        public ListenForModelPropertyChangedAttribute (string [] propertyNames)
        {
            ModelPropertyNames.AddRange (propertyNames);
        }
    }
}
在本例中,视图模型具有“全名”属性。因此,它希望听到模型上的名字或姓氏发生的任何更改,然后对其中任何一个的更改做出反应。我意识到这不是使用这种系统的最佳“现实世界”场景,但它确实有助于说明这一概念。我的视图模型的第一部分如下:

using System;

namespace TestPropagationOfPropertyChanges
{
    public class ViewModel : NotifyPropertyChangedObject
    {
        #region Private data members

        //This is public for testing purposes
        public Model _model = new Model();

        #endregion

        #region Constructors

        public ViewModel ()
        {
            _model.PropertyChanged += ReactToModelPropertyChanged;
        }

        #endregion

        #region Properties

        [ListenForModelPropertyChangedAttribute(new string [] {"FirstName", "LastName"})]
        public string FullName 
        {
            get
            {
                return _model.FirstName + _model.LastName;
            }
        }
最后,视图模型以对模型上的更改作出反应的方法结束。通常,在大型复杂应用程序中,此方法可能包含一个大型if-else语句,其中包含大量对NotifyPropertyChanged的调用。相反,我们现在只需遍历视图模型的属性,看看哪些属性订阅了已更改的模型属性。见下文:

void ReactToModelPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    //Get the name of the property that was changed on the model
    string model_property_changed = e.PropertyName;

    //Get a System.Type object representing the current view-model object
    System.Type t = typeof(ViewModel);

    //Retrieve all property info for the view-model
    var property_info = t.GetProperties();

    //Iterate through each property
    foreach (var property in property_info) 
    {
        //Get the custom attributes defined for this property
        var attributes = property.GetCustomAttributes (false);
        foreach (var attribute in attributes) 
        {
            //If the property is listening for changes on the model
            var a = attribute as ListenForModelPropertyChangedAttribute;
            if (a != null) 
            {
                //If the property that was changed on the model matches the name
                //that this view-model property is listening for...
                if (a.ModelPropertyNames.Contains(model_property_changed))
                {
                    //Notify the UI that the view-model property has been changed
                    NotifyPropertyChanged (property.Name);
                }
            }
        }
    }
}

总的来说,它工作得非常出色,这正是我所需要的。对于感兴趣的人来说,此代码可以很容易地扩展,使其功能更加强大。

这也是我们团队经常头疼的问题。Fody PropertyChanged试图以稍微不同的方式帮助解决此问题。我个人还没有在生产中尝试过!