我一直在使用WPF中的MVVM并且对人们提出了一个快速的问题。现在我有:
我在UserControl中公开了对Grid Properties的访问,但是User Control不知道任何内容,也没有与MainWindow交互。
我还有一个我调用ViewModel的类,它为我操作MainWindow / UserControl。我的理解是ViewModel知道View(MainWindow / UserControl)以及如何操作它,而View通常对ViewModel一无所知。
如果我有这个权利,这是我的问题:
当我按下MainWindow MenuBar上的按钮时,我想执行操作。现在,这些操作必然会在MainWindow中说出一个EventHandler,并且EventHandler会实例化ViewModel并调用方法进行处理,如下所示:
private void RunQueryMenuItemAdvClick(object pSender, RoutedEventArgs pRoutedEventArgs)
{
ViewModel vViewModel = new ViewModel(this);
vViewModel.RunQuery();
}
View Model看起来像这样:
public class ViewModel
{
private DataProvider fDataProvider;
private MainWindow fMainWindow;
private BackgroundWorker fQueryWorker = new BackgroundWorker();
public ViewModel(MainWindow pMainWindow)
{
fDataProvider = new DataProvider();
fMainWindow = pMainWindow;
//Query Worker
fQueryWorker.DoWork += QueryWorkerDoWork;
fQueryWorker.RunWorkerCompleted += QueryWorkerCompleted;
}
private void QueryWorkerCompleted(object pSender, RunWorkerCompletedEventArgs pRunWorkerCompletedEventArgs)
{
fMainWindow.UserControl_Data.busyIndicator1.IsBusy = false;
fMainWindow.UserControl_Data.DataToPresent = pRunWorkerCompletedEventArgs.Result;
}
private void QueryWorkerDoWork(object pSender, DoWorkEventArgs pDoWorkEventArgs)
{
pDoWorkEventArgs.Result = this.fDataProvider.GetParticipantsData();
}
public void RunQuery()
{
if (!fQueryWorker.IsBusy)
{
fMainWindow.UserControl_Data.busyIndicator1.IsBusy = true;
fQueryWorker.RunWorkerAsync();
}
}
}
我的方法是否可以离开基地?
编辑新解决方案: 首先,感谢大家的回应。我想提供我的新解决方案。这可能不是100%的MVVM,但它必须至少比我的好80%!
我的ViewModel:
public class ViewModel : ObservableObject
{
private DataProvider fDataProvider;
private BackgroundWorker fQueryWorker = new BackgroundWorker();
public ViewModel()
{
fDataProvider = new DataProvider();
//Query Worker
fQueryWorker.DoWork += QueryWorkerDoWork;
fQueryWorker.RunWorkerCompleted += QueryWorkerCompleted;
}
//This is my Command for the MainWindow.MenuItem to bind to to run a query
RelayCommand fRunQueryCommand;
public ICommand RunQueryCommand
{
get
{
if (this.fRunQueryCommand == null)
{
this.fRunQueryCommand = new RelayCommand(param => this.RunQuery(),
param => true);
}
return this.fRunQueryCommand;
}
}
//This is my Property for the UserControl.progressBar to bind to
private bool fIsBusy;
public bool IsBusy
{
get { return this.fIsBusy; }
set
{
if (value != this.fIsBusy)
{
this.fIsBusy = value;
OnPropertyChanged("IsBusy");
}
}
}
//This is my Property for the UserControl.gridControl.ItemSource to bind to
private object fSource;
public object Source
{
get { return this.fSource; }
set
{
if (value != this.fSource)
{
this.fSource = value;
OnPropertyChanged("Source");
}
}
}
private void QueryWorkerCompleted(object pSender, RunWorkerCompletedEventArgs pRunWorkerCompletedEventArgs)
{
this.IsBusy = false;
Source = pRunWorkerCompletedEventArgs.Result;
}
private void QueryWorkerDoWork(object pSender, DoWorkEventArgs pDoWorkEventArgs)
{
pDoWorkEventArgs.Result = this.fDataProvider.GetParticipantsData();
}
public void RunQuery()
{
if (!fQueryWorker.IsBusy)
{
this.IsBusy = true;
fQueryWorker.RunWorkerAsync();
}
}
我已经从MainWindow和UserControl后面删除了所有代码,并将其替换为XAML,用于将我需要的元素绑定到ViewModel和1 Command中的两个属性。请随意提供有关我可能会或可能没有重新考虑因素的额外反馈。 (除了缺乏模型用法)。
答案 0 :(得分:5)
你方式离开这里。
反过来说:View了解ViewModel,而ViewModel对View一无所知。
在ViewModel中引用MainWindow和UserControl是除了 MVVM之外的任何东西。
使用MVVM时,通常没有点击处理程序。
处理这种情况的正确方法如下:
RunQuery
,但您只需将ViewModel 上的IsBusy
设置为true即可。反过来,UserControl将绑定到该属性。所有这一切都可以通过将View的DataContext
设置为ViewModel的实例来实现。
答案 1 :(得分:2)
Daniel是正确的,你似乎误解了MVVM设计模式,并且你的ViewModels
永远不应该真正引用任何UI对象。
我能想到的描述模式的最佳方式是ViewModels
是您的实际应用,Models
是您的数据对象,而您的Views
只是用户友好的让用户与您的ViewModels
互动的方式。在完美的世界中,您可以完全使用测试脚本运行应用程序,而根本不使用View层。
例如,您的ViewModel就是您的应用,因此它可能有一个List<ICommand> MenuCommands
,每个ICommand
都是RelayCommand
或DelegateCommand
,指向您的方法代码和布尔IsBusy
属性。
您的视图(窗口)只是通过将<Menu>
绑定到MenuCommands
集合来反映您的ViewModel,并且可能会显示一些基于IsBusy
布尔值的加载图形。
如果您有兴趣从头到尾看到一个简单的MVVM示例,我有一个公平basic MVVM example on my blog