【问题标题】:How to notify Properties outside of an Object instance?如何通知对象实例之外的属性?
【发布时间】:2019-04-25 13:31:18
【问题描述】:

我是 WPF 初学者,我对 PropertyChanged 事件有疑问:

我有一个包含另一个视图模型实例的视图模型。 (这里我将使用通用名称)

我希望AxisVM 的实例通知我的SomeDouble 属性。 (不能使用转换器)

编辑:我没有在这里包含完整的课程,PropertyChangedEvent 显然已实现。

public class ViewModel : INotifyPropertyChanged
{
   private AxisVM axis;
 
   public ViewModel()
   {
      this.AxisVM = new AxisVM();
   }
   
   public AxisVM Axis
   {
     get { return axis}; 
     set { axis = value; FireOnPropertyChanged(); }
   }

   public double SomeDouble
   {
     get { return axis.Lowerlimit * 1.5 }; 
   }

}

AxisVM 也继承自 INotifyPropertyChanged(我使用 ClassMemberName)

public class AxisVM: INotifyPropertyChanged
{
   private double lowerLimit;

   public double LowerLimit
   {
     get { return lowerLimit }; 
     set { lowerLimit = value; FireOnPropertyChanged(); }
   }

}

在 XAML 中,我将 Viewmodel 绑定为 DataContext(我认为在这种情况下无关紧要),然后将下限绑定到文本框。

当我编辑文本框时,轴的下限事件被触发并且值被更改(视图和视图模型)但我需要通知我的SomeDouble 属性,因为它会在下限更改时更新。

我的 ViewModel 中的轴实例的属性更改事件永远不会被触发,即使我访问它的属性(它会触发它的事件但不会通知我的 SomeDouble 属性)。

我现在不知所措,感谢任何帮助。

【问题讨论】:

  • 请看这个docs.microsoft.com
  • 您可以为此使用事件。或者将两个 Ui 元素绑定到 LowerLimit 并使用转换器(仅当 SomeDouble 仅用于在 UI 中显示时)。
  • 经常有父子关系。如果父母需要了解孩子,那么通常父母会订阅孩子事件。公共Axis setter 是个问题,您必须从那里取消订阅以前的孩子并订阅新的(或者您可能会出现内存泄漏)。父母也可能不得不在某个地方取消订阅。你真的需要public setter 吗?
  • 为什么somedouble不在axisvm或viewmodel中的下限?视图模型的目的是使数据和流程适应视图。将这些属性放在单独的视图模型中似乎只会让事情变得更难。
  • 因此,您无需将相关属性放在一个或另一个 vm 中,而是创建一个依赖项。一个虚拟机需要知道另一个虚拟机的属性。这是你应该避免的。

标签: c# wpf inotifypropertychanged


【解决方案1】:

只需在您的视图模型中处理AxisVM 中的PropertyChanged

public class ViewModel : INotifyPropertyChanged
{
    private readonly AxisVM axis;

    public ViewModel()
    {
        axis = new AxisVM();
        axis.PropertyChanged += Axis_PropertyChanged;      
    }

    private void Axis_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        FireOnPropertyChanged(nameof(SomeDouble));
    }

    ...
}

【讨论】:

  • 它有效,谢谢,但没有更清洁的方法吗?
  • 连接事件处理程序和处理对象事件的更简洁方法?不,我不这么认为。
  • 您应该检查Axis_PropertyChanged 方法中的属性名称,因为它会被每个属性触发,而不仅仅是LowerLimit
【解决方案2】:

你可以为此使用一个事件。

在 AxisVM 中添加事件

public class AxisVM: INotifyPropertyChanged
{
   public event EventHandler LowerLimitChanged;  

   private double lowerLimit;

   public double LowerLimit
   {
     get { return lowerLimit }; 
     set { lowerLimit = value; FireOnPropertyChanged(); LowerLimitChanged?.Invoke(this, EventArgs.Empty); }
   }
}

然后像这样订阅

public class ViewModel : INotifyPropertyChanged
{
   private AxisVM axis;

   public ViewModel()
   {
      this.AxisVM = new AxisVM();
      this.AxisVM.LowerLimitChanged += OnLowerLimitChanged;
   }

   public AxisVM Axis
   {
     get { return axis}; 
     set { axis = value; FireOnPropertyChanged(); }
   }

   public double SomeDouble
   {
     get { return axis.Lowerlimit * 1.5 }; 
   }

   public void OnLowerLimitChanged(object sender, EventArgs e)
   {
      FireOnPropertyChanged("SomeDouble");
   }
}

您可以在属性 public AxisVM Axis 中删除 FireOnPropertyChanged("SomeDouble");,因为这只会在设置 AxisVM 实例时触发,而不会在此实例中的属性更改时触发。

【讨论】:

  • PropertyChanged 事件在您实现 INotifyPropertyChanged 时已经可用
  • 不确定@walkerbox 是否相同,但您不需要LowerLimitChanged 事件。订阅PropertyChanged 并检查e.PropertyNamenameof(LowerLimit) 还是""(用于告诉“所有属性都已更改”)。
  • 我已经这样做了,我会再做一次。消费者可能只对LowerLimit 的更改感兴趣。添加LowerLimitChanged 事件除了PropertyChanged 显然不是唯一的解决方案,但它是一个非常明智的解决方案。你可能太正交了。
【解决方案3】:

当您在 AxisVM 中实现 INotifyPropertyChanged 时,您在那里添加了 PropertyChanged 事件。在ViewModel 处理它并触发FireOnPropertyChanged()

Axis.PropertyChanged += OnAxisVMPropertyChanged(...)
void OnAxisVMPropertyChanged(..)
{
    // Check property name
    // Fire OnPropertyChanged for LowerLimit
}

【讨论】:

  • 我已经在 AxisVm 中触发了该事件。当 Lowerlimit 更改时,它会在 UI 中更新。我需要的是通知我的 SomeDouble 属性的 AxisVM 实例。
  • @d0neall:你看到我的回答了吗?您应该在事件处理程序中为SomeDouble 属性引发PropertyChanged 事件。
  • 对不起。它有效,但我有几个实例,我不应该使用太多事件。不过谢谢!
  • is there not a cleaner way for doing this - 只是我的意见,但为了更简洁的代码检查 MVVM Light lib 中的 Messenger。允许您发布消息,您不必拥有发布者的实例。连同编写不相互保存实例的小型 ViewModel 一起,提供了非常好的代码。
猜你喜欢
  • 1970-01-01
  • 2016-02-17
  • 2014-09-15
  • 2016-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多