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>
现在,假设您有一个 UserControl
或 Window
绑定到您希望在 UserControl
或 Window
中表示的 ViewModel
。您这样做的方法是在 UserControl
或 Window
中使用 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>
框架将隐式查找 DataTemplate
数据类型 MyViewModel
。框架将在我们指定的 ResourceDictionary
中找到 MyViewModel
的 DataTemplate
,并发现它应该由 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));
}
}
}
我在实例化 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>
现在,假设您有一个 UserControl
或 Window
绑定到您希望在 UserControl
或 Window
中表示的 ViewModel
。您这样做的方法是在 UserControl
或 Window
中使用 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>
框架将隐式查找 DataTemplate
数据类型 MyViewModel
。框架将在我们指定的 ResourceDictionary
中找到 MyViewModel
的 DataTemplate
,并发现它应该由 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));
}
}
}