I am currently writing a WPF application which does command-line argument handling in App.xaml.cs (which is necessary because the Startup event seems to be the recommended way of getting at those arguments). Based on the arguments I want to exit the program at that point already which, as far as I know, should be done in WPF with Application.Current.Shutdown() or in this case (as I am in the current application object) probably also just this.Shutdown().
The only problem is that this doesn't seem to work right. I've stepped through with the debugger and code after the Shutdown() line still gets executed which leads to errors afterwards in the method, since I expected the application not to live that long. Also the main window (declared in the StartupUri attribute in XAML) still gets loaded.
I've checked the documentation of that method and found nothing in the remarks that tell me that I shouldn't use it during Application.Startup or Application at all.
So, what is the right way to exit the program at that point, i. e. the Startup event handler in an Application object?
First remove the StartupUri property from App.xaml and then use the following:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
bool doShutDown = ...;
if (doShutDown)
{
Shutdown(1);
return;
}
else
{
this.StartupUri = new Uri("Window1.xaml", UriKind.Relative);
}
}
If you remove the StartupUri from app.xaml for an application with a MainWindow you need to make sure you make the following call in your OnStartup method otherwise the application will not terminate when your MainWindow closes.
this.ShutdownMode = System.Windows.ShutdownMode.OnMainWindowClose;
#Frank Schwieterman, something along these lines may help you with your console window issue.
I did this a little differently to avoid having to set the StartupUri and ShutdownMode properties. First edit the App.xaml file and replace StartupUri with Startup:
<Application x:Class="Menu.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Menu"
Startup="Application_Startup">
<Application.Resources>
</Application.Resources>
</Application>
Then add Application_Startup to the code along with OnExit:
public partial class App : Application
{
private volatile static Mutex s_mutex;
private void Application_Startup(object sender, StartupEventArgs e)
{
s_mutex = new Mutex(true, #"Global\MenuMutex", out bool grantedOwnership);
if (!grantedOwnership)
{
MessageBox.Show($"Another instance is already running!", "Error", MessageBoxButton.OK, MessageBoxImage.Exclamation);
Current.Shutdown();
}
else
new MainWindow().Show();
}
protected override void OnExit(ExitEventArgs e)
{
s_mutex?.ReleaseMutex();
s_mutex?.Dispose();
s_mutex = null;
base.OnExit(e);
}
Write in the Application_Startup:
private void Application_Startup(object sender, StartupEventArgs e)
{
...
if (!condition)
{
e.GetType()
.GetProperty("PerformDefaultAction", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(e, false);
Shutdown();
return;
}
...
}
Related
Lets take a simple WPF application with two Window classes. The MainWindow has a single control - button - which creates AnotherWindow instance. If an Exception happens after the creation before the main thread exits ButtonMethod scope, then the application remains running after the MainWindow is closed and disappeared.
A workaround for that is to set a new window's Owner property to the MainWindow object instance.
The app will also keeps running even without any exception throwing if there would be no w.Show() or w.Close() call after an instance of AnotherWindow is created.
Questions:
Where is such behaviour of WPF window threads is described?
What is the best practice for creating other windows with an exception possibility keeping in mind: set window's Owner, call window.Close() in some finally scope or something else?
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
try
{
ButtonMethod();
}
catch (Exception exc)
{
MessageBox.Show(exc.Message);
}
}
private void ButtonMethod()
{
Window w = new AnotherWindow();
// Uncomment the line below to fix freezing at the exit.
// w.Owner = this;
throw new Exception("Custom user exception!");
w.Show();
}
}
To open a new window on wpf you use this code:
private void Button_Click(object sender, RoutedEventArgs e)
{
SecondWindow w = new SecondWindow();
w.Show();
}
And if you wish to close the one you're on is:
This.close();
You don't need the code
throw new Exception("Custom user exception!");
Because you're just making an exception which you are catching anyways, you throw an exception (typically) when you want to debug your code or to see if it's catching the right type of exceptions. I hope I helped.
I just got a weird behavior in my WPF application, hope somebody will give me a clue. I have the following overriden Application's OnStartup:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
try
{
if (AppMutex.WaitOne(TimeSpan.Zero, true))
{
IoC.Instance.RegisterInstance(LogManager.GetLogger("MainLogger"));
Init();
var mainView = new MainView()
{
DataContext = IoC.Instance.MainViewModel
};
mainView.ShowDialog();
}
}
catch (Exception exception)
{
IoC.Instance.Log.Error(exception);
throw;
}
finally
{
AppMutex.ReleaseMutex();
}
}
Here MainView is a subclass of System.Windows.Window and has no overriden methods.
The trouble is with mainView.ShowDialog(). This line is executing without any exceptions but the window isn't shown. Thread just goes forward and application terminates.
Has somebody any ideas about this behavior?
Thanks in advance!
UPD:
If I place MessageBox.ShowDialog() before this call, then MessageBox.ShowDialog executing correct, but mainView.ShowDialog throws an exception:
Cannot set Visibility or call Show, ShowDialog, or WindowInteropHelper.EnsureHandle after a Window has closed.
at System.Windows.Window.VerifyCanShow()
at System.Windows.Window.ShowDialog()
with null InnerException.
UPD: I got symbols and checked VerifyCanShow:
private void VerifyCanShow()
{
if (this._disposed)
throw new InvalidOperationException(SR.Get("ReshowNotAllowed"));
}
Looks like my mainView getting disposed in some way...
You need to set Application.ShutdownMode to OnExplicitShutdown. http://msdn.microsoft.com/en-us/library/system.windows.application.shutdownmode(v=vs.110).aspx
You can then manually shut down the application after your dialog closes.
mainView.ShowDialog();
Shutdown();
I have a WPF application, with two methods Application_Startup and Application_Exit in the App.cs file, which are hooked to the Startup and Exit events of the application respectively. Here's the implementation for these two methods:
private void Application_Startup(object sender, StartupEventArgs e)
{
try
{
if (LoginModel.TryLoginUsingSavedCredentials())
{
MainView view = new MainView();
view.Show();
}
else
{
LoginView loginView = new LoginView();
var result = loginView.ShowDialog();
if (result.HasValue && result.Value)
{
MainView view = new MainView();
view.Show();
}
}
}
catch (FaultException ex)
{
Views.MessageBox.ShowMessage("Login failed",
string.Format("Unabled to login.\r\n{0}", ex.Message),
Entities.Enums.DialogType.OK,
Entities.Enums.DialogIcon.Error);
}
}
private void Application_Exit(object sender, ExitEventArgs e)
{
ConnectionFactory.Instance.CloseAllProxyChannels();
}
When I run the application, if LoginModel.TryLoginUsingSavedCredentials() returns false, the LoginView is displayed. Now, I set a breakpoint at if(result.HasValue ...) and I step in. MainView gets created and view.Show() gets executed, however, the debugger arrow jumps immediately to the Application_Exit method, and the application terminates. I don't see any exception,output error, dump file, etc.
The weird thing is, if I comment out all the logic in Application_Startup and just put these two lines:
MainView view = new MainView();
view.Show();
Then the application works just fine. Any idea what could be possibly going wrong? Thanks in advance.
Probably the auto-shutdown because you effectively close the last window. You can try to change the Application.ShutdownMode to OnExplicitShutdown while getting the credentials.
(You can also set it to OnMainWindowClose permanently and set Application.MainWindow to your main view)
For some reason I can't get this to work at all. I have read from various sources that I can override OnStartup in a WPF application and it will fire off as the App is created. However, no matter what I do, nothing is happening. Here is the code.
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
// My code goes here, but nothing ever happens.
base.OnStartup(e);
}
}
Obviously I am missing something. Sadly the MSDN page doesn't offer much insight either.
http://msdn.microsoft.com/en-us/library/system.windows.application.onstartup.aspx
What am I doing wrong?
EDIT:
It turns out that my problem was a small typo in the namespace. App.xaml.cs had the class defined as 'RTDMyApp.App' and the App.xaml file was referring to it as 'RTD_MYApp.App' At any rate, this fact, combined with the accepted answer below has gotten me back on track.
Did you remove the StartupUri too from the App xaml?
If you did you have to create the window you want show:
base.OnStartUp(e);
var window = new Window1();
this.MainWindow = window;
window.Show();
I think what you really want to do is to subscribe to the Startup event. You can do this in your XAML file:
<Application ... Startup="Application_Startup">
Sequence sequence sequence. How annoying.
The right sequence (for a WPF application with NO Main method explicitly developed/declared) is:
// XAML
... Startup="Application_Startup"
//code-behind
private void Application_Startup(object sender, StartupEventArgs e)
{
...
...
// do something. In fact, here I do a lot of stuff that reflects
// some serious recent application illnesss:
try
{
//http://connect.microsoft.com/VisualStudio/feedback/details/618027/uriformatexception-thrown-by-ms-internal-fontcache-util
System.Environment.SetEnvironmentVariable("windir", Environment.GetEnvironmentVariable("SystemRoot"));
// per http://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo.ietflanguagetag(v=vs.110).aspx
var cultureName = CultureInfo.CurrentCulture.Name;
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(cultureName)));
// Setup unhandled exception handlers
#region Handlers For Unhandled Exceptions
// anything else to do on startup can go here and will fire after the base startup event of the application
// First make sure anything after this is handled
// Creates an instance of the class holding delegate methods that will handle unhandled exceptions.
CustomExceptionHandler eh = new CustomExceptionHandler();
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(eh.OnAppDomainException);
// this ensures that any unhandled exceptions bubble up to a messagebox at least
Dispatcher.CurrentDispatcher.UnhandledException += new DispatcherUnhandledExceptionEventHandler(eh.OnDispatcherUnhandledException);
#endregion Handlers For Unhandled Exceptions
// Start the dispatcher
// for Galasoft threading and messaging
DispatcherHelper.Initialize();
}
catch (Exception ex)
{
ex.PreserveExceptionDetail();
throw ex;
}
}
and then I do:
protected override void OnStartup(StartupEventArgs e)
{
App.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
App.HasRaisedFatalException = false;
base.OnStartup(e);
try
{
//Force just one copy to run
this.ForceSingleInstance();
...
...
...
}
and so far the patient is feeling much better.
I want that the WPF application starts only in certain conditions. I tried the following with no success:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
if (ConditionIsMet) // pseudo-code
{
base.OnStartup(e);
}
}
}
But the app runs normally even when the condition is not met
Try this:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
if (MyCondition)
{
ShowSomeDialog("Hey, I Can't start because...");
this.Shutdown();
}
}
Another solution
Designer
<Application x:Class="SingleInstanceWPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="Application_Startup">
</Application>
Code behind
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
if (ConditionIsMet)
{
var window = new MainWindow();
window.Show();
}
else
{
this.Shutdown();
}
}
}
It may be that I am doing this the really really hard way, but I have found that fighting with the invocation precedence/order on startup by assuming anything is futile. I really wanted any exception raised during the startup of the application to IMMEDIATELY bubble up to the outermost exception handler, but even when one was raised, my MVVM Locator object was automatically instantiating itself because it is defined as an application-level resource.
That meant that the chicken arrived before the egg but after the same egg had already broken!!!
So the solution was:
1) Remove MVVM Locator from App.xaml.
2) Create an Application_Startup event
Add these lines at the top:
#region Handlers For Unhandled Exceptions
// anything else to do on startup can go here and will fire after the base startup event of the application
// First make sure anything after this is handled
// Creates an instance of the class holding delegate methods that will handle unhandled exceptions.
CustomExceptionHandler eh = new CustomExceptionHandler();
AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(eh.OnAppDomainException);
// this ensures that any unhandled exceptions bubble up to a messagebox at least
Dispatcher.CurrentDispatcher.UnhandledException += new DispatcherUnhandledExceptionEventHandler(eh.OnDispatcherUnhandledException);
#endregion Handlers For Unhandled Exceptions
3) Tie Startup to the Application_Startup event in App.xaml
e.g.
Startup="Application_Startup" <<<< this name is arbitrary but conventional AFAICT
4) In Applicaton_Startup, create the ViewModelLocator like this:
Resources.Add("Locator", new ViewModelLocator());
//You can use FindResource and an exception will be thrown straightaway as I recall
if (!(TryFindResource("Locator") == null))
throw new ResourceReferenceKeyNotFoundException("ViewModelLocator could not be created", "Locator");
5) Then, immediately after the resource has been found, open the MainWindow, but only if the Locator was successfully instantiated
Uri uri = new Uri("pack:Views/MainWindow.xaml", UriKind.RelativeOrAbsolute);
Application.Current.StartupUri = uri;
Step (4) will throw an exception immediately if the constructor on the Locator fails WHICH HAPPENS ALL THE TIME TO ME, REGRETTTABLY.
Then, the exception from step 4 is handled as follows (this example uses a RadMessageBox but feel free to fix that:
public void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
try
{
var result = this.ShowExceptionDialog(e.Exception);
}
catch
{
RadMessageBox.Show("Fatal Dispatcher Error - the application will now halt.", Properties.Resources.CaptionSysErrMsgDlg,
MessageBoxButton.OK, MessageBoxImage.Stop, true);
}
finally
{
e.Handled = true;
// TERMINATE WITH AN ERROR CODE -1!
//Environment.Exit(-1);
}
}