【问题标题】:RaisePropertyChanged in another view modelRaisePropertyChanged 在另一个视图模型中
【发布时间】:2015-10-13 09:07:48
【问题描述】:

我有一个主视图模型

public class MainViewModel : ViewModelBase
{
    public MenuViewModel MenuVM { get; set; }
    public StatusBarViewModel StatusBarVM {get; set; }
}

每个子视图模型都有绑定在视图上的属性:

public class MenuViewModel
{
    private string _property1;
    public  string  Property1
    {
        get { return _property1; }
    }
}

public class StatusBarViewModel
{
    private string _property2;
    public  string  Property2
    {
        get { return _property2; }
        set
        {
            _property2 = value;
            RaisePropertyChanged("Property2");
            RaisePropertyChanged("Property1");
        } 
    }
}

我想做的是,什么时候Property2被改变了,为了更新Property1,raise property改变。

所以问题是 Property1.Get 在我更改 Property2 时没有被调用(我用断点进行了测试)。

问题是:

为什么这不起作用? 如何做这项工作?

谢谢:)

【问题讨论】:

  • 您需要为正确的视图模型实例调用它。例如。 menuViewModelInstance.RaisePropertyChanged("Property1");
  • @Sinatr,它不能工作,因为从属性的另一个实例调用 RaisePropertyChanged 时 MVVMLight 处理程序为空。然后它返回而不调用处理程序。见代码: var handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); }

标签: c# wpf mvvm


【解决方案1】:

记住!您不能从实例调用RaisePropertyChanged(),因为该方法在 MVVM Light 中受到保护!所以在你的MenuViewModel 中做一个包装器:

public void RaiseProperty1Changed()
{
     RaisePropertyChanged("Property1");
}

在您的MainViewModel 中订阅Property2 中的Property2 中的RaisePropertyChanged 事件StatusBarViewModel

StatusBarVM.PropertyChanged += OnProperty2Changed

在这个委托方法调用中:

private void OnProperty2Changed(object sender, PopertyChangedEventArgs e)
{
    if (e.PropertyName == "Property2")
    {
            MenuVM.RaiseProperty1Changed();
    } 
}

【讨论】:

    【解决方案2】:
    public class MainViewModel : ViewModelBase
    {
        public MenuViewModel MenuVM { get; set; }
        public StatusBarViewModel StatusBarVM { get; set; }
    
        public MainViewModel()
        {
            MenuVM = new MenuViewModel();
            StatusBarVM = new StatusBarViewModel();
    
            StatusBarVM.PropertyChanged += (s, e) =>
            {
                if (e.PropertyName == "Property2" && MenuVM != null)
                    MenuVM.RaisePropertyChanged("Property1");
            };
        }
    }
    
    public class MenuViewModel : INotifyPropertyChanged
    {
        private string _property1;
        public string Property1
        {
            get { return _property1; }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        internal void RaisePropertyChanged(string p)
        {
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(p));
        }
    }
    
    public class StatusBarViewModel : INotifyPropertyChanged
    {
        private string _property2;
        public string Property2
        {
            get { return _property2; }
            set
            {
                _property2 = value;
                RaisePropertyChanged("Property2");
            }
        }
    
        private void RaisePropertyChanged(string p)
        {
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(p));
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    }
    

    【讨论】:

      【解决方案3】:

      由于其他答案显示了很好的解决方法,它们创建了对 ViewModel 的硬引用,或者检查要更改的属性的名称,我发现耦合太多,我建议您使用另一种方法来实现状态栏。

      一个好的方法是使用Messenger 模式,我在这里构建了一个示例库,它提供了一个StatusBar UserControl,以及一个帮助器类StatusManager,它帮助StatusBar 内的控件到Subsribe更新,并允许外部对象将updates 发送到状态栏

      订阅:

      public static void Subscribe<TSubscriber, TMessage>(string token, Action<TMessage> subscriberAction)
              {
                  _subscribers.Add(new Subscribtion<TSubscriber, TMessage>(subscriberAction, token));
              }
      

      更新中:

      public static void UpdateStatus<TSubscriber, TMessage>(object status, string token)
              {
                  if (!(status.GetType() == typeof(TMessage)))
                  {
                      throw new ArgumentException("Message type is different than the second type argument");
                  }
      
                  var subscribersWithCorrespondingType = (from subscriber in _subscribers
                                     where (subscriber.GetType().GenericTypeArguments[0] == typeof(TSubscriber)) &&
                                            (subscriber.GetType().GenericTypeArguments[1] == typeof(TMessage))
                                     select subscriber).ToList();
      
                  var subscribers = (from subscriber in subscribersWithCorrespondingType
                                     where ((Subscribtion<TSubscriber, TMessage>) subscriber).Token == token
                                     select subscriber).ToList();
      
                  foreach (var subscriber in subscribers)
                  {
                      ((Subscribtion<TSubscriber, TMessage>)subscriber).SubscriberAction((TMessage)status);
                  }
              }
      

      使用:

      如果状态栏包含一个名为lblCursorPosition 的标签,它可以订阅string 类型的更新:

      StatusManager.Subscribe<Label, string>((s) =>
                  {
                      lblCursorPosition.Content = s;
                  });
      

      并且更新它看起来像这样:

      private void Button_Click(object sender, RoutedEventArgs e)
              {
                  StatusManager.UpdateStatus<Label, string>("StatusBar's label updated !!"); 
              }
      

      如您所见,这提供了更多的独立性和灵活性,我很高兴收到建议和更正。

      【讨论】:

      • 好主意。在这种情况下,我会在 MVVM Light 中使用 Messenger。但是“创建硬引用”到底错在哪里?
      • @SebastianRichter 这不是“错”,我只是发现它以某种方式耦合,保持你的类(甚至你的视图模型)解耦总是更好的,更少耦合的代码,更好的软件。跨度>
      【解决方案4】:

      我想增强@Sebastian Richter 解决方案,使其在通知属性方面更加通用:

      /// <summary>
      /// Wrapper to Raise a Property name from outside this View Model
      /// It's not possible to call RaisePropertyChanged(..) from outside in MVVM Light.
      /// </summary>
      /// <param name="propertyName"></param>
      internal void OuterRaisePropertyChanged(string propertyName)
      {
          RaisePropertyChanged(propertyName);
      }
      

      然后从另一个 Class 实例调用它:

      public class StatusBarViewModel
      {
          private string _property2;
          public  string  Property2
          {
              get { return _property2; }
              set
              {
                  _property2 = value;
                  RaisePropertyChanged(nameof(Property2));
                  OuterRaisePropertyChanged(nameof(MenuVM.Property1));
              } 
          }
      }
      

      请注意,使用 nameof 关键字的好处是,当您使用 VS IDE 重命名属性名称时,它将重命名包含 RaisePropertyChanged 参数的解决方案中的所有变量名称。

      @Epitouille,我认为这就是您要搜索的内容。

      问候

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-09
        • 2018-01-18
        • 2014-04-03
        • 2018-05-23
        相关资源
        最近更新 更多