MVVM模式实现的正确方法

时间:2013-09-07 18:36:59

标签: c# wpf mvvm

我正在尝试实现MVVM,所以我不知道以下是正确的。 看起来ViewModel是视图的某种模型,因此视图中的关联应该在ViewModel中显示,在这种情况下,ViewModel之间应该有一些关联。因此,通过为ViewModel类型创建一些模板,似乎应用程序可以工作,这里是一些示例代码:

的ViewModels:

public class SomeVm : INotifyPropertyChanged
{
    public SomeVm()
    {
        SomeOtherVm = new SomeOtherVm();
    }
    public INotifyPropertyChanged SomeOtherVm { set; get; }

    private int _a;
    public int A
    {
        set { 
            _a= value;
            B = value;
        }
        get { return _a; }
    }

    private int _b;
    public int B
    {
        set 
        {
            _b = value;
            OnPropertyChanged("B");
        }
        get { return _b; }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class SomeOtherVm : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private int _c;
    public int C
    {
        set
        {
            _c = value;
            D = value;
        }
        get { return _c; }
    }

    private int _d;
    public int D
    {
        set
        {
            _d = value;
            OnPropertyChanged("D");
        }
        get { return _d; }
    }
    protected virtual void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

观点:

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:wpfApplication1="clr-namespace:WpfApplication1"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        mc:Ignorable="d" 
        x:Class="WpfApplication1.MainWindow"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <wpfApplication1:SomeVm x:Key="SomeVm"/>
        <DataTemplate DataType="{x:Type wpfApplication1:SomeVm}">
            <StackPanel d:DesignWidth="339" d:DesignHeight="54">
                <TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding A}" VerticalAlignment="Stretch"/>
                <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding B}" VerticalAlignment="Stretch"/>
                <ContentPresenter Content="{Binding SomeOtherVm}"/>
            </StackPanel>

        </DataTemplate>
        <DataTemplate DataType="{x:Type wpfApplication1:SomeOtherVm}">
            <StackPanel d:DesignWidth="339" d:DesignHeight="54">
                <TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding C}" VerticalAlignment="Stretch"/>
                <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding D}" VerticalAlignment="Stretch"/>
            </StackPanel>
        </DataTemplate>

    </Window.Resources>
    <Grid>
        <ContentPresenter Content="{DynamicResource SomeVm}" />   
    </Grid>
</Window>

通过这种方式可以在一些资源字典中创建所有视图,所以问题是:这样使用MVVM是对的吗?如果是,那有什么缺点?

2 个答案:

答案 0 :(得分:1)

通常,ViewModel应该是整个视图的DataContext,即它应该是实体提供数据来查看以呈现自身并监听UI命令,事件和属性更改以与业务层(模型)交互的实体

您实现它的方式是将VM作为资源并将其设置为内容,而不是将DataContext设置为一个内容,并且对于您提到的场景可能效果很好。但是您应该将VM设置为整个视图的DataContext,以便视图中的所有元素都可以绑定到VM中的属性以呈现其状态。

在您的场景中,如果您必须再添加一个UI元素,除了ContentPresenter之外,您将再次访问资源VM。

因此,如果您将VM实例设置为DataContext(如this.DataContext = new ViewModel())并将您的contentpresenter内容绑定到视图的DataContext,例如Content = {Binding},这将更加正确并且如果您曾经帮助过您想要扩展你的观点。这是关于mvvm实现http://msdn.microsoft.com/en-us/library/gg405484(v=pandp.40).aspx

的一篇很好的msdn文章

由于

答案 1 :(得分:1)

ViewModel嵌套而言,这段代码一目了然。您在XAML中设置的绑定也是正确的。

关于缺点,我不会在窗口资源中创建wpfApplication1:SomeVm。通常DataContext的{​​{1}}设置为Window的实例,而WindowViewModel则会引用SomeVm。想象一下这样的课程:

public class WindowViewModel
{
    public SomeVM SomeVM{get; set;}
    public string Title {get; set;} //other data to bind by window
    //...
}

然后,在初始化窗口时,必须将DataContext设置为ViewModel实例,例如:

MainWindow.DataContext = new WindowViewModel();

在XAML中,您将再次使用绑定:

<Grid>
    <ContentPresenter Content="{Binding SomeVm}" />   
</Grid>

我还建议将隐式DataTemplates放在generic.xaml字典中,而不是放在窗口中。这样您就可以在整个应用程序中重用这些模板。

此外,使用实现常见事件处理的ViewModelBase类要好得多,这样您就不需要重新实现INotifyPropertyChanged。还要尽量避免在属性更改通知中使用“魔术字符串”。最好使用lambda based approach或新的Caller Info Attributes。我知道你的示例代码可能已经简化了,但我正在对它进行评论。