MVVM View 与 ViewModel 绑定问题

MVVM View vs ViewModel binding issues

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

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

我遇到的问题是,当我创建 ViewModel 并传入参数时,会调用正确的构造函数。但是因为ViewModel是绑定到view上的,所以view会调用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,viewModel 就会使用 viewModel 的默认无参数构造函数再次创建。

任何人都可以解释为什么会发生这种情况,并阐明如何设置视图模型优先方法以使其正常工作吗?我很茫然,我一直在为此工作一整天。

谢谢, 蒂姆

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

删除这个

<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 将隐式查找与绑定到其 ContentViewModel 关联的 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>

并且您已将 ResourceDictionaryApplication.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>

框架将隐式查找 DataTemplate 数据类型 MyViewModel。框架将在我们指定的 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));
        }
    }
}