在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;
}
}
答案 0 :(得分:4)
以下是我在MVVM
中组织它的方式:
还记得当三层系统风靡一时吗?如果您认为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层。
由于您的视图模型将模型从模型中抽象出来(理想情况下),您将无法(并且不希望)将命令放在模型类上。处理呈现或交互的任何逻辑都不属于模型层。