【问题标题】:Most efficient way to notify view(WPF/C#)通知视图的最有效方式(WPF/C#)
【发布时间】:2017-01-25 19:52:26
【问题描述】:

我有一个汽车课。

class Car
{
  string ModelNum;
}

然后我有 Car 实例

class CarInstance
{
  string RegistrationNum;
}

在我的视图模型中有几个 Cars 实例。

 ViewModel(Car car)
 {
    CarInstance Ins = car.GetInstance();
 }

问题:假设 Car 本身正在发生变化,它需要通知视图模型该变化。什么是最有效的方法来做到这一点。我知道我可以使用事件(包括 PRISM 中的 eventtaggregator)。我想知道是否有更快的方法来做到这一点。

一个可以调用多个订阅者的Action参数?有这样的想法吗?

所有伪代码。

【问题讨论】:

  • 你遇到过 INotifyPropertyChanged 吗?
  • 是的。两个问题。这比活动更有表现力吗?将属性通知放在 Car 类中是否可以接受(这是一个业务对象,并且通知属性更改是 UI 特定的想法)?
  • 我相信是的。是的,将属性通知放在你的模型类中是很正常的,比如你拥有的 Car。这些事件是从模型属性的设置器中引发的。你也可以从你的 ViewModel 接收通知,但是如果你要在你的 UI 上绑定这些通知,那么将它们放在你的属性设置器中很重要

标签: c# wpf


【解决方案1】:

INotifyPropertyChanged 是否比事件更有效?

INotifyPropertyChanged 接口定义了一个PropertyChanged 事件,即它实际上是使用一个事件来通知视图。

假设 Car 本身正在发生变化,它需要通知视图模型该变化。什么是最有效的方法来做到这一点。我知道我可以使用事件(包括 PRISM 中的 eventtaggregator)。我想知道是否有更快的方法来做到这一点。

在大多数情况下,就性能而言,引发事件的影响很小,因此您不必担心:

How much performance overhead is there in using events?

使用事件聚合器的好处是,与从每个发布者类保持对每个订阅者的直接引用相比,您的类之间的耦合更加松散:https://blog.magnusmontin.net/2014/02/28/using-the-event-aggregator-pattern-to-communicate-between-view-models/

但是,无论是使用强引用还是事件聚合器来引发事件,都是一种很好的、​​推荐的、快速的方式来通知外部世界发生了变化。

【讨论】:

    【解决方案2】:

    如果属性更改通知绑定到您的视图,则在模型中放置属性更改通知是绝对正常的。它是 MVVM 中任何东西的首选方法。这就是你可以做到的方式

    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    
    public class Car: INotifyPropertyChanged
    {
        #region Property Changed
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion
    
        private string _modelNum;
    
        public string ModelNum 
        {
            get{ return _modelNum; }
            set 
            { 
                _modelNum = value; 
                //this will raise the notification
                OnPropertyChanged("ModelNum"); 
            }
        }
    }
    

    如果这个汽车对象绑定到你的View,你可以

    Text="{Binding Car.ModelNum, UpdateSourceTrigger=PropertyChanged}"
    

    UpdateSourceTrigger=PropertyChanged 将在 ModelName 更新时更新 UI,或者在您从 UI 更新时更新 ModelName。

    【讨论】:

      【解决方案3】:

      为通知创建以下类:

      public abstract class Notifier : INotifyPropertyChanged
      {
          public event PropertyChangedEventHandler PropertyChanged;
          private readonly Dictionary<string, object> _properties = new Dictionary<string, object>();
      
          protected T Get<T>([CallerMemberName] string name = null)
          {
              Debug.Assert(name != null, "name != null");
              object value = null;
              if (_properties.TryGetValue(name, out value))
                  return value == null ? default(T) : (T)value;
              return default(T);
          }
          protected void Set<T>(T value, [CallerMemberName] string name = null)
          {
              Debug.Assert(name != null, "name != null");
              if (Equals(value, Get<T>(name)))
                  return;
              _properties[name] = value;
              PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
          }
          public void Notify<T>(Expression<Func<T>> memberExpression)
          {
              if (memberExpression == null)
              {
                  throw new ArgumentNullException("memberExpression");
              }
              var body = memberExpression.Body as MemberExpression;
              if (body == null)
              {
                  throw new ArgumentException("Lambda must return a property.");
              }
      
              var vmExpression = body.Expression as ConstantExpression;
              if (vmExpression != null)
              {
                  LambdaExpression lambda = Expression.Lambda(vmExpression);
                  Delegate vmFunc = lambda.Compile();
                  object sender = vmFunc.DynamicInvoke();
      
                  PropertyChanged?.Invoke(sender, new PropertyChangedEventArgs(body.Member.Name));
              }
          }
      }
      

      您的 ViewModel 应该如下所示。

      public class ViewModel : Notifier
      {
          public ObservableCollection<String> Messages
          {
              get { return Get<ObservableCollection<String>>(); }
              set
              {
                  Set(value); 
                  Notify(() => AreThereMessages);
              }
          }
      
          public bool AreThereMessages => Messages?.Count > 0;
      }
      

      上面的 Notifier 类不需要任何私有变量。

      吸气剂:get { return Get&lt;T&gt;(); }

      二传手:get { Set(value); }

      通知其他属性:Notify(() =&gt; OtherProperty);

      示例视图:

      <DataGrid ItemsSource="{Binding Messages}"/>
      <Button IsEnabled="{Binding AreThereMessages}"/>
      

      上面的代码通知一个按钮消息集合是否为空。 这是我所知道的无需额外库即可处理您的视图模型的最简单方法。

      【讨论】:

        猜你喜欢
        • 2011-05-14
        • 2012-11-28
        • 1970-01-01
        • 2014-03-21
        • 2018-06-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多