你如何处理MVVM ViewModel中的'SelectedItemChanged'事件?

时间:2010-03-13 22:31:36

标签: c# wpf silverlight mvvm

我有一些逻辑依赖于设置的两个属性,因为它在两个属性都有值时执行。例如:

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();
}

2)来自Property Setter的调用方法

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

public string Property1 {
  get {return property1;}
  set {
    if (property1 != value) {
      property1 = value;
      NotifyPropertyChanged("Property1");
      DoCalc();
    }
  }
}

3)进入PropertyChanged事件

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

public ViewModel() {
  this.PropertyChanged += new PropertyChangedEventHandler(ViewModel_PropertyChanged);
}

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

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

3 个答案:

答案 0 :(得分:3)

我不认为在setter中这样做是丑陋的......实际上它可能是你提到的3种方法中最好的,因为当你阅读代码时,你会立即看到更改Property1的值或Property2将重新计算Property3;对于其他两种方法,这一点并不明显。

但是,我不会使用这些选项。根据{{​​1}}和Property3,我认为更好的方法是将Property1只读,并在getter中计算其值:

Property2

这样,在public string Property3 { get { return Property3 = Property1 + " " + Property2; } } Property1的制定者中,您只需要为Property2拨打NotifyPropertyChanged

答案 1 :(得分:1)

(2)是我通常这样做的方式。

那就是说,让我想知道是否有其他方法可以使用Rx框架来完成这类事情:http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx

所以这就是我想出来的(CAVEAT - 不要这样做!);)

public class ViewModel : INotifyPropertyChanged
{
    public ViewModel()
    {
        var o1 =
            Observable.FromEvent<PropertyChangedEventArgs>(this, "PropertyChanged");
        o1.Subscribe(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 != "Result");
        var o5 = Observable.CombineLatest(o2, o3, (e1, e2) => DoStuff()).TakeUntil(o4);
        o5.Subscribe(o => Debug.WriteLine("Got Prop1 and Prop2"));
    }

    public string DoStuff()
    {
        return Result = string.Concat(Property1, Property2);                
    }

    private string _property1;
    public string Property1
    {
        get { return _property1; }
        set
        {
            _property1 = value;
            OnNotifyPropertyChanged("Property1");
        }
    }
    private string _property2;
    public string Property2
    {
        get { return _property2; }
        set
        {
            _property2 = value;
            OnNotifyPropertyChanged("Property2");
        }
    }
    private string _result;
    public string Result
    {
        get { return _result; }
        set
        {
            _result = value;
            OnNotifyPropertyChanged("Result");
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnNotifyPropertyChanged(string name)
    {
        var handler = PropertyChanged;
        if(handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

答案 2 :(得分:0)

是的@Thomas是对的。方法2是进入WPF环境的最佳方式。如果已将ListBox.SelectedValue两个绑定添加到Property1

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

相关问题