哪个图层应包含ICommand?

时间:2015-04-28 15:23:00

标签: c# wpf mvvm icommand

在WPF中,我有一个名为Malfunctions的ViewModel类,它有一个PartMalfunctions的ObservableCollection。通常,ObservableCollection中有10到15个PartMalfunction对象;有多少取决于超出此问题范围的其他参数。

我有一些xaml,它有一个绑定到这个ObservableCollection的DataGrid。在DataGrid中,我显示了PartMalfuction的各种属性(即 - 描述,名称等),我有一个用户可以单击的“开始计时器”按钮。 “开始计时器”按钮绑定到PartMalfunction Model类中的 ICommand StopwatchCmd (您可以在代码中看到以下所有内容)。

以下是我的问题:我是否在错误的图层中安装了StopwatchCmd(即 - 它是否属于Malfunctions ViewModel)?我真的很挣扎于此并试图弄明白我的问题拥有,但我一直打墙,可以这么说,因为Model类中的StopwatchCmd效果很好!我的意思是它能够在那里执行并执行它需要的任何业务规则,并与它触发的对象的实例进行交互。如果我将它粘贴在ViewModel中,那么我似乎必须经历更多工作才能让它完成它已经在做的事情。

请注意,我遗漏了Malfunctions ViewModel中的一些代码,因为它与此问题无关。以下是Malfunctions ViewModel的代码。

public class Malfunctions : ViewModelBase {
       public ObservableCollection<Model.PartMalfunction> AllPartMalfunctions {
            get;
            private set;
        }
}

PartMalfunction的Model类看起来像这样:

public class PartMalfunction : INotifyPropertyChanged {
    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName) {
        if (PropertyChanged != null) {
            PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }
    }

    private int _seconds;
    private string _stopwatchText = string.Empty;
    private bool _isStopwatchInProgress = false;
    System.Windows.Threading.DispatcherTimer _timer = new System.Windows.Threading.DispatcherTimer();
    RelayCommand _stopwatchCmd;

    public ICommand StopwatchCmd {
        get {
            if (_stopwatchCmd == null)
                _stopwatchCmd = new RelayCommand(param => this.StopwatchClick());
            return _stopwatchCmd;
        }
    }
    public bool IsStopwatchInProgress {
        get {
            return _isStopwatchInProgress;
        }
        set {
            _isStopwatchInProgress = value;
            OnPropertyChanged("IsStopwatchInProgress");
        }
    }
    public string StopwatchText {
        get {
            return _stopwatchText;
        }
        set {
            _stopwatchText = value;
            OnPropertyChanged("StopwatchText");
        }
    }
    private void StopwatchClick() {

        if (!this.IsStopwatchInProgress) {
            // Start the timer
            _seconds = 0;

            // Will immediately update the timer text to "00:00:00"
            this.StopwatchText = GetElapsed();

            _timer.Tick += DispatcherTimer_Tick;
            _timer.Interval = new TimeSpan(0, 0, 1); // Ticks every second
            _timer.Start();

            this.IsStopwatchInProgress = true;
        }
        else {
            // Stop the timer
            _timer.Stop();
            _timer.Tick -= DispatcherTimer_Tick;
            _seconds = 0;

            this.IsStopwatchInProgress = false;
        }
    }
    private void DispatcherTimer_Tick(object sender, System.EventArgs e) {
        _seconds += 1;

        this.StopwatchText = GetElapsed();
    }
    private string GetElapsed() {
        int hour = 0, min = 0, sec = 0;

        if (_seconds > 59) {
            min = (int)_seconds / 60;
            sec = _seconds % 60;

            if (min > 59) {
                hour = (int)min / 60;
                min = min % 60;
            }
        }
        else
            sec = _seconds;

        string elapsed = hour < 10 ? "0" + hour.ToString() : hour.ToString();
        elapsed += ":" + (min < 10 ? "0" + min.ToString() : min.ToString());
        elapsed += ":" + (sec < 10 ? "0" + sec.ToString() : sec.ToString());

        return elapsed;
    }
}

3 个答案:

答案 0 :(得分:4)

以下是我在MVVM中组织它的方式:

  • 模型:仅使用实体,消耗的类的部分。
  • 查看:Xaml和后面的代码可以显示/操作视图模型提供的模型。
  • ViewModel:此层是 business 逻辑所在的位置,并且存储从DB检索的数据(模型)。它是View和Models 之间的管道。它可以访问数据库,创建计时器,保存ICommands,....只要什么仅与业务逻辑相关。没有直接查看处理。

还记得当三层系统风靡一时吗?如果您认为MVVM可能有三层可能会有所帮助。 IMHO

  

哪个图层应包含ICommand?

viewmodel,因为它处理命令所执行的业务逻辑。将来,当一个人拿起代码来维护代码时,大多数开发人员都会首先看到它。

答案 1 :(得分:3)

这个问题可以被视为主要基于意见,但我相信它有助于减少开发人员理解模型 - 视图 - 视图模型边界的经验。

对于我来说,你认为Model实际上是一个ViewModel,更具体地说,父ViewModel(Malfunctions)有一个子集ViewModel(PartMalfunction)作为集合公开(ObservableCollection),意思是没有&#39;有关PartMalfunction类的ICommand属性的问题。

如果我发现一个Model类为显示(文本,日期等)做了很多格式的数据,那么它更可能是一个ViewModel,这种东西就是ViewModel的响应性。另外对我来说,Model类没有实现INotifyPropertyChanged接口,通知是使用事件(或Rx流)完成的,订阅者(ViewModel)可以选择更新UI的方式和时间。

答案 2 :(得分:2)

ICommand有两个有趣的成员。 Execute,定义另一个对象可以请求命令所有者执行的操作,CanExecute,定义另一个对象是否应该请求操作。这在ICommand属性的名称中非常明显,但如果仔细观察,它们会在ICommand中找到ViewModel层中的完美主页。您的视图模型可以将某些操作暴露给未知视图,并控制它们何时可以执行。由于命令通常是视图模型上的公共属性,因此您可以轻松地从WPF控件绑定它们,而无需任何紧密耦合。例如,Button可以绑定到命令来定义它的功能,而无需知道它的DataContext(视图模型)是什么类型。

如果您尝试将ICommand放在视图中,您可能会发现与方法相比它们会很麻烦,因为视图可以访问自己的方法。因此,ICommand不适合View层。

由于您的视图模型将模型从模型中抽象出来(理想情况下),您将无法(并且不希望)将命令放在模型类上。处理呈现或交互的任何逻辑都不属于模型层。