简体   繁体   English

WPF中的视图和视图模型

[英]View and View-Model in WPF

I've been messing around with MVVM in WPF and have a quick question for folks. 我一直在WPF中弄乱MVVM,并向人们提出了一个快速问题。 Right now I have: 现在我有:

  • MainWindow which consists of a MenuBar and a UserControl 由MenuBar和UserControl组成的MainWindow
  • UserControl (mentioned above) contains basically a Grid. UserControl(上面提到的)基本上包含一个Grid。

I expose access to Grid Properties as I need in the UserControl, but the User Control knows nothing nor interacts with the MainWindow. 我在UserControl中根据需要公开了对Grid Properties的访问,但是User Control一无所知,也不与MainWindow交互。

I also have a class I call ViewModel which does manipulation of the MainWindow/UserControl for me. 我也有一个叫ViewModel的类,它为我做MainWindow / UserControl的操作。 My understanding is that the ViewModel knows about the View (MainWindow/UserControl) and how to manipulate it while the View generally knows nothing about the ViewModel. 我的理解是,ViewModel知道View(MainWindow / UserControl)以及如何操纵它,而View通常对ViewModel一无所知。

If I have that right, here is my question: 如果我有这项权利,这是我的问题:

  1. When I do button clicks on the MainWindow MenuBar I want to perform actions. 当我点击按钮时,我要执行MainWindow菜单栏上的操作。 Right now those actions are bound to say a EventHandler in the MainWindow and the EventHandler instantiates the ViewModel and calls the method for handling like such: 现在,这些动作势必会在MainWindow中说一个EventHandler,并且EventHandler实例化ViewModel并调用该方法进行如下处理:

     private void RunQueryMenuItemAdvClick(object pSender, RoutedEventArgs pRoutedEventArgs) { ViewModel vViewModel = new ViewModel(this); vViewModel.RunQuery(); } 

The View Model looks something like this: 视图模型如下所示:

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

Am I way off base with my approach here? 我在这里的方法会偏离基础吗?

EDIT New Solution: First, thanks to everyone for their response. 编辑新解决方案:首先,感谢大家的回应。 I'd like to provide my new solution. 我想提供我的新解决方案。 This may not be 100% MVVM, but it has to be at least 80% better than what I had! 这可能不是100%MVVM,但它必须比我的产品至少好80%!

My ViewModel: 我的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();
        }
    }

I've removed all of my code from behind the MainWindow and the UserControl and replaced it with XAML for Binding the elements that I needed to the two properties in ViewModel and the 1 Command. 我已经从MainWindow和UserControl的后面删除了所有代码,并用XAML替换了它,以便将所需的元素绑定到ViewModel和1 Command中的两个属性。 Feel free to provide additional feedback on what I may or may not have picked up on with the re-factoring. 随意提供关于我在重构中可能会或可能不会学到的更多信息。 (Aside from the lack of a Model usage). (除了缺乏Model用法)。

You are way off base here. 你是大错特错这里。

  1. It is the other way around: The View knows about the ViewModel and the ViewModel knows nothing about the View. 这是另一回事:View对ViewModel一无所知,而ViewModel对View一无所知。
    Having references to the MainWindow and the UserControl in your ViewModel is anything but MVVM. 在您的ViewModel中引用MainWindow和UserControl 只是 MVVM。

  2. When you use MVVM you normally don't have click handlers. 使用MVVM时,通常没有单击处理程序。

The correct way to handle this situation would be the following: 解决此情况的正确方法如下:

  • Expose an ICommand in your ViewModel as a property. 在ViewModel中将ICommand作为属性公开。 The MainWindow can bind its button to that command. MainWindow可以将其按钮绑定到该命令。
  • When the command is invoked inside the ViewModel, execute RunQuery , but you would simply set IsBusy on the ViewModel to true. 在ViewModel中调用该命令时,请执行RunQuery ,但您只需将ViewModel上的 IsBusy设置为true。 The UserControl in turn would bind to that property. 反过来,UserControl将绑定到该属性。

All of this works by setting the DataContext of the View to an instance of the ViewModel. 所有这些都通过将View的DataContext设置为ViewModel的实例来实现。

Daniel is correct that you seem to be misunderstanding of the MVVM design pattern, and that your ViewModels should never actually reference any UI objects. Daniel是正确的,您似乎对MVVM设计模式有误解,并且ViewModels绝对不应引用任何UI对象。

The best way I can think of to describe the pattern is your ViewModels are your actual application, your Models are your data objects, and your Views are simply a user-friendly way to let users interact with your ViewModels . 我能想到的最好的描述模式的方法是: ViewModels是您的实际应用程序, Models是您的数据对象,而Views只是一种用户友好的方式,可让用户与ViewModels交互。 In a perfect world, you can run your application using test scripts entirely and never use the View layer at all. 在理想的情况下,您可以完全使用测试脚本来运行应用程序,而根本不要使用View层。

For example, your ViewModel is your application, so it might have an List<ICommand> MenuCommands , with each ICommand being a RelayCommand or DelegateCommand that points to a method in your code, and a boolean IsBusy property. 例如,您的ViewModel是您的应用程序,因此它可能具有List<ICommand> MenuCommands ,每个ICommand是指向代码中的方法的RelayCommandDelegateCommand以及一个布尔值IsBusy属性。

Your View (Window) simply reflects your ViewModel by binding your <Menu> to the MenuCommands collection, and perhaps showing some loading graphic based on the IsBusy boolean. 通过将<Menu>绑定到MenuCommands集合,并且可能显示一些基于IsBusy布尔值的加载图形,您的View(窗口)仅反映了ViewModel。

I have a fairly basic MVVM example on my blog if you're interested in seeing a simple MVVM example from start to finish 如果您有兴趣从头到尾看到一个简单的MVVM示例,那么我的博客上有一个相当基本的MVVM示例

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM