Application closes if any window is shown before the main window - wpf

I have a very puzzling case. During the initialization of my application, before any windows are shown, this function deserializes application settings from XML file, but if deserialization function throws any errors, it displays an error message to the user (which is a custom-made WPF dialog), and once that message dialog is closed, it creates new instance of the settings, and continues the initialization:
Public Function LoadSettings()
Try
Return DeserializeFromXML(settingsPath)
Catch ex As Exception
Msg.ShowMessage(Msg.corruptedSettingsFile)
Return new AppSettings
End Try
End Function
ShowMessage is defined as:
Public Function ShowMessage(message As Message) As Boolean
Dim messageDialog As New MessageDialog(message.Title, message.Content, message.Buttons)
Return messageDialog.ShowDialog()
End Function
Now, the weird thing is, after that exception is caught, and error message is closed by the user, initialization continues, but when it arrives at mainWindow.Show(), nothing happens. Main window is not shown. And once initialization code finishes, the application closes immediately.
In my Application Properties, the Shutdown mode is set to "On main window close". StartupUri is not set, because I want to show that main window exactly when I want, not automatically. However, for testing, I tried to remove mainWindow.Show() altogether, and set StartupUri to MainWindow.XML, but it doesn't solve the issue either.
I have tested that if I do not display that error message to the user, the application loads correctly, either with mainWindow.Show() or with StartupUri.
Why is this happening?
EDIT: I forgot to mention, which is probably important, that before the mainWindow.Show(), I also call Application.Current.MainWindow = mainWindow. So in case WPF makes that message dialog main window, this call should override it... But it still doesn't work.
EDIT 2: I discovered that if I call Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown before showing the message, and restore it with Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose and also call Application.Current.MainWindow = mainWindow, then the application does not close, and all works correctly. However, this is a horrible solution. It is obvious now that the message dialog hijacks the Application.Current.MainWindow. How do I stop this behavior on the application level, so that the Application.Current.MainWindow would only get set when I explicitly set it?

you must set MainWindow before calling ShowDialog methed of any other windows. The reason of this is: a window that was created in application thread, will set itself as main window if MainWindow was null; and after ShowDialog was called, you would have any chance to fix this because of modal mode.
// this will work.
private void Application_Startup(object sender, StartupEventArgs e)
{
var a = new MainWindow();
var b = new MessageDialog();
b.ShowDialog();
a.Show();
}
// this will not.
private void Application_Startup(object sender, StartupEventArgs e)
{
var b = new MessageDialog();
b.ShowDialog(); //app shutdown at this point
var a = new MainWindow();
a.Show();
}

Related

How do I show windows sequentially in WPF?

I have the following code in App.xaml.cs:
protected override void OnStartup(StartupEventArgs e)
{
var window = new WelcomeWindow();
if (window.ShowDialog() == true)
{
var mainWindow = new MainWindow();
mainWindow.ShowDialog();
}
}
The second window never shows. Instead, the application simply closes when the Welcome window is closed. How do I ensure a second window can be shown after a first one is closed?
This is because default value of Application.ShutdownMode is OnLastWindowClose. This means when your WelcomeWindow is closed the application shuts down and you see nothing more.
To solve this set ShutdownMode to OnExplicitShutdown and call Shutdown explicitly if you want to exit your app.
public App()
{
this.ShutdownMode = ShutdownMode.OnExplicitShutdown;
}
What about to show WelcomeWindow on Initialized event of MainWindow and close last if Dialog is not true. This was you let MainWindow to stay the MainWindow of Application.
private void Window_Initialized(object sender, EventArgs e)
{
// at this moment MainWindow is Initialized but still nonvisible
if ((new WelcomeWindow()).ShowDialog()!=true)
{
this.Close();
}
}
When you load any window Application_Startup it become The MainWindow of application. And it will closed on this window closing.
I've checked that even if you have StartupUri="MainWindow.xaml" in you app.xaml it have no effect if some else window have been shown on Application StartUp event.
You may do it yourself. Just make breakpoint on your firstloaded window Loaded event handler and look in debuger on "Aplication.Current.MainWindow == this" expression result. It will be true.

App xaml assumes the first window instantiated is the main window (showdialog is ignored), I need to show multiple windows

I have the following code in my App.xaml.cs
private void App_Start(object sender, StartupEventArgs e)
{
if ( CompletedInstall())
{
//using show to allow for pacifier if loading is slow
var manager = new WINServiceConfig();
MainWindow = manager;
manager.ShowDialog();
}
}
private bool CompletedInstall()
{
var window = new Initialize();
window.ShowDialog();
return window.DoLaunchManager;
}
and the following in the App.xaml
<Application x:Class="Manager.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="App_Start">
When i comment out the line that checks CompletedInstall() the manager.ShowDialog() works fine, and my configuration window shows.
When CompletedInstall() is called the call to manager.ShowDialog() returns right away without displaying the window. I added the main window on the assumption that somewhere along the line someone decided an app should only show one window.
I found a workaround by setting the main window before calling CompletedInstall
private void App_Start(object sender, StartupEventArgs e)
{
var manager = new WINServiceConfig();
MainWindow = manager;
if (CompletedInstall())
{
manager.ShowDialog();
}
but this forces me to develop WINServiceConfig (specifically the constructor) based on its use, because it cannot count on the prerequisites being completed. This is bad form. What else can i do to get around this problem?
Dummy window? That can't be the best answer. Can it??
You should set the ShutdownMode to OnExplicitShutdown (at least while showing the initial dialog).

My application loses focus when a window is closed

I have a simple two forms, one that contains a grid and a button. When I click the button, my application starts doing a long operation. While it is working, I show another form that contains a progress bar
I open it like this:
_busyWindow.ShowDialog();
And defined
public partial class BusyWindow : DevExpress.XtraEditors.XtraForm
{
public BusyWindow()
{
InitializeComponent();
}
private void BusyWindow_FormClosing(object sender, FormClosingEventArgs e)
{
this.Hide();
e.Cancel = true; // this cancels the close event.
}
}
When the operation is finished, I hide the form like this
if (ended)
_busyWindow.Hide();
It works fine. The problem is that when I close the second form (same closing code), it also closes fine but my main GUI loses the focus. For example, if I have the Firefox opened behind the application, then the Firefox gets the focus.
This only happens when I close the second form when the busyWindow has been opened, and no when it hasn't (ie, if I open the form, I close it without clicking on the button, then the main GUI doesn't lose the focus).
Do you know what is happening or where could I try to search?
There could be two possible solutions to enable you to keep focus on your main window:
//Edited: Main Window in the below example would be the window with Grid and Button.
Since you are showing the busy window via ShowDialog() try setting the owner of the window by this: _busyWindow.ShowDialog(this);. I had earlier faced a similar problem and this worked for me. Since you specify the owner of the busyWindow, when it closes it would put the focus back on its owner,i.e. your main window
In case the above technique doesnt work (it should, as it worked for me), you could try to pass the reference of the main window to the busyWindow and then on its close set the focus of the main window. Sample:
_busyWindow.MyMainWindow = this; //MyMainWindow references mainWindow of your app
_busyWindow.ShowDialog();
And the following at the FormClosing of busyWindow:
private void BusyWindow_FormClosing(object sender, FormClosingEventArgs e)
{
this.Hide();
e.Cancel = true; // this cancels the close event.
MainWindow.Focus();
}
See if it works. The first solution should work.
Hope it helps.
Thanks & Happy Windowing!
Just set child's window Owner = null before closing it

How to change StartupUri of WPF Application?

I am trying to modify App.cs and load the WPF XAML files from code behind but its not working as it should.
No matter whatever I try to set as StartupUri it doesnt start, the program quits after this.
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
LoginDialog dlg = new LoginDialog();
if (dlg.ShowDialog() != true)
return;
switch (dlg.ChoiceApp) {
case ChoiceApp.CustomerEntry:
StartupUri = new Uri("/MyApp;component/Forms/CustomerEntry.xaml",
UriKind.Relative);
break;
case ChoiceApp.VendorEntry:
StartupUri = new Uri("/MyApp;component/Forms/VendorEntry.xaml",
UriKind.Relative);
break;
}
}
}
Now I even did trace and found out that LoginDialog is working correctly and is returning values correctly but setting "StartupUri" does not work.
I checked in reverse assembly that DoStartup method of App gets called after OnStartup, so technically my StartupUri must load, but it doesnt, in App.xaml startup uri is not at all defined.
Note: Bug Confirmed
I noticed that ShowDialog sets Application.MainWindow and when dialog ends, it sets it back to null, and because of this setting StartupUri does not work after calling Modal Dialog in OnStartup or Startup event.
There is no error or exception about invalid uri or anything like that.
This method works without DialogBox being called in Startup event or OnStartup, i think calling showdialog on this method causes something like its mainwindow being set to expired window and it shuts down after this.
Akash, I ran into this exactly issue trying to implement a LoginDialog just like yours. The dialog does not have a bug, but rather the behavior is by design.
Not a bug. The default ShutdownMode of
Application is OnLastWindowClosed, so
as soon as the first window is closed
your application will start shutting
down! Change to OnExplicitShutdown and
it will work, but you'll have to
manage the shutdown.
See this previous StackOverflow question: WPF ShowDialog returns null immediately on second call
instead of overriding the OnStartup() method, hook into the event instead.
in the XAML
<Application x:Class="SOTestWPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="Application_Startup">
<Application.Resources>
</Application.Resources>
</Application>
in the code behind:
private void Application_Startup(object sender, StartupEventArgs e)
{
var rnd = new Random();
if (rnd.NextDouble() > 0.5)
StartupUri = new Uri("/SOTestWPF;component/Window1.xaml", UriKind.Relative);
else
StartupUri = new Uri("/SOTestWPF;component/Window2.xaml", UriKind.Relative);
}
This is only my test case and I have verified that it performs correctly (randomly :D)
Just try in OnStartup() :
StartupUri = new Uri("Forms/CustomerEntry.xaml", UriKind.Relative);
Do you still have a StartupUri specified in the XAML? If so, remove it and see if that helps.MSDN Source
If not, you may need to approach this differently: have your Dialog as your startup, then from that point open another Window based on the selected value.

WPF ComboBox DropDown part appears in the wrong place

I put several ComboBoxes on a XAML window. When I expand any of them, the DropDown part appears on the upper left corner of the screen.
I use Visual Studio 2008 C# Express. I don't remember this phenomenon when I used Visual Studio 2008 (Trial Version), though I use the same FrameWork (3.5).
It seems to be a bug.
Workaround:
Use Window.Show() instead with a custom logic to simulate the ShowDialog() behavior.
This appears to be a bug in WPF. In my case, I was trying to open a window in the Loaded event of another window. To get around this, I set a timer up to fire, then used a delegate to open the window (cannot open the window in a timer event because the calling thread that opens a window must be STA).
Edit - timer isn't necessary - didn't see the answer above just queue it on the dispatcher...
private delegate void DelegateOpenWindow();
private DelegateOpenWindow m_DelegateOpenWindow;
private Timer loginTimer = new Timer(200);
private void MainWindow1_Loaded(object sender, RoutedEventArgs e)
{
// create delegate used for asynchronous call
m_DelegateOpenWindow= new DelegateOpenWindow(this.OpenWindow);
// start a timer to fire off the open window.
loginTimer.Elapsed += loginTimer_Elapsed;
loginTimer.Enabled = true;
}
void loginTimer_Elapsed(object sender, ElapsedEventArgs e)
{
loginTimer.Enabled = false;
this.Dispatcher.BeginInvoke(m_DelegateOpenWindow);
}
void OpenWindow()
{
MyWindow w = new MyWindow();
w.Owner = this;
w.ShowDialog();
}
I started observing this (and other strange behavioral quirks) yesterday when I tried to "tweak" window sizes, shapes, colors, and invoke a log-on dialog from the Window.Loaded event handler. I had been doing this just fine in each of a dozen+ individual "MVVM" pattern apps. Yesterday, I decided to move this from each app's code behind into a consolidated code-behind base class, since the pre-processing had become common in all those apps. When I did, the drop-downs in two ComboBoxes in the log-in dialog suddenly appeared in the upper left corner of my screen. I seem to have "solved" it by using the following technique (your mileage may vary):
protected void WindowBaseLoadedHandler(object sender, RoutedEventArgs e)
{
...non-essential lines of code removed...
if (DataContext != null)
{
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
/*----------------------------------------------------------------------
* Do we have a View Model? If so, perform standard VM Initialization...
*---------------------------------------------------------------------*/
this.IsEnabled = false;
LoginDlg loginDlg = new LoginDlg();
loginDlg.ShowDialog();
if (!loginDlg.Success)
{
/*-----------------------------------
* Log on failed -- terminate app...
*----------------------------------*/
...termination logic removed...
}
this.IsEnabled = true;
}));
}
WindowBaseLoadedHandler is the Loaded event handler. LoginDlg is a WPF app with a dialog containing two ComboBoxes.
Recap: After I consolidated the code into the Loaded event handler of the base class the ComboBox's drop down lists appeared in the upper left corner of my screen. Once I wrapped the logic into the Dispatcher.BeginInvoke call, the appropriate ComboBox behavior returned with lists below the current item.
I suspect WPF needs the application to return from the Loaded event to complete the layout system's initialization. That doesn't fully explain why it worked before, but I'll have to queue up my desire to hunt that "why" down for some rainy day in the future and celebrate overcoming the latest obstacle for today.
In any event, I hope someone finds this of use.
I'm using the latest .Net 4.5 and WPF framework and I still have this problem. One thing I noticed is that it only happen when there's an attached debugger. When the debugger is not attached, everything works fine.
I had the same problem on Visual Studio 2019.
Using window.Show() can help but it can ruin your design.
The solution is to open the window asynchronously.
var yourDialog= new YourDialog();
yourDialog.Owner = this;
TaskCompletionSource<bool?> completion = new TaskCompletionSource<bool?>();
this.Dispatcher.BeginInvoke(new Action(() =>
completion.SetResult(yourDialog.ShowDialog())));
bool? result = await completion.Task;
You can also create a more elegant solution by making the extension method:
public static class AsyncWindowExtension
{
public static Task<bool?> ShowDialogAsync(this Window self)
{
if (self == null) throw new ArgumentNullException("self");
TaskCompletionSource<bool?> completion = new TaskCompletionSource<bool?>();
self.Dispatcher.BeginInvoke(new Action(() => completion.SetResult(self.ShowDialog())));
return completion.Task;
}
}
And you can use it like this:
await dlgReview.ShowDialogAsync();
It’s a bug in WPF (not the only one, I'm afraid). It happened when I opened another window in the Loaded Event, something like:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Window selectionWindow = new SelectionWindow();
bool? result = selectionWindow.ShowDialog();
if (result == true)
RecordChanged();
}
I already found a workabout.

Resources