Prism Dialog: Unable to resolve Resolution root Object - c#

I tried this with Prism 8.0.0.1909 and dotnet core 3.1 and 5.
For my dialog I have a view:
<UserControl
x:Class="xzy.Modules.CapillaryBatchwise.Dialogs.NewBatchDialog"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
...
</UserControl>
and a View Model with nothing really in it for now:
namespace zxy.Modules.CapillaryBatchwise.ViewModels
{
public class NewBatchDialogViewModel : BindableBase, IDialogAware
{
...
public string Title => "MyTitle";
public event Action<IDialogResult> RequestClose;
public bool CanCloseDialog() => true;
public void OnDialogClosed()
{ }
public void OnDialogOpened(IDialogParameters parameters)
{ }
}
}
I registered the View and View Model in my App.xaml.cs
namespace xyz.CapillaryJournal
{
public partial class App
{
...
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterDialog<NewBatchDialog, NewBatchDialogViewModel>();
}
}}
And then call it from my actual ViewModel
public class CapillaryBatchNavigationViewModel : BindableBase
{
private readonly IDialogService dialogService;
public CapillaryBatchNavigationViewModel(//...
IDialogService dialogService)
{
///...
ShowNewBatchDialogCommand = new DelegateCommand(ShowNewBatchDialog);
this.dialogService = dialogService;
//...
}
public DelegateCommand ShowNewBatchDialogCommand { get; }
private void ShowNewBatchDialog()
{
dialogService.ShowDialog(nameof(ShowNewBatchDialog));
}
//...
}
However when I call ShowNewBatchDialogCommand from my View I get this exception, that I can't make any sense of:
Prism.Ioc.ContainerResolutionException: 'An unexpected error occurred while resolving 'System.Object', with the service name 'ShowNewBatchDialog''
Inner Exception
ContainerException: code: Error.UnableToResolveFromRegisteredServices;
message: Unable to resolve Resolution root Object {ServiceKey="ShowNewBatchDialog"}
from container without scope
with Rules with {TrackingDisposableTransients, UseDynamicRegistrationsAsFallbackOnly, FuncAndLazyWithoutRegistration, SelectLastRegisteredFactory} and without {ThrowOnRegisteringDisposableTransient, UseFastExpressionCompilerIfPlatformSupported}
with FactorySelector=SelectLastRegisteredFactory
with Made={FactoryMethod=ConstructorWithResolvableArguments}
with normal and dynamic registrations:
("NewBatchDialog", {FactoryID=160, ImplType=xyy.Modules.CapillaryBatchweise.Dialogs.NewBatchDialog, Reuse=TransientReuse}) ("TaskPresenter", {FactoryID=177, ImplType=xyy.Modules.CapillaryBatchweise.Views.TaskPresenter, Reuse=TransientReuse})
This is basically slightly modified what was done in this doc: https://prismlibrary.com/docs/wpf/dialog-service.html
I can't spot what is wrong with my code.

You should call dialogService.ShowDialog(nameof(NewBatchDialog)); since the name of the view's NewBatchDialog while ShowNewBatchDialog is the name of some unrelated method.
Or you can register the view with a specific name like containerRegistry.RegisterDialog<NewBatchDialog, NewBatchDialogViewModel>( "ShowNewBatchDialog" );...

Related

Prism with Unity wrong instance gets passed into constructor

I am trying to understand/learn Prism with Unity
I created following classes:
==========================================
Seperate Assembly containing a "Module":
using GlobalContracts;
using Prism.Ioc;
using Prism.Modularity;
namespace ModuleA
{
[Module(ModuleName = MyModuleA.NAME, OnDemand = true)]
public class MyModuleA : IModule
{
public const string NAME = "MyModuleA";
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register<MyControlA>();
containerRegistry.Register<IView, MyControlA>(NAME);
containerRegistry.Register<PluginViewModelBase, MyControlViewModel>(NAME);
}
public void OnInitialized(IContainerProvider containerProvider)
{
}
}
}
==========================================
A ViewModel
using GlobalContracts;
namespace ModuleA
{
public class MyControlViewModel : PluginViewModelBase
{
public MyControlViewModel(IView view) : base(view)
{
}
}
}
==========================================
The Host Application (other assembly):
public partial class App : PrismApplication
{
private Shell mShell;
private ShellViewModel mShellViewModel;
protected override IModuleCatalog CreateModuleCatalog()
{
return new DirectoryModuleCatalog(){ModulePath = #"..\..\..\..\ModulesOutput"};
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register<IView, Shell>();
containerRegistry.Register<IViewModel, ShellViewModel>();
}
protected override Window CreateShell()
{
mShellViewModel = Container.Resolve<ShellViewModel>();
mShell = (Shell)mShellViewModel.View;
return mShell;
}
(...)
Now my question is:
How do I tell Prism to resolve the IView-Parameter passed to the
constructor of the ViewModel properly?
It resolves it as "Shell" and not as "MyControlA".
Further tips regarding my code are welcome
I found some sources in the web but they used "RegisterType" method of a container. And for now I do not have dependencies to Unity in my ModuleA and I would not know how to get the container to call the "RegisterType". All sources are outdated in the web..
By default, it resolves the default registration, which in your case is Shell.
Registering a type with a name does not mean that that name is automatically used to resolve dependencies. You have to do that manually, with parameter override, injection factory or the like. But I'd try to avoid that as it makes things a bit fragile and tedious.

View is not opening after implementing new interface in viewModel class in MVVMCross

I am using MVVMCross for creating an android app, I have a viewModel class which is working fine. After I have implemented a new interface into this the view is not opening.
The interface and viewmodel is given below.
Viewmodel:
public sealed class PuCreationViewModel : BaseDataScreenViewModel
{
private readonly IProjectPuManager _puManager;
public PuCreationViewModel(
IProjectPuManager puManager )
{
_puManager = puManager;
}
}
Interface:
public interface IProjectPuManager
{
string CreatePu(string projectId, PuEntity entity);
}
Implementation class:
public class ProjectPuManager : IProjectPuManager
{
private readonly IFirebaseRepository<PuEntity> _puRepository;
public ProjectPuManager(IFirebaseRepository<ProjectPuEntity> puRepository)
{
_puRepository = puRepository;
}
public string CreatePu(string projectId, PuEntity entity)
{
_puRepository.CreateReference(
$"{AppConstants.Firebase.Key.ProjectPi.Root}/{projectId}");
return _puRepository.Create(entity, true);
}
}
Registered the interface in app.cs
Mvx.RegisterType<IProjectPuManager, ProjectPuManager>();
In my case I missed create the the mapper file for converting the entities. I thought the mapper is not needed to run the app but the mapper is mandatory for the manager. Without that the app doesn't open and it won't show any error to you.

How is this viewModel being created?

I have a simple application here but I am unsure of how my ViewModel is being created. I am assuming it's from the unity container but I am unsure and curious. The module initializes and registers the view with the region. The view's code behind has the ViewModel initialized in it's constructor and the ViewModel calls some services that were previously registered.
My question is how is the ViewModel created in the View's code behind when I've never registered the ViewModel type with the unity container? Is there some magic happening in the RegisterViewWithRegion method?
AlarmsModule.cs: This simply registers the view with the region
[Module(ModuleName = "AlarmsModule")]
public class AlarmsModule : IModule
{
[Dependency]
public IRegionManager regionManager { get; set; }
public void Initialize()
{
regionManager.RegisterViewWithRegion("AlarmsRegion", typeof(AlarmPanel.View));
}
}
View.xaml.cs:
public partial class View : UserControl
{
public View(ViewModel vm)
{
InitializeComponent();
DataContext = vm;
}
}
ViewModel.cs
public class ViewModel: DependencyObject
{
IEventAggregator _eventAggregator;
public ObservableCollection<IAlarmContainer> AlarmList { get; set; }
IAlarmService _alarmService;
public ViewModel(IAlarmService alarmService)
{
//Adding an alarm from the alarm service, which is injected into this viewModel
AlarmList = alarmService.AlarmList;
}
}
The view model is created by the unity container in the DoGetInstance method of the UnityServiceLocatorAdapter class in the Prism.Unity assembly which is in turn called by the RegisterViewWithRegion method through some other methods of the RegionViewRegistry class.
Unity is able to resolve the view model type automatically provided that it has a default parameterless constructor.
You could verify this yourself using the following code:
var view = unityContainer.Resolve(typeof(View), null); //will automatically resolve the view model type and inject the view with an instance of it

Prism for Xamarin, dependency failed for INavigationService

I am working on a sample Xamarin.Forms App for iOS, using PRISM.
But when I tried to use INavigationService in ViewModel, it is failed saying dependency failed for interface INavigationService
I dont know what wrong I am doing, how can we get around it or is it a bug in INavigationServices injection.
Thanks
Divikiran Ravela
Heres the code
public App()
{
// The root page of your application
Prism = new PrismBootStrap();
Prism.Run(this);
}
public class PrismBootStrap : UnityBootstrapper
{
protected override Page CreateMainPage()
{
return new NavigationPage(Container.Resolve<HomePage>());
}
protected override void RegisterTypes()
{
Container.RegisterTypeForNavigation<HomePage, HomePageViewModel>();
}
}
public class HomePage : ContentPage
{
public HomePageViewModel ViewModel { get; set; }
public HomePage()
{
ViewModel = App.Prism.Container.Resolve<HomePageViewModel>();
Content = new StackLayout
{
Children = {
new Label { Text = "Hello ContentPage" }
}
};
}
}
public class HomePageViewModel : BindableBase
{
private readonly INavigationService _navigationService;
public HomePageViewModel(INavigationService navigationService)
{
_navigationService = navigationService; //Fails here
}
}
The INavigationService depends on the use of the ViewModelLocator. If you don't use it, then the service cannot be injected. Instead of manually resolving your VM, use the ViewModelLocator.AutowireViewModel attached property. Besides, you don't want to be using your container like that. You only want one composition root when using DI.
Also, I recommend using the latest 6.1 preview. It has a ton of navigation improvements.

Constructor Injection of an ObjectContext using Unity/Prism/MVVM

I develop an application using WPF with MVVM pattern and Prism. The views are added to the ModuleCatalog and the viewmodels are registered to a unity container. For that I'm using a Bootstrapper which is responsible creating the shell, configuring the unity container and the module catalog.
The question is now, how injecting my EntityContext to the several viewmodels.
First the Bootstrapper:
public class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
Shell shell = Container.Resolve();
shell.Show();
return shell;
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.RegisterType<EntityContext >("Context");
Container.RegisterType<PersonViewModel>(new InjectionConstructor(
new ResolvedParameter<EntityContext >("Context")));
}
protected override IModuleCatalog GetModuleCatalog()
{
ModuleCatalog catalog = new ModuleCatalog();
catalog.AddModule(typeof(PersonModule));
return catalog;
}
The viewmodel looks like that (excerpt)
public class PersonViewModel : ViewModelBase, IDataErrorInfo
{
private Person _person;
private PersonRepository _repository;
readonly EntityContext _context;
public PersonViewModel(EntityContext context)
{
_context = context;
_person = new Person();
_repository = new PersonRepository(context);
}
The module:
public class PersonModule : IModule
{
private readonly IRegionManager regionManager;
public PersonModule(IRegionManager regionManager)
{
this.regionManager = regionManager;
}
public void Initialize()
{
regionManager.RegisterViewWithRegion("PersonData", typeof(PersonView));
}
}
The view code-behind:
public partial class PersonView : UserControl
{
private PersonViewModel _vm;
public PersonView()
{
InitializeComponent();
}
[Dependency]
public PersonViewModel VM
{
get
{
return this.DataContext as PersonViewModel;
}
set
{
_vm = value;
this.DataContext = _vm;
}
}
}
I'm not sure if my approach is working in principle but for the sake of saving changes to the database I need my context in knowledge of changes made to it. Right now it is obviously not working bacause an ModuleInitializeException occurs. Stacktrace:
An exception occurred while initializing module 'PersonModule'.
- The exception message was: An exception has occurred while trying to add a view to region 'PersonData'.
- The most likely causing exception was was: 'System.InvalidOperationException: The type EntityContext has multiple constructors of length 1. Unable to disambiguate.
bei Microsoft.Practices.ObjectBuilder2.ConstructorSelectorPolicyBase1.FindLongestConstructor(Type typeToConstruct)
bei Microsoft.Practices.ObjectBuilder2.ConstructorSelectorPolicyBase1.SelectConstructor(IBuilderContext context)
bei Microsoft.Practices.ObjectBuilder2.DynamicMethodConstructorStrategy.PreBuildUp(IBuilderContext context)
bei Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context)'.
But also check the InnerExceptions for more detail or call .GetRootException().
- The Assembly that the module was trying to be loaded from was:App, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Check the InnerException property of the exception for more information. If the exception occurred while creating an object in a DI container, you can exception.GetRootException() to help locate the root cause of the problem.
If there are other solutions for that problem I'm open-minded to it, but I want to use the basic structure presented more or less.
Thanks in advance.
You have to configure the container to disambiguate the construction of EntityContext:
Container.RegisterType<EntityContext >("Context", new InjectionConstructor(...))

Categories