【问题标题】:the right way for MVVM pattern implementationMVVM 模式实现的正确方法
【发布时间】:2013-09-07 18:36:59
【问题描述】:

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

视图模型:

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是否正确?如果是,有什么缺点?

【问题讨论】:

    标签: c# wpf mvvm


    【解决方案1】:

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

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

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

    因此,如果您将 VM 实例设置为 DataContext(例如 this.DataContext = new ViewModel())并将您的 contentpresenter Content 绑定到 View 的 DataContext,例如 Content={Binding},这将更正确,并且会在您有帮助时帮助您想扩展你的视野。这是一篇关于 mvvm 实现的不错的 msdn 文章http://msdn.microsoft.com/en-us/library/gg405484(v=pandp.40).aspx

    谢谢

    【讨论】:

    • 通过资源设置视图模型只是一个快速的示例,所以把它放在一边使用dataContext使用这种方法没有问题吗?
    • 视图模型之间的交互也应该更松散耦合,不使用直接引用,而是实现类似 Mediator 或 Event aggregator...
    【解决方案2】:

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

    关于缺点,我会避免在窗口资源中创建wpfApplication1:SomeVm。通常Window 中的DataContext 被设置为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。我知道您的示例代码可能已简化,但我正在对其进行评论。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-06
      • 1970-01-01
      • 2012-11-11
      • 2023-03-29
      • 2012-05-08
      • 1970-01-01
      • 2020-07-26
      • 1970-01-01
      相关资源
      最近更新 更多