0

我一直在 WPF 中搞乱 MVVM,并有一个快速的问题要问人们。现在我有:

  • MainWindow 由 MenuBar 和 UserControl 组成
  • UserControl(上面提到的)基本上包含一个网格。

我根据需要在 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();
    }
    

视图模型看起来像这样:

    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%!

我的视图模型:

    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 命令中的两个属性。随时提供关于我可能会或可能不会通过重构获得的额外反馈。(除了缺少模型用法)。

4

2 回答 2

5

你在这里离谱了。

  1. 反过来说:View 知道 ViewModel,而 ViewModel 对 View 一无所知。
    在 ViewModel 中引用 MainWindow 和 UserControl 绝不是MVVM

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

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

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

所有这些都通过将DataContextView 设置为 ViewModel 的实例来实现。

于 2012-09-12T16:29:41.347 回答
2

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

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

例如,您的 ViewModel 是您的应用程序,因此它可能有一个List<ICommand> MenuCommands, 每个ICommand都是一个RelayCommandor DelegateCommand,它指向您代码中的一个方法,以及一个布尔IsBusy属性。

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

如果您有兴趣从头到尾查看一个简单的 MVVM 示例,我的博客上有一个相当基本的 MVVM 示例

于 2012-09-12T16:35:51.527 回答