gheff
gheff

Reputation: 193

WPF, MVVM and PRISM - No Parameterless Constructor defined for this object

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

Upvotes: 5

Views: 5445

Answers (3)

fullStackChris
fullStackChris

Reputation: 1502

In my case it was just a matter of correcting the falsely marked internal constructor to a public one. We typically have internal components and implementations in our UIs, and we just got carried away with also making the constructor internal, which led to the error. I.e. something like this is okay:

// okay to have internal, depending on how your project is setup
internal partial class MyControl : UserControl
{
    // constructor needs to be public to prevent "No parameterless constructor" error
    public SensitiveInfoControl()
    {
        InitializeComponent();
    }

    // ... implementation
}

Upvotes: 0

E-Bat
E-Bat

Reputation: 4892

Your view is trying to execute logic that only make sense at runtime, so you need to make sure that you are not in design mode:

public HeaderView()
{
    if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
    {
        var svc = ServiceLocator.Current;
        var eventAggregator = svc.GetInstance<IEventAggregator>();
        this.DataContext = new HeaderViewModel(eventAggregator);
     }
     InitializeComponent();
}

EDIT:

For support of designtime view model have a look here

Basically you need to provide a parameterless constructor for you ViewModel to support design mode.

Upvotes: 2

Neil
Neil

Reputation: 637

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.

Upvotes: 7

Related Questions