在WPF视图模型上调用OnPropertyChanged的属性

时间:2019-02-10 09:11:47

标签: c# wpf mvvm

在wpf应用程序的视图模型中,我经常需要声明如下属性:

 public string IdAnalisi5
  {
     get { return ricettaCorrente.IdAnalisi1; }
     set
     {
        ricettaCorrente.IdAnalisi5 = value;
        OnPropertyChanged(nameof(IdAnalisi5));
     }
  }

该类实现OnPropertyChanged方法来触发ui的更新,但是大多数时候我只需要调用此方法,并且是很多重复的代码。
实际上可以创建一个属性,我可以将其添加到所有类中,并使用在设置值后调用该方法的方法来实现一个类或接口吗?
我想转换成这样的东西

[PropertyChanged(nameof(IdAnalisi5)]
public string IdAnalisi5 => ricettaCorrente.IdAnalisi1; 

通常可以节省很多代码。

2 个答案:

答案 0 :(得分:1)

正如我所说,我不知道通过“属性”解决此问题的方法,但是有一些动态代理模式的实现可简化OnPropertyChanged的使用。

创建代理

    public class ProxyCreator
{
  public static T MakeINotifyPropertyChanged<T>() where T : class, new ()
  {
    //Creates a proxy generator
    ProxyGenerator proxyGen = new  ProxyGenerator();

    //Generates a proxy using our Interceptor and implementing INotifyPropertyChanged
    var proxy = proxyGen.CreateClassProxy(
      typeof (T),
      new  Type[] { typeof (INotifyPropertyChanged) },
      ProxyGenerationOptions.Default,
      new  NotifierInterceptor()
      );

    return proxy as T;
  }
}

拦截器,它主要做两件事:

它公开了PropertyChangedEventHandler, 当使用好名字调用设置器时,它将引发PropertyChangedEventHandler事件。此外,它还缓存了PropertyChangedEventArgs,以提高性能。

public class NotifierInterceptor : IInterceptor
{
  private PropertyChangedEventHandler handler;
  public static Dictionary<String, PropertyChangedEventArgs> _cache =
    new  Dictionary<string, PropertyChangedEventArgs>();

  public void Intercept(IInvocation invocation)
  {
    //Each subscription to the PropertyChangedEventHandler is intercepted (add)
    if (invocation.Method.Name == "add_PropertyChanged")
    {
      handler = (PropertyChangedEventHandler)
            Delegate.Combine(handler, (Delegate)invocation.Arguments[0]);
      invocation.ReturnValue = handler;
    }
    //Each de-subscription to the PropertyChangedEventHandler is intercepted (remove)
    else if (invocation.Method.Name == "remove_PropertyChanged")
    {
      handler = (PropertyChangedEventHandler)
         Delegate.Remove(handler, (Delegate)invocation.Arguments[0]);
      invocation.ReturnValue = handler;
    }
    //Each setter raise a PropertyChanged event
    else if (invocation.Method.Name.StartsWith("set_"))
    {
      //Do the setter execution
      invocation.Proceed();
      //Launch the event after the execution
      if (handler != null)
      {
        PropertyChangedEventArgs arg =
          retrievePropertyChangedArg(invocation.Method.Name);
        handler(invocation.Proxy, arg);
      }
    }
    else invocation.Proceed();
  }

  // Caches the PropertyChangedEventArgs
  private PropertyChangedEventArgs retrievePropertyChangedArg(String methodName)
  {
    PropertyChangedEventArgs arg = null;
    NotifierInterceptor._cache.TryGetValue(methodName, out arg);
    if (arg == null)
    {
      arg = new  PropertyChangedEventArgs(methodName.Substring(4));
      NotifierInterceptor._cache.Add(methodName, arg);
    }
    return arg;
  }
}

最后的用法是这样的:

MyBusinessObject myBusinessObject;
DataContext = myBusinessObject = ProxyCreator.MakeINotifyPropertyChanged<MyBusinessObject>();

我还发现了动态代理的其他实现方式也是如此:

Implement InotifyPropertyChanged with Castle.DynamicProxy

希望对您有所帮助:)

答案 1 :(得分:0)

您可以使用MVVM Light Toolkit。您需要从ViewModelBase继承视图模型,然后才能编写如下代码:

public string IdAnalisi5
{
    get => ricettaCorrente.IdAnalisi5;
    set => Set(ref ricettaCorrente.IdAnalisi5, value);
}

请注意,这仅在ricettaCorrente.IdAnalysi5是字段而非属性的情况下有效。

MVVM Light Toolkit不是唯一提供此功能的工具。其他类似工具包括Prism和Caliburn.Micro。