正确使用MVVM模式

时间:2015-03-16 08:08:08

标签: c# wpf mvvm

我希望这个问题不是一般的。

我习惯在MVVM-Pattern中实现我的WPF-Applications。所以我总是为View创建一个文件夹,一个用于ViewModel,一个用于Model(以及一些其他文件夹用于行为,转换器......)

首先我将MainWindowView.xaml放入View - 文件夹。然后我将MainWindowViewModel.cs添加到ViewModel - 文件夹中。在此之后,我在MainWindowModel.cs - 文件夹中添加了Model - 文件。

MainWindowModel.cs - 文件是我有问题的地方。

我习惯将这个类用于我的业务逻辑,比如从数据库加载数据或解析xml文件并将结果放入集合中。

例如,如果我想在Button-Click上从XML文件加载数据,我会用以下内容加载:

MainWindowView

<Window x:Class="MVVMExample.View.MainWindowView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:viewModel="clr-namespace:MVVMExample.ViewModel"
        Title="MainWindowView" Height="300" Width="300">
    <Window.DataContext>
        <viewModel:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
        <Button Command="{Binding LoadDataCommand}" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10" Content="Load data" Width="120"/>
    </Grid>
</Window>

MainWindowViewModel

internal class MainWindowViewModel : ViewModelBase
{
    private readonly MainWindowModel mainWindowModel;

    public MainWindowViewModel()
    {
        mainWindowModel = new MainWindowModel();
        mainWindowModel.XmlEntriesLoaded += XmlEntriesLoaded;
        LoadDataCommand = new RelayCommand(LoadData);
    }

    private void XmlEntriesLoaded(object sender, GenericEventArgs<List<XmlEntry>> e)
    {
        List<XmlEntry> entries = e.Value;
        // Display entries in ObservableCollection
    }

    private void LoadData(object parameter)
    {
        mainWindowModel.LoadData();
    }

    private ICommand loadDataCommand;
    public ICommand LoadDataCommand
    {
        get { return loadDataCommand; }
        set
        {
            loadDataCommand = value;
            OnPropertyChanged();
        }
    }
}

MainWindowModel

internal class MainWindowModel
{
    public EventHandler<GenericEventArgs<List<XmlEntry>>> XmlEntriesLoaded;

    public void LoadData()
    {
        BackgroundWorker backgroundWorker = new BackgroundWorker();
        backgroundWorker.DoWork += (s, e) =>
        {
            // Do load the data here and assign the result to e.Result
        };
        backgroundWorker.RunWorkerCompleted += (s, e) =>
        {
            EventHandler<GenericEventArgs<List<XmlEntry>>> temp = XmlEntriesLoaded;
            if (temp != null)
            {
                temp(this, new GenericEventArgs<List<XmlEntry>>((List<XmlEntry>) e.Result));
            }
        };
        backgroundWorker.RunWorkerAsync();
    }
}

班级XmlEntry也位于Model - 文件夹中。

现在有人告诉我,模型层只包含业务对象,没有我在MainWindowModel中做过的逻辑。加载数据的交互应该位于ViewModel。那是不是很严格?所以我做错了?

3 个答案:

答案 0 :(得分:3)

我之前就此有过一些争论。您的模型不仅仅是一个愚蠢的数据对象,它应该包含与 ViewModel 类似的行为。

考虑一下这个例子:

class Camera
    {
        BitmapImage CurrentFrame { get; set; }
        BitmapImage CapturedFrame { get; set; }
        VideoCaptureDevice CaptureDevice { get; set; }

        void TakePhoto();
        void ClearFrame();
        void Reset();
    }

以上只是一些没有实现的示例代码,但您明白了。

这里的要点是TakePhotoReset等方法如果包含在模型中而不是 ViewModel 。这是一个好主意的原因之一是因为它确保您的模型可以被多个ViewModel使用,而不必在多个ViewModel中实现逻辑,因为逻辑包含在模型中。

也就是说,在某些情况下,模型没有任何行为,只能作为数据对象。

回答你的问题:

模型只知道自己,因此在您的方案中,加载模型的责任是 ViewModel 。将加载方法放在ViewModel中是正确的。

更进一步,如果您的模型逻辑加载包含在 Service 类中,那么您的ViewModel可以引用该服务以加载相应的楷模。这将允许多个ViewModel引用服务并减少代码重复。

答案 1 :(得分:1)

我不会说你这样做错误本身。通常我的Model类是非常基本的东西,它只定义了我的程序所需的对象。 ViewModel包含一组由View绑定的方法和属性,它们使用Model类。 “普通”MVVM模式将具有1个View to 1 ViewModel,然后是许多定义业务对象的Model类,然后是支持ViewModel中定义的操作所需的任何补充类。

我通常将你的'LoadData'方法放入它自己的类中,称为'DataService',它将负责程序所需的任何外部数据操作。它与您的Model类不同,它只是定义对象,但不一定属于您的ViewModel严格定义的范围。

答案 2 :(得分:1)

Model - ViewModel - View之间的关系不是1:1:1。 它更像是M:N:O。 因此,多个Views可以使用一个ViewModel,而一个ViewModel可以使用多个Models

通常Model包含类定义数据结构及其关系,如实体框架类。我通常为Model类提供单独的VS项目。所以例如如果您的应用使用3个数据库,则VS解决方案中可以有3个模型。模型类名称应遵循应用程序的UML类模型或数据库模型,并使用适当的名称。 MainWindowModel不是模型类的好名字 - 它表明它属于MainWindow,这是错误的 - 它应该包含通常可用的类。

因此,如果LoadData()MainWindowModel的代码通常有用且会被多个ViewModels或其他组件使用,那么您应该将其放入Model在一个名为例如的类中CustomerXMLData但也可以使ViewModel根本不使用Model。即您不必为每个XXXWindowModel创建ViewModel类 - 这不是MVVM的重点。重点是模块化代码的可重用性和良好的设计,没有重复的代码。