【问题标题】:How do you handle 'SelectedItemChanged' events in a MVVM ViewModel?您如何处理 MVVM ViewModel 中的“SelectedItemChanged”事件?
【发布时间】:2010-03-13 22:31:36
【问题描述】:

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

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) 从属性设置器调用方法

这种做法看起来最“纯粹”,但也显得丑陋,仿佛隐藏了逻辑。它看起来像这样:

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

3) 挂钩 PropertyChanged 事件

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

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

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

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

【问题讨论】:

    标签: c# wpf silverlight mvvm


    【解决方案1】:

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

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

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

    这样,在Property1Property2 的设置器中,您只需为Property3 调用NotifyPropertyChanged

    【讨论】:

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

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

    【讨论】:

    • 有趣。我不会那样做的;-)
    【解决方案3】:

    是的@Thomas 是对的。方法 2 是进入 WPF 环境的完美方式。如果您已将 ListBox.SelectedValue TwoWay 绑定添加到 Property1

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-09-18
      • 2014-01-23
      • 1970-01-01
      • 2013-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多