How to "restore" SynchronizationContext when using custom startup? - wpf

My app works fine, but now I wanted to have custom startup, so I could catch any errors, add my logging from the start, etc.
So I used approach as shown in this answer What code controls the startup of a WPF application?
[STAThread]
public static void Main(string[] args) {
//include custom startup code here
var app = new MyApplication();//Application or a subclass thereof
var win = new MyWindow();//Window or a subclass thereof
app.Run(win); //do WPF init and start windows message pump.
}
It works fine with one exception -- SynchronizationContext.Current is null. And I need it :-)
So how to correctly make custom startup and have synchronization context?

Don't create your own Main method. Override the OnStartup method in your App.xaml.cs file:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var win = new MyWindow();
win.Show();
}
}
Then you will get a SynchronizationContext as usual.
Don't forget to remove the StartupUri attribute from the <Application> root element of your App.xaml file.

Related

How to correctly dispose of dependencies created by ContainerControlledLifetimeManager in a Prism-Unity-WPF application

I got an application mostly following this MSDN article.
Here is my bootstrapper class:
internal class Bootstrapper : UnityBootstrapper
{
// Wire up the dependencies using Unity container
protected override void ConfigureContainer()
{
base.ConfigureContainer();
// Register the connection manager
Container.RegisterType<IConnectionManager, ConnectionManager>(new ContainerControlledLifetimeManager());
}
// Return an instance of the main window
protected override DependencyObject CreateShell()
{
return ServiceLocator.Current.GetInstance<Shell>();
}
// For WPF, initialising shell is simply setting the data context and showing the main window
protected override void InitializeShell()
{
base.InitializeShell();
Application.Current.MainWindow = (Window)this.Shell;
Application.Current.MainWindow.DataContext =
ServiceLocator.Current.GetInstance<MainViewModel>();
Application.Current.MainWindow.Show();
}
}
The connection manager in the code is registered with ContainerControlledLifetimeManager which means it will be treated as a singlton.
Now my connection manager is implementing IDisposable and I want to know the conventional way to dispose the container in order for it to dispose all related ContainerControlledLifetimeManager-ed objects.
I started by overriding OnExit of the application, but I found that UnitBootstrapper doesn't have a dispose method. I can write all the code manually to dispose the container on exit, but I am guessing there must be a formal way to do this kind of usual stuff.
This question is based on the old-style (Prism 6) Unity Bootstrapper. This answer is compatible with Prism 7 Unity containers.
An IUnityContainer object may be retrieved from the Prism IContainerProvider using the a Unity extension method, GetContainer(). The IUnityContainer can be disposed from the OnExit override of an Application that inherits from PrismApplication:
protected override void OnExit(ExitEventArgs e)
{
base.OnExit(e);
Container.GetContainer().Dispose(); // must be called to dispose singletons
}

Caliburn.Micro Starting the bootstrapper from code?

Normally I just but the bootstrapper in the resources of the App.xaml, but for an app I'm building I need code execution to begin elsewhere and then start the bootstrapper up once I'm done my initialization code.
How can I start the bootstrapper?
I set the App.xaml to call a function as then did this:
using System.Windows;
namespace WpfApplication9
{
public partial class App : Application
{
private AdminBootstrapper b;
private void App_OnStartup(object sender, StartupEventArgs e)
{
//DO initialization
b = new AdminBootstrapper();
b.Start();
}
}
}
When I run nothing happens, and my view does not appear. I know the view/viewmodel work because if I put the bootstrapper in the resources section of the App.xaml it appears.
What am I doing wrong here?
Use the Initialize() method instead of Start(). I had the same error and searched the internet and found a post that said to use Initialize() instead. I tried it on the sample app in the Caliburn.Micro docs and it worked for me.
This is not the "correct" way to use the bootstrapper, the bootstrapper IS the place were you do the setup you talk of. The whole point is to provide a place were Caliburn.Micro knows you are going to set up. Without seeing your bootstrapper though it is impossible to know what is wrong.
The root ViewModel is not loaded until after Configure is called. This is your extensibility point. If you need to do complex configuration you could call a couple of overrides here and then derive a class which exposes these methods.
In general though you would need to provide more information about what you are doing before I could give you a concrete recipe.
Maybe not the correct way (wanted to remove it from the markup) but still, this code seems to work...
public partial class App : Application
{
MyBootstrapper _bootstrapper;
protected override void OnStartup(StartupEventArgs e)
{
_bootstrapper = new MyBootstrapper();
base.OnStartup(e);
}
}
public class MyBootstrapper : BootstrapperBase
{
public MyBootstrapper()
{
Start();
}
protected override void OnStartup(object sender, System.Windows.StartupEventArgs e)
{
DisplayRootViewFor<ShellViewModel>();
}
}

Application class in winforms and WPF

I am new to GUI development, i was trying to develop a sample ui application using winforms and WPF.
I found some of the code missing in WPF
namespace WindowsFormsApplication3
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
Why this code is not present in WPF when the project is created
Why we are using Application class, what is the need of this class?
WPF uses the Application.Xaml and Application.cs to start your application.
It start your application and you can override some methods and choose how to start your application, apply come general configuration and error handling for your application
This code is not present because in the app.xaml definition there is usually a StarupURI="window1.xaml" attribute that allow the WPF infrastructure to wire the startup code behind the scene.
Of course you can create a custom bootsrapper, you need to remove the StartupURI attribute and intercept the Application startup to create/show a window:
public partial class App
{
protected override void OnStartup(StartupEventArgs e)
{
try
{
var mainView = new MainView();
mainView.Show();
mainView.DataContext = new YourDataContext();
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
}

How to customize startup of WPF application?

When a new WPF Application project is created, MainWindow.xaml, App.xaml and their corresponding code behind classes are automatically generated. In the App.xaml there is an attribute that defines which window is going to be run initially and by the default it's StartupUri="MainWindow.xaml"
I have created a new Dispatcher class in the same project. At startup, I want the instance of that class Dispatcher to be constructed and then one of its method to run. That method would actually create and show the MainWindow window. So how do I modify the App.xaml or App.xaml.cs in order to make it happen? Or, if it cannot be done by App, how should I implement it? Thanks.
You can remove the StartupUri attribute from the App.xaml.
Then, by creating an override for OnStartup() in the App.xaml.cs, you can create your new instance of your Dispatcher class.
Here's what my quick app.xaml.cs implementation looks like:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
new MyClassIWantToInstantiate();
}
}
}
Update
I recently discovered this workaround for a bug if you use this method to customize app startup and suddenly none of the Application-level resources can be found.
Try to use the Startup event (class Application) - MSDN.
You can show MainWindow in this event handler - after you create a Dispatcher instance.
1.In App.xaml, To replace the StartupUri with a subscription to the Startup event.
Use the event in App.xaml.cs .
For instance,
Startup="Application_Startup" in .xaml.
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
// Create the startup window
MainWindow wnd = new MainWindow();
// Do stuff here, e.g. to the window
wnd.Title = "Something else";
// Show the window
wnd.Show();
}
}

Caliburn Launch without App.xaml, but with bootstrapper

I have a WinForms project from which I want to open a WPF window from a WPF user control project.
But when I create an instance of the WPF window and call Show(), the bootstrapper isn't loaded. In an Windows Application, it's located in the App.xaml, but an user control project doesn't have this.
What can I do?
Thanks!
The only thing accomplished by having the bootstrapper in App.xaml's resources is instantiation of the bootstrapper and keeping a reference so it isn't garbage-collected. You could try making it instantiate like this:
public class SomeClass {
static Bootstrapper _bs = new Bootstrapper();
...
}
That will make sure it's initialized as part of static construction, which happens sometime before you can create an instance of SomeClass. You may have to experiment to see whether that should happen in your UserControl or in your Window.
I have a console application which presents a WPF gui that I made with Caliburn.Micro. I present the GUI like this:
_App = new App();
_App.Run();
Where App.xaml contains the bootstrapper and the main thread is STA like this:
[STAThread]
static int Main(string[] args)
{ ... }
I know your situation is different but maybe this will give you an idea.
Console application test:
Add MaterialDesignColors pakage
[System.STAThreadAttribute()]
static void Main(string[] args)
{
var _app = new App();
_app.InitializeComponent();
_app.Run();
_app.Shutdown();
}

Resources