Answer OK so adding the suggested code given by E-Bat didn't have any affect until I started a new project and copied all the code across verbatim. I can only assume there must be some background code within the ViewModelLocator on http://prismlibrary.com/ which did not update to take the parameterless constructor into account. Hope this helps anyone else with the same issue
Original Question I have set up a MVVM project using prism. I have a MainWindow.xaml and 5 Views; ButtonsView, HeaderView, ProcessInputView, ProcessLogView and ProcessSelectionView which I am using, each View has an associated 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));
}
}
When I try to view the MainWindow in design mode I get the following Error Screenshot of MainWindow In design Mode
No Parameterless constructor found for this object - This Highlights the HeaderView and the ButtonsView
Both the HeaderViewModel and ButtonsViewModel take IEventAggregator as a parameter within their constructor where as the rest of the ViewModels do not. I am assuming this is where the errors are coming from.
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;
}
}
So how can I get round this error if I need to subscribe to these events and hence need the IEventAggregator passed into my ViewModels?
Do I need to register this within the Bootstrap via an override of the ConfigureContainer method? If so I'm not entirely sure how to do this.
Bootstrap
class Bootstraper: UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
}
}
The application builds successfully and runs successfully but it is just when trying to view the MainWindow in the designer that I get this message.
Any help would be greatly appreciated. EDIT All my view constructors just have the initalizeComponent methods and take no parameters
The answer marked as accepted addresses the exception, but doesn't answer the question about why. Also, that approach will make unit testing quite difficult as you will be setting the datacontext to a specific object instead of passing in a dependency.
The reason you are getting the exception is because the HeaderView is not being instantiated by the container(by default it's UnityContainer).
You are constructing the entirety of MainWindow at design time instead of individual pieces. Try the following in 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>
Then you can either use View Discovery or View Injection. For View Discovery, you can do something like
this.RegionManager.RegisterViewWithRegion("Row0Region", HeaderView())
and so on for rest of the views.
You can register views with regions in initialize method of modules or somewhere else. Upto you as to where you do that. You could override the Run method of the bootstrapper. Once the base run method has finished, you can register your views.
When the main window is displayed, all the regions will be discovered and RegionManager
will populate the regions with the views that have been registered with each of the regions.
The regionmanager will instantiate the views using the container. When the container constructs each of the views, their viewmodels will be auto wired up. Also the IEventAggregator
will be provided to the HeaderView
's viewmodel.
This article is based on prism 4 - https://www.codeproject.com/Articles/165376/A-Prism-Application-Checklist but it talks about how to construct views.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With