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)
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.
In my application, I am overriding the OnClose event the way shown below. Since the application can take some time to perform SynchronizeLotsaStuff, I want to notify the user that application will soon close.
I tried with a MessageBox, but that blocks the continuation of the program, and also displays an "OK" button that is not desired.
I guess I would prefer something more characteristic, such as fading/graying the window, or even a "splash screen" for closing, but a regular messagebox would be fine too.
// MainWindow.xaml.cs:
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
MessageBox.Show("Wait while application is closed...");
if (this.DataContext != null) {
var vm = this.DataContext as ShellViewModel;
// possibly long-running method
vm.SynchronizeLotsaStuff();
}
base.OnClosing(e);
}
UPDATE: Following Stijn Bernards advice, I put the MessageBox stuff inside a thread, but I haven't found (yes I googled) a proper way to terminate it. Even if Abort, the MessageBox keeps displaying after MainWindow has closed, until I click "OK" button.
// MainWindow.xaml.cs:
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
var messagethread = new Thread(new ThreadStart(() => {
MessageBox.Show("Aguarde enquanto o aplicativo é encerrado...");
}));
messagethread.Start();
if (this.DataContext != null) {
var vm = this.DataContext as ShellViewModel;
// possibly long-running method
vm.SynchronizeLotsaStuff();
}
// UGLY UGLY UGLY (and doesn't work either)
messagethread.Abort();
base.OnClosing(e);
}
I have tried using the abort thread and to my suprises it isn't working.
And well I can say it doesn't look easy...
So I advice to look into this: click me
It's a good tutorial about how to create your own message box, which would then also help you with your problem of removing the ok button.
Cheers.
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();
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);
}
}