How solve circular reference in Caliburn.Micro - c#

I am working with Caliburn.Micro v2.0.1 on a Windows 8.1 Unversal (WinRT) project.
I followed the Caliburn.Micro Working with WinRT example.
My code looks as follows:
App.xaml.cs
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
Initialize();
DisplayRootViewFor<LoginViewModel>();
}
protected override void PrepareViewFirst(Frame rootFrame)
{
_container.RegisterNavigationService(rootFrame);
}
LoginViewModel.cs
public LoginViewModel(INavigationService navigationService, ...)
{
...
}
The issue
The OnLaunched is called first.
Initialize() Configures the WinRT container.
The DisplayRootViewFor<LoginViewModel> invokes an instance of the LoginViewModel and results in a Null exception because NavigationService has not yet been registered by PrepareViewFirst(Frame)
PrepareViewFirst(Frame) is not yet called, having a dependency on the RootFrame that should be configured by OnLaunched
Thus LoginViewModel is dependent on RegisterNavigationService and RegisterNavigationService is dependent on DisplayRootViewFor<LoginViewModel>() which is dependent on LoginViewModel
Is there any way to overcome this circular reference issue?

Register your services in the container before resolving the Views - this way all dependencies are available in the particular Dependency Injection container and you can use ServiceLocator to find them.
Typically I've always done this in the OnStartup() method of App.xaml.cs.

You should register/configure your container at the composition root, the earliest access point of your application.
This point depends on what kind of Application you have:
ASP.NET MVC: Global.asax.cs
Console Application: the Main Method
WPF: Application.OnStartup
WinPhone 7: See https://stackoverflow.com/a/7310492/455493
etc.
Check the Windows 7 lifecycle on http://msdn.microsoft.com/en-us/magazine/hh148153.aspx

Related

WPF MVVM Dependency Injection for UserControls

I'm developing a WPF .Net Core 5 Application using MVVM Pattern, and I'm trying to use Dependency Injection to learn something new.
I've just modified the App.xaml.cs class as follows and after initial login, I can reach MainWindow showing as expected:
public partial class App : Application
{
private readonly ServiceProvider _serviceProvider;
public App()
{
ServiceCollection services = new ServiceCollection();
ConfigureServices(services);
_serviceProvider = services.BuildServiceProvider();
}
private void ConfigureServices(ServiceCollection services)
{
services.AddTransient<frmSQLConnection>();
services.AddTransient<frmLogin>();
services.AddSingleton<MainWindow>();
}
protected override void OnStartup(StartupEventArgs e)
{
if ((SuccessfullyConnected || _serviceProvider.GetService<frmSQLConnection>().ShowDialog() == true) && _serviceProvider.GetService<frmLogin>().ShowDialog() == true)
{
_serviceProvider.GetService<MainWindow>().Show();
}
else
{
Shutdown();
}
}
}
My doubts come now because MainWindow.xaml will be composed by a RibbonMenu on the top and a TabControl right below, like this example:
enter image description here
My question is what would be a good implementation of Dependency Injection for UserControls?
I've read some articles speaking about Composite Application but it seemed to be more useful for nested UserControls than in my case.
Any suggestion?
Thanks in advance for availability
If you are conforming to the MVVM pattern, then you would set up ViewModels for each user control and use DI in the constructor or method bodies to inject the 'services' you need. Then you can use something like PRISM (MVVM Framework) to trigger events that other ViewModels can subscribe to. It also provides mechanisms for navigation within an MVVM application as well.
EDIT:
You may also want to look into using a 'shell' view and then user controls inside. This will enable you to swap/navigate to different user controls within the 'shell' view. Which can have its own view model and global events, etc.
PRISM Resources: https://prismlibrary.com/docs/index.html

How to locally disable optimization

I faced with strange problem. My app has next code, that execute on startup:
protected override void RegisterTypes()
{
// App.container - UnityContainer
var dvcc = new MyClientCore(new MySorter());
App.container.RegisterInstance(typeof(ClientCore), dvcc);
App.container.RegisterInstance(typeof(MyClientCore), dvcc);
this.dataProvider = new MyProvider();
this.dataProvider.Configure(App.container);
App.container.RegisterInstance(typeof(Provider), this.dataProvider);
App.container.RegisterInstance(typeof(MyProvider), this.dataProvider);
// Create view models and register them in container
this.RegisterViewModels();
// Command - singleton, that resolved in ctor view models,
// that registered in RegisterViewModels();
Command.Instance.InitCommands();
// Create and configure shell
}
When app execute in debug configuration there are no problem. Also when app execute in release configuration in visual studio.
But when I try to start app directly I get TypeInitializationException in Command ctor because of resolution of the dependency registered in dataProvider.Configure method.
I thought that cause in JIT optimization, and code may be execute like
protected override void RegisterTypes()
{
Command.Instance.InitCommands();
// other method body
}
I found solution for that - mark RegisterTypes method atribute [MethodImpl(MethodImplOptions.NoOptimization)].
But maybe exist better method to fix it? Also I will be gratiful for link, when that situation explained with simple words.
P.S. Sorry for my english.

Inject Dispatcher into ViewModel - Recompose Unity - property injection

I'm trying to inject the Dispatcher of my ShellPage into my ViewModel with Unity and Prism. Because the dispatcher is available after the shell is being created, I can't register the Dispatcher in time.
First step:
protected override Task OnInitializeAsync(IActivatedEventArgs args)
{
[...]
Container.RegisterInstance<IResourceLoader>(resourceLoader);
Secondly the CreateShell method will be executed.
protected override UIElement CreateShell(Frame rootFrame)
{
var shell = Container.Resolve<ShellPage>();
Container.RegisterType<MyViewModel>(new InjectionProperty(nameof(MyViewModel.Dispatcher), shell.Dispatcher));
Although I inject the Dispatcher into MyViewModel into the propertie Dispatcher is null. Maybe I need something like recomposing in MEF? How can I achieve the propertie injection into MyViewModel?
I think is not possible.
Recomposing like MEF is not supported.
The easy way is creating a helper to get the Dispatcher of the current view and use it in your ViewModels
Please see this example:
http://mikaelkoskinen.net/post/fixing-coredispatcher-invoke-how-to-invoke-method-in-ui-thread-in-windows-8-release-preview
This example is for Windows 8 but you can use it for UWP apps
just when I use this approach I do small changes for UWP apps:
In Onlaunched event in app.xaml.cs
I add UIDispatcher in a different place.
if (rootFrame.Content == null)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
UIDispatcher.Initialize();
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
// Ensure the current window is active
Window.Current.Activate();

Bootstrapper design pattern using Caliburn.Micro MVVM framework with IoC-container

The WPF application uses Caliburn.Micro as MVVM framework. CM has it's built in IoC-Container named SimpleContainer, I replaced it with Castle.Windsor container. (But container type does not matter here I guess.) CM uses a Bootstrapper.Configure() method where container can be configured. After that Bootstrapper.OnStartup() method starts application displaying a View for root ViewModel. So container configures before first View appears. In my case container configuration a pretty complex and may cause errors. For example I want to deserialize some XML files from app directory to objects and register them as components in container. So I want to get a SplashScreen to see container configuring progress, and then if all OK, splash disappears, container resolves root item and application starts as expected. If not - problem shown on splashscreen. I can't get in mind how to get a SplashScreenView (binded to SplashScreenViewModel) before I get a configured container. So application divides on "before" and "after" container. How can that issue can be solved? Is there any patterns? Is it OK to partly configure container in Configure and partly somewhere else, after it Resolved some components? Or maybe there is a practic to use container instance "inside" components resolved by another container instance? Thanks.
public class CastleModernUIBootstrapper : BootstrapperBase
{
private WindsorContainer _container;
public CastleModernUIBootstrapper()
{
Initialize();
}
protected override void Configure()
{
_container = new WindsorContainer();
// here components are registered.
// I want perform complex container configuration here,
// but I can't vizualize what happens here.
_container.Register(Component.For<LoadingSplashScreenViewModel>());
// ...
}
// ...
protected override void OnStartup(object sender, StartupEventArgs e)
{
// after we resolving first element from container
DisplayRootViewFor<LoadingSplashScreenViewModel>();
}
}
WPF has a built-in (as in I don't know how it works) Splash Screen. Just use "Add new item" on your project. It does work with Caliburn.
It's only a simple bitmap, but it takes care of the appearance. You can handle your errors as normal data.
You don't want your Splash window to be the Root View anyway.

MissingMethodException - Caliburn

I have created a program that uses plugins using Caliburn and .NET 4.
When creating an instance of the plugin, a container, window manager, and a view-model factory is injected using caliburn's abilities.
However, when a code containing usage of one of the injected properties is run, I get a
MissingMethodException
. It is driving me crazy.
Also, if the plugin instance has dependencies that need to be satisfied (like IContainer, and IWindowManager) which are registered by default in Caliburn, it fails to create it with the message that no such components were found in the IoC container.
In debug mode I checked and everything is registered so I don't know what to do.
What do you think could be the problem?
Regarding the MissingMethodException, make sure the signature in the XAML matches the method's.
For example, this in the XAML:
<DockPanel cal:Message.Attach=" [Event KeyDown] = [Action KeyDown($eventArgs)]">
Matches this method:
public void KeyDown(KeyEventArgs args)
{
//stuff
}
If you remove the $eventArgs bit you will get an exception.

Categories

Resources