Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 您如何处理';选择EditemChanged';MVVM视图模型中的事件?_C#_Wpf_Silverlight_Mvvm - Fatal编程技术网

C# 您如何处理';选择EditemChanged';MVVM视图模型中的事件?

C# 您如何处理';选择EditemChanged';MVVM视图模型中的事件?,c#,wpf,silverlight,mvvm,C#,Wpf,Silverlight,Mvvm,我有一些依赖于两个属性被设置的逻辑,当两个属性都有值时它就会执行。例如: private void DoCalc() { if (string.IsNullOrEmpty(Property1) || string.IsNullOrEmpty(Property2)) return; Property3 = Property1 + " " + Property2; } 每次Property1或Property2发生更改时都需要执行该代码,但我很难弄清楚如何以一种风格上可以接受的方式

我有一些依赖于两个属性被设置的逻辑,当两个属性都有值时它就会执行。例如:

private void DoCalc() {
  if (string.IsNullOrEmpty(Property1) || string.IsNullOrEmpty(Property2))
    return;
  Property3 = Property1 + " " + Property2;
}
每次Property1或Property2发生更改时都需要执行该代码,但我很难弄清楚如何以一种风格上可以接受的方式执行该代码。以下是我看到的选择:

1) 从ViewModel调用方法

我在概念上没有问题,因为逻辑仍然在ViewModel中-我不是一个“无代码隐藏”的纳粹分子。然而,“触发器”逻辑(当任何一个属性改变时)仍然在UI层中,我不喜欢它。代码隐藏将如下所示:

void ComboBox_Property1_SelectedItemChanged(object sender, RoutedEventArgs e) {
  viewModel.DoCalc();
}
public string Property1 {
  get {return property1;}
  set {
    if (property1 != value) {
      property1 = value;
      NotifyPropertyChanged("Property1");
      DoCalc();
    }
  }
}
public ViewModel() {
  this.PropertyChanged += new PropertyChangedEventHandler(ViewModel_PropertyChanged);
}

void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) {
  if (e.PropertyName == "Property1" || e.PropertyName == "Property2") {
    DoCalc();
  }
}
2) 从属性设置器调用方法

这种方法似乎是最“纯粹”的,但也似乎很丑陋,似乎逻辑是隐藏的。它看起来是这样的:

void ComboBox_Property1_SelectedItemChanged(object sender, RoutedEventArgs e) {
  viewModel.DoCalc();
}
public string Property1 {
  get {return property1;}
  set {
    if (property1 != value) {
      property1 = value;
      NotifyPropertyChanged("Property1");
      DoCalc();
    }
  }
}
public ViewModel() {
  this.PropertyChanged += new PropertyChangedEventHandler(ViewModel_PropertyChanged);
}

void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) {
  if (e.PropertyName == "Property1" || e.PropertyName == "Property2") {
    DoCalc();
  }
}
3) 钩住PropertyChanged事件

我现在认为这可能是正确的方法,但在实现的viewmodel中挂接property changed事件感觉很奇怪。它看起来像这样:

void ComboBox_Property1_SelectedItemChanged(object sender, RoutedEventArgs e) {
  viewModel.DoCalc();
}
public string Property1 {
  get {return property1;}
  set {
    if (property1 != value) {
      property1 = value;
      NotifyPropertyChanged("Property1");
      DoCalc();
    }
  }
}
public ViewModel() {
  this.PropertyChanged += new PropertyChangedEventHandler(ViewModel_PropertyChanged);
}

void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) {
  if (e.PropertyName == "Property1" || e.PropertyName == "Property2") {
    DoCalc();
  }
}

所以,我的问题是,如果您浏览一些具有该需求的源代码,您希望看到实现哪种方法(为什么?)。谢谢你的意见。

我不认为在setter中这样做很难看。。。实际上,这可能是您提到的3种方法中最好的,因为当您阅读代码时,您会立即看到更改
Property1
Property2
的值将重新计算
Property3
;这在其他两种方法中根本不明显

然而,我不会使用这两个选项。我认为更好的方法是使
Property3
只读,并根据
Property1
Property2
在getter中计算其值:

public string Property3
{
    get { return Property3 = Property1 + " " + Property2; }
}

这样,在
Property1
Property2
的设置程序中,您只需调用
NotifyPropertyChanged
来设置
Property3

Yes@Thomas是正确的。在WPF环境中,方法2是一种完美的方法。前提是您已将ListBox.SelectedValue双向绑定添加到属性1

您的(1)无效,因为它将业务逻辑公开给视图。(3) 是一个不必要的事件处理,它无论如何都是由Property1 setter代码触发的。所以最好直接从Setter调用它。所以MVVM的方式是(2)

(2)是我通常的方式

也就是说,这让我想知道是否有其他方法可以使用Rx框架来完成这类工作:

所以这就是我想到的(警告——不要这样做!)

公共类视图模型:INotifyPropertyChanged
{
公共视图模型()
{
var o1=
可观察的。从事件(本“财产变更”);
订阅(e=>Debug.WriteLine(e.EventArgs.PropertyName));
var o2=o1.SkipWhile(e=>e.EventArgs.PropertyName!=“Property1”);
var o3=o1.SkipWhile(e=>e.EventArgs.PropertyName!=“Property2”);
var o4=o1.SkipWhile(e=>e.EventArgs.PropertyName!=“结果”);
var o5=可观察的。组合测试(o2,o3,(e1,e2)=>DoStuff())。TakeUntil(o4);
订阅(o=>Debug.WriteLine(“获得了Prop1和Prop2”);
}
公共字符串DoStuff()
{
返回结果=string.Concat(Property1,Property2);
}
私有字符串_属性1;
公共字符串属性1
{
获取{return\u property1;}
设置
{
_财产1=价值;
OnNotifyPropertyChanged(“Property1”);
}
}
私有字符串_property2;
公共字符串属性2
{
获取{return\u property2;}
设置
{
_财产2=价值;
OnNotifyPropertyChanged(“Property2”);
}
}
私有字符串_结果;
公共字符串结果
{
获取{return\u result;}
设置
{
_结果=值;
OnNotifyPropertyChanged(“结果”);
}
}
公共事件属性更改事件处理程序属性更改;
私有void OnNotifyPropertyChanged(字符串名称)
{
var handler=PropertyChanged;
if(处理程序!=null)
{
处理程序(此,新PropertyChangedEventArgs(名称));
}
}
}

除非第三个属性像您的示例中那样微不足道,否则setter
Property1
Property2
还应该设置一些
IsProperty3Invalid
标志,
Property3
的getter将检查是否需要再次调用
DoCalc
。这是一个有趣的想法(我显然没有考虑)。我必须考虑一下,因为在“真实”代码中,属性3有时是由prop1和prop2设置的,但如果不是由这些条件设置的,则用户必须手动填写。可以使用这种方法完成……我将对其进行编码并查看其外观。很好。标记正确,只是为了给您一些道具,但不确定我是否100%同意。需要沉浸其中:)@gabe,是的,很好。如果属性需要大量计算,则不应该每次我同意在setter中执行时都重新计算它,因为recalc确实是通过设置prop1/2触发的,因此它非常合理且可读。用第三个属性扩展您的想法:我宁愿使用带有备份字段和私有getter的公共属性。对于视图绑定到属性的情况,这将启用“缓存”并从属性的“内部”触发PropertyChanged事件。我不会那样做;-)