【问题标题】:MVVM View vs ViewModel binding issuesMVVM View 与 ViewModel 绑定问题
【发布时间】:2017-03-07 00:09:32
【问题描述】:

我在实例化 viewModel 时遇到了问题。

我在大多数情况下都使用 ViewModelLocator,因为在大多数情况下我必须注入依赖项。但是,在某些情况下,我需要将参数传递给 ViewModel。据我了解,我需要为此使用 ViweModel-First 方法。这意味着我需要为 ViewModel 创建一个在运行时绑定到 View 的 DataTemplate。确保包含一个带有我要传入的参数的构造函数。

我遇到的问题是,当我创建 ViewModel 并传入我的参数时,会调用正确的构造函数。但是由于 ViewModel 是绑定到视图上的,所以视图调用了视图模型的默认无参构造函数。

这是我将 ViewModel 绑定到的 UserControl 的 XAML 的样子:

<UserControl x:Class="MyView">
    <UserControl.DataContext>
      <viewModels:MyViewModel></viewModels:MyViewModel>
    </UserControl.DataContext>
</UserControl>

数据模板如下所示:

<DataTemplate DataType="{x:Type viewModels:MyViewModel}">
   <views:MyView></views:MyView>
</DataTemplate>

这是一个示例 ViewModel:

public class MyViewModel : ViewModelBase
{
  private MyModel _myModel;

  public MyViewModel()
  {
  }

  public MyViewModel(MyModel myModel)
  {
    _myModel = myModel;
  }
}

一旦我通过代码使用正确的构造函数来传递参数创建了我的视图模型,视图模型就会再次由视图使用视图模型的默认无参数构造函数创建。

谁能解释为什么会发生这种情况,并阐明如何设置 viewmodel-first 方法以使其正常工作?我很茫然,我整天都在努力。

谢谢, 蒂姆

【问题讨论】:

    标签: mvvm data-binding viewmodellocator


    【解决方案1】:

    如果您从UserControl 中删除以下 sn-p 并按照我剩余的说明进行操作,我相信您会得到您想要的:

    删除此

    <UserControl.DataContext>
          <viewModels:MyViewModel></viewModels:MyViewModel>
    </UserControl.DataContext>
    

    现在,假设您有一个 UserControlWindow 绑定到您希望在您的 UserControlWindow 中表示的 ViewModel。这样做的方法是在UserControlWindow 中使用ContentControl,并在ResourceDictionary 中指定DataTemplate,如下所示:

    .XAML:

    <Window x:Class="WPF_Sandbox.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:WpfApplication1.ViewModels"
        Title="MainWindow"
        x:Name="ThisControl">
        <Window.DataContext>
            <vm:MainWindowViewModel/>
        </Window.DataContext>
        <DockPanel LastChildFill="True">
            <ContentControl DockPanel.Dock="Left" Content="{Binding NavigationRegion}"/>
            <ContentControl DockPanel.Dock="Left" Content="{Binding ContentRegion}"/>
        </DockPanel>    
    </Window>
    

    ContentControl 将隐式查找与绑定到其Content 属性的ViewModel 关联的DataTemplate(在ResourceDictionary 对象的层次结构中)。假设我们将MainWindowViewModel 中的ContentRegion 属性设置为MyViewModel 的一个实例,如下所示:

    MainWindowViewModel.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace WpfApplication1.ViewModels
    {
        public class MainWindowViewModel : ViewModel
        {
            private object _navigationRegion;
            private object _contentRegion;
    
            public object NavigationRegion
            {
                get
                {
                    return _navigationRegion;
                }
                set
                {
                    _navigationRegion = value;
                    OnPropertyChanged(nameof(NavigationRegion));
                }
            }
    
            public object ContentRegion
            {
                get
                {
                    return _contentRegion;
                }
                set
                {
                    _contentRegion = value;
                    OnPropertyChanged(nameof(ContentRegion));
                }
            }
    
            public MainWindowViewModel()
            {
                ContentRegion = new MyViewModel(new MyModel());
            }
        }
    }
    

    你有一个ResourceDictionary,像这样指定:

    MyResourceDictionary.xaml

    <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        xmlns:local="clr-namespace:WpfApplication1"
                        xmlns:vm="clr-namespace:WpfApplication1.ViewModels"
                        xmlns:views="clr-namespace:WpfApplication1.Views">
        <DataTemplate DataType="{x:Type vm:MyViewModel}">
            <views:MyView/>
        </DataTemplate>
    
    </ResourceDictionary>
    

    您已将您的ResourceDictionary 与您的Application.Resources 合并到您的 App.xaml 文件中,如下所示:

    <Application x:Class="WpfApplication1.App"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:local="clr-namespace:WpfApplication1"
                 StartupUri="MainWindow.xaml">
        <Application.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="MyResourceDictionary.xaml"/>
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </Application.Resources>
    </Application>
    

    框架将为数据类型MyViewModel 隐式查找DataTemplate。框架将在我们指定的ResourceDictionary 中找到MyViewModelDataTemplate,并看到它应该由MyView 用户控件表示。此时框架将呈现MyView 用户控件。

    为了子孙后代:

    ViewModel.cs

    using System.ComponentModel;
    
    namespace WpfApplication1.ViewModels
    {
        public abstract class ViewModel : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected string _title;
    
            protected string Title
            {
                get
                {
                    return _title;
                }
                set
                {
                    _title = value;
                    OnPropertyChanged(nameof(Title));
                }
            }
    
            protected void OnPropertyChanged(string propName)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
            }
        }
    }
    

    【讨论】:

    • 请显示一些代码,以便我提供帮助。仅凭您的评论尚不清楚问题是什么。
    • 我对造成的混乱表示歉意。我真的不能从办公室发帖。它被锁定了。基本上你回答了我的问题。有 2 个问题。首先,由于定位器用于实例化视图模型,因此使用定位器在视图中绑定上下文也再次称为 viemodel。第二个问题是在内容控件中返回和显示的用户控件中定义数据上下文。所以它被控件和代码调用。再次感谢。
    猜你喜欢
    • 2013-08-29
    • 2012-06-17
    • 1970-01-01
    • 1970-01-01
    • 2011-05-03
    • 1970-01-01
    • 2013-06-14
    • 2011-01-21
    • 2010-11-10
    相关资源
    最近更新 更多