WPF中的视图和视图模型

时间:2012-09-12 16:10:53

标签: c# wpf mvvm viewmodel backgroundworker

我一直在使用WPF中的MVVM并且对人们提出了一个快速的问题。现在我有:

  • MainWindow,包含MenuBar和UserControl
  • UserControl(如上所述)基本上包含一个Grid。

我在UserControl中公开了对Grid Properties的访问,但是User Control不知道任何内容,也没有与MainWindow交互。

我还有一个我调用ViewModel的类,它为我操作MainWindow / UserControl。我的理解是ViewModel知道View(MainWindow / UserControl)以及如何操作它,而View通常对ViewModel一无所知。

如果我有这个权利,这是我的问题:

  1. 当我按下MainWindow MenuBar上的按钮时,我想执行操作。现在,这些操作必然会在MainWindow中说出一个EventHandler,并且EventHandler会实例化ViewModel并调用方法进行处理,如下所示:

    private void RunQueryMenuItemAdvClick(object pSender, RoutedEventArgs pRoutedEventArgs)
    {
        ViewModel vViewModel = new ViewModel(this);
        vViewModel.RunQuery();
    }
    
  2. 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中的两个属性。请随意提供有关我可能会或可能没有重新考虑因素的额外反馈。 (除了缺乏模型用法)。

2 个答案:

答案 0 :(得分:5)

方式离开这里。

  1. 反过来说:View了解ViewModel,而ViewModel对View一无所知。
    在ViewModel中引用MainWindow和UserControl是除了 MVVM之外的任何东西。

  2. 使用MVVM时,通常没有点击处理程序。

  3. 处理这种情况的正确方法如下:

    • 在ViewModel中将ICommand公开为属性。 MainWindow可以将其按钮绑定到该命令。
    • 在ViewModel中调用该命令时,请执行RunQuery,但您只需将ViewModel 上的IsBusy 设置为true即可。反过来,UserControl将绑定到该属性。

    所有这一切都可以通过将View的DataContext设置为ViewModel的实例来实现。

答案 1 :(得分:2)

Daniel是正确的,你似乎误解了MVVM设计模式,并且你的ViewModels永远不应该真正引用任何UI对象。

我能想到的描述模式的最佳方式是ViewModels是您的实际应用,Models是您的数据对象,而您的Views只是用户友好的让用户与您的ViewModels互动的方式。在完美的世界中,您可以完全使用测试脚本运行应用程序,而根本不使用View层。

例如,您的ViewModel就是您的应用,因此它可能有一个List<ICommand> MenuCommands,每个ICommand都是RelayCommandDelegateCommand,指向您的方法代码和布尔IsBusy属性。

您的视图(窗口)只是通过将<Menu>绑定到MenuCommands集合来反映您的ViewModel,并且可能会显示一些基于IsBusy布尔值的加载图形。

如果您有兴趣从头到尾看到一个简单的MVVM示例,我有一个公平basic MVVM example on my blog