WPF、MVVM 和 PRISM - 没有为此对象定义无参数构造函数
WPF, MVVM and PRISM - No Parameterless Constructor defined for this object
回答
好的所以添加 E-Bat 给出的建议代码没有任何影响,直到我开始一个新项目并逐字复制所有代码。我只能假设 http://prismlibrary.com/ 上的 ViewModelLocator 中一定有一些后台代码没有更新以考虑无参数构造函数。希望这对遇到同样问题的其他人有所帮助
原问题
我已经使用棱镜建立了一个 MVVM 项目。我有一个 MainWindow.xaml 和 5 个视图;我正在使用的 ButtonsView、HeaderView、ProcessInputView、ProcessLogView 和 ProcessSelectionView,每个 View 都有一个关联的 ViewModel。
MainWindow.xaml
<Window x:Class="TransactionAutomationTool.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TransactionAutomationTool"
xmlns:views="clr-namespace:TransactionAutomationTool.Views"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="800">
<Grid>
<views:HeaderView x:Name="HeaderViewControl" Margin="20,21,0,0" />
<views:ProcessSelectionView x:Name="ProcessSelectionViewControl" Margin="20,119,0,0" />
<views:ProcessInputView x:Name="ProcessInputViewControl" Margin="20,280,0,0" />
<views:ProcessLogView x:Name="ProcessLogView" Margin="298,105,0,0" />
<views:ButtonsView x:Name="ButtonViewControl" Margin="0,513,0,0" />
</Grid>
MainWindowViewModel
public class MainWindowViewModel: BindableBase
{
public IEventAggregator _events;
private UserPrincipal userPrincipal;
public MainWindowViewModel(IEventAggregator events)
{
_events = events;
userPrincipal = UserPrincipal.Current;
_events.GetEvent<HeaderLoaded>().Subscribe(HeaderHasBeenLoaded);
}
private void HeaderHasBeenLoaded()
{
_events.GetEvent<UserNameUpdate>().Publish(string.Format("{0} {1}", userPrincipal.GivenName, userPrincipal.Surname));
}
}
当我尝试在设计模式下查看主窗口时,出现以下错误
Screenshot of MainWindow In design Mode
找不到此对象的无参数构造函数 - 这突出显示了 HeaderView 和 ButtonsView
HeaderViewModel 和 ButtonsViewModel 都将 IEventAggregator 作为其构造函数中的参数,而其余的 ViewModel 则不这样做。我假设这是错误的来源。
HeaderViewModel
public class HeaderViewModel: BindableBase
{
private string userName;
private string runTime;
public string UserName
{
get { return userName; }
set { SetProperty(ref userName, value); }
}
public string RunTime
{
get { return runTime; }
set { SetProperty(ref runTime, value); }
}
public HeaderViewModel(IEventAggregator events)
{
events.GetEvent<RunTimeUpdate>().Subscribe(RunTimeUpdated);
events.GetEvent<UserNameUpdate>().Subscribe(UserNameUpdated);
events.GetEvent<HeaderLoaded>().Publish();
}
private void RunTimeUpdated(string newRunTime)
{
RunTime = newRunTime;
}
private void UserNameUpdated(string userName)
{
UserName = userName;
}
}
那么,如果我需要订阅这些事件并因此需要将 IEventAggregator 传递到我的 ViewModel,我该如何解决这个错误?
我是否需要通过覆盖 ConfigureContainer 方法在 Bootstrap 中注册它?如果是这样,我不完全确定该怎么做。
Bootstrap
class Bootstraper: UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
}
}
应用程序构建成功并成功运行,但我只是在尝试在设计器中查看主窗口时收到此消息。
如有任何帮助,我们将不胜感激。
编辑
我所有的视图构造函数都只有 initalizeComponent 方法并且不带参数
您的视图正在尝试执行仅在运行时才有意义的逻辑,因此您需要确保您未处于设计模式:
public HeaderView()
{
if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
var svc = ServiceLocator.Current;
var eventAggregator = svc.GetInstance<IEventAggregator>();
this.DataContext = new HeaderViewModel(eventAggregator);
}
InitializeComponent();
}
编辑:
查看设计时视图模型的支持看看here
基本上你需要为你的ViewModel提供一个无参数的构造函数来支持设计模式。
标记为已接受的答案解决了异常,但没有回答有关原因的问题。此外,这种方法将使单元测试变得非常困难,因为您会将数据上下文设置为特定对象而不是传递依赖项。
你得到异常的原因是因为 HeaderView 没有被容器实例化(默认情况下它是 UnityContainer)。
您在设计时构建了整个 MainWindow,而不是单独的部分。在 MainWindow
中尝试以下操作
<Grid>
<Grid.RowDefinitions>
<RowDefinitions />
<RowDefinitions />
<RowDefinitions />
<RowDefinitions />
<RowDefinitions />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" prism.RegionManager.RegionName="Row0Region" />
<ContentControl Grid.Row="1" prism.RegionManager.RegionName="Row1Region" />
<ContentControl Grid.Row="2" prism.RegionManager.RegionName="Row2Region" />
<ContentControl Grid.Row="3" prism.RegionManager.RegionName="Row3Region" />
<ContentControl Grid.Row="4" prism.RegionManager.RegionName="Row4Region" />
</Grid>
然后您可以使用 View Discovery 或 View Injection。对于 View Discovery,您可以执行类似
的操作
this.RegionManager.RegisterViewWithRegion("Row0Region", HeaderView())
等等。
您可以在模块的初始化方法或其他地方用区域注册视图。由你决定在哪里做。您可以覆盖引导程序的 运行 方法。基础 运行 方法完成后,您可以注册您的视图。
当主要 window 显示时,将发现所有区域,并且 RegionManager
将使用已在每个区域注册的视图填充这些区域。
区域管理器将使用容器实例化视图。当容器构建每个视图时,它们的视图模型将自动连接起来。 IEventAggregator
也将提供给 HeaderView
的视图模型。
这篇文章是基于 prism 4 - https://www.codeproject.com/Articles/165376/A-Prism-Application-Checklist 但它讨论的是如何构建视图。
回答 好的所以添加 E-Bat 给出的建议代码没有任何影响,直到我开始一个新项目并逐字复制所有代码。我只能假设 http://prismlibrary.com/ 上的 ViewModelLocator 中一定有一些后台代码没有更新以考虑无参数构造函数。希望这对遇到同样问题的其他人有所帮助
原问题 我已经使用棱镜建立了一个 MVVM 项目。我有一个 MainWindow.xaml 和 5 个视图;我正在使用的 ButtonsView、HeaderView、ProcessInputView、ProcessLogView 和 ProcessSelectionView,每个 View 都有一个关联的 ViewModel。
MainWindow.xaml
<Window x:Class="TransactionAutomationTool.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TransactionAutomationTool"
xmlns:views="clr-namespace:TransactionAutomationTool.Views"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="800">
<Grid>
<views:HeaderView x:Name="HeaderViewControl" Margin="20,21,0,0" />
<views:ProcessSelectionView x:Name="ProcessSelectionViewControl" Margin="20,119,0,0" />
<views:ProcessInputView x:Name="ProcessInputViewControl" Margin="20,280,0,0" />
<views:ProcessLogView x:Name="ProcessLogView" Margin="298,105,0,0" />
<views:ButtonsView x:Name="ButtonViewControl" Margin="0,513,0,0" />
</Grid>
MainWindowViewModel
public class MainWindowViewModel: BindableBase
{
public IEventAggregator _events;
private UserPrincipal userPrincipal;
public MainWindowViewModel(IEventAggregator events)
{
_events = events;
userPrincipal = UserPrincipal.Current;
_events.GetEvent<HeaderLoaded>().Subscribe(HeaderHasBeenLoaded);
}
private void HeaderHasBeenLoaded()
{
_events.GetEvent<UserNameUpdate>().Publish(string.Format("{0} {1}", userPrincipal.GivenName, userPrincipal.Surname));
}
}
当我尝试在设计模式下查看主窗口时,出现以下错误 Screenshot of MainWindow In design Mode
找不到此对象的无参数构造函数 - 这突出显示了 HeaderView 和 ButtonsView
HeaderViewModel 和 ButtonsViewModel 都将 IEventAggregator 作为其构造函数中的参数,而其余的 ViewModel 则不这样做。我假设这是错误的来源。
HeaderViewModel
public class HeaderViewModel: BindableBase
{
private string userName;
private string runTime;
public string UserName
{
get { return userName; }
set { SetProperty(ref userName, value); }
}
public string RunTime
{
get { return runTime; }
set { SetProperty(ref runTime, value); }
}
public HeaderViewModel(IEventAggregator events)
{
events.GetEvent<RunTimeUpdate>().Subscribe(RunTimeUpdated);
events.GetEvent<UserNameUpdate>().Subscribe(UserNameUpdated);
events.GetEvent<HeaderLoaded>().Publish();
}
private void RunTimeUpdated(string newRunTime)
{
RunTime = newRunTime;
}
private void UserNameUpdated(string userName)
{
UserName = userName;
}
}
那么,如果我需要订阅这些事件并因此需要将 IEventAggregator 传递到我的 ViewModel,我该如何解决这个错误?
我是否需要通过覆盖 ConfigureContainer 方法在 Bootstrap 中注册它?如果是这样,我不完全确定该怎么做。
Bootstrap
class Bootstraper: UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
}
}
应用程序构建成功并成功运行,但我只是在尝试在设计器中查看主窗口时收到此消息。
如有任何帮助,我们将不胜感激。 编辑 我所有的视图构造函数都只有 initalizeComponent 方法并且不带参数
您的视图正在尝试执行仅在运行时才有意义的逻辑,因此您需要确保您未处于设计模式:
public HeaderView()
{
if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
var svc = ServiceLocator.Current;
var eventAggregator = svc.GetInstance<IEventAggregator>();
this.DataContext = new HeaderViewModel(eventAggregator);
}
InitializeComponent();
}
编辑:
查看设计时视图模型的支持看看here
基本上你需要为你的ViewModel提供一个无参数的构造函数来支持设计模式。
标记为已接受的答案解决了异常,但没有回答有关原因的问题。此外,这种方法将使单元测试变得非常困难,因为您会将数据上下文设置为特定对象而不是传递依赖项。
你得到异常的原因是因为 HeaderView 没有被容器实例化(默认情况下它是 UnityContainer)。
您在设计时构建了整个 MainWindow,而不是单独的部分。在 MainWindow
中尝试以下操作<Grid>
<Grid.RowDefinitions>
<RowDefinitions />
<RowDefinitions />
<RowDefinitions />
<RowDefinitions />
<RowDefinitions />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" prism.RegionManager.RegionName="Row0Region" />
<ContentControl Grid.Row="1" prism.RegionManager.RegionName="Row1Region" />
<ContentControl Grid.Row="2" prism.RegionManager.RegionName="Row2Region" />
<ContentControl Grid.Row="3" prism.RegionManager.RegionName="Row3Region" />
<ContentControl Grid.Row="4" prism.RegionManager.RegionName="Row4Region" />
</Grid>
然后您可以使用 View Discovery 或 View Injection。对于 View Discovery,您可以执行类似
的操作this.RegionManager.RegisterViewWithRegion("Row0Region", HeaderView())
等等。
您可以在模块的初始化方法或其他地方用区域注册视图。由你决定在哪里做。您可以覆盖引导程序的 运行 方法。基础 运行 方法完成后,您可以注册您的视图。
当主要 window 显示时,将发现所有区域,并且 RegionManager
将使用已在每个区域注册的视图填充这些区域。
区域管理器将使用容器实例化视图。当容器构建每个视图时,它们的视图模型将自动连接起来。 IEventAggregator
也将提供给 HeaderView
的视图模型。
这篇文章是基于 prism 4 - https://www.codeproject.com/Articles/165376/A-Prism-Application-Checklist 但它讨论的是如何构建视图。