I have the following simple wpf application:
App.xaml:
<Application x:Class="TestWpf2.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Application>
App.xaml.cs:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
var parentWindow = new Window();
parentWindow.Show();
var childWindow1 = new Window { Owner = parentWindow };
childWindow1.Show();
var childWindow2 = new Window { Owner = parentWindow };
childWindow2.Show();
}
}
The application causes 3 windows to appear on screen. If you run the application and close the two child windows, the parent window is minimized to the task bar. If you comment out childWindow2.show(), run the application, and close the single child window, the parent window is not minimized to the taskbar.
I can add the following code to work around this problem:
childWindow1.Closing += delegate(object sender, CancelEventArgs ex)
{
(sender as Window).Owner = null;
};
but I don't want to use a hack like this, and I want to understand why this problem occurs.
Why does this happen?
This is a WPF undocumented feature(Bug)
This bug has been reported to Microsoft more than 7 years ago.
Modal dialog on top of non-modal window sends main window to back.
The WPF team has recently reviewed this issue and will not be
addressing this issue as at this time the team is focusing on the bugs
impacting the highest number of WPF developers.
Let's look at the behavior of this undocumented feature a bit.
It doesn't Minimize, just go one window behind (in debug mode, behind the visual studio window)
Steps to prove:
Run this application.
Minimize all other windows (eg: visual studio and all other windows). Keep only these three windows on the screen.
Close both children, parent is still in Normal State
Test this code for further proof:
StateChange wouldn't fire.
protected override void OnStartup(StartupEventArgs e)
{
var parentWindow = new Window() { Title = "Parent" };
parentWindow.StateChanged += (sender, ep)=>
{
var state = ((Window)sender).WindowState;
};
parentWindow.Show();
var childWindow1 = new Window { Owner = parentWindow, Title = "Child1" };
childWindow1.Show();
var childWindow2 = new Window { Owner = parentWindow, Title = "Child2" };
childWindow2.Show();
}
Being TopMost prevent this happening
If parent window is TopMost, this wouldn't happen. But this may not be an option in many cases.
parentWindow.Topmost = true;
Workaround
Activate the parent on child2 close.
childWindow2.Closed += (a, b) => { parentWindow.Activate(); };
This is due to the fact that you can't use the parent window once the child window is shown with the Owner set. Just try to access the parent window while the child is on the screen, and it won't let you, to see what i mean.
If you don't specify Owner then this behaviour doesn't happen.
I never worked out what is causing this to happen! In the end, I wrote this code that is called after any child window is closed:
// App inherits from Application, and has a Window property called MainWindow
// and a List<Window> property called OpenWindows.
if (!App.OpenWindows.Any())
App.ParentWindow.Activate();
So if the last child window is closed (making App.OpenWindows.Any() false) the parent window is activated. This does not result in flicker (the main window minimizing then maximizing, for instance.)
This isn't the best solution. I will not close this question in the hope that someone can explain why WPF has this functionality!
Related
I'm pretty new to WPF and the Metro styling and I ran into the following problem:
I open all my message dialogs from the main window of my application by calling ShowMessageAsync()., even when the actual caller is another window (e.g. a settings window). Now when for example this settings window is open and wants to show a message that the settings have been saved, the dialog appears in between the main and the settings window (which makes sense because the main window is the caller and the settings window is a child of that main window).
What I want is for all dialogs created by ShowMessageAsynch() to be displayed always in front of the screen.
Any ideas on how to achieve that?
Thanks in advance for your answers!
I know this is an older question but I could not find a good answer for this anywhere. I ended up minimizing any child windows, opening the message dialog, and then setting the child windows back to normal. I would still be curious to know if there is a better solution for this problem.
private MetroWindow MetroWindow => (MetroWindow)Application.Current.MainWindow;
public async Task ShowInfoDialog(string text)
{
var childWindows = MetroWindow.OwnedWindows;
MinimizeChildWindows(childWindows);
await MetroWindow.ShowMessageAsync("Info", text);
MaximizeChildWindows(childWindows);
}
private void MinimizeChildWindows(WindowCollection childWindows)
{
foreach (Window win in childWindows)
{
win.WindowState = WindowState.Minimized;
}
}
private void MaximizeChildWindows(WindowCollection childWindows)
{
foreach (Window win in childWindows)
{
win.WindowState = WindowState.Normal;
}
}
I've seen many questions regarding similar issues, but I haven't found one quite specific enough to my question to find an answer yet.
I allow the user to move the MainWindow of my program around and, if they have one, onto another monitor. When they click to add something on the MainWindow, a new Window is created so they can fill in a form.
The problem is that this new Window will start on the primary screen of the OS instead of starting on the screen that the MainWindow is currently located on.
I had a look at this question; How to make a dialog(view) open up on the same monitor as the main window and saw that setting the owner to the MainWindow worked in this case. So I added
Owner = Application.Current.MainWindow;
into the Window_Loaded method the new Window. This does load the Window on the same screen as the MainWindow but then crashes, stating Cannot set Owner property after Dialog is shown.
Naturally I move the setting of the Owner property to before the new Window is shown, so it looks like this;
contractAddwindow.Owner = Application.Current.MainWindow;
contractAddwindow.ShowDialog();
however this then has the same issue as before, the contractAddWindow starts on the primary screen.
So my question is, how do I force the new Window to load on the same monitor as the MainWindow instead of the primary screen?
You can use WindowStartupLocation:
From XAML:
WindowStartupLocation="CenterOwner"
From Code:
WindowStartupLocation = WindowStartupLocation.CenterOwner;
#EDIT: Consider trying Nawed's answer (see below) before using this one. His is much simpler.
Try this methods:
using System.Windows;
using System.Windows.Forms; // Reference System.Drawing
[...]
public void SetWindowScreen(Window window, Screen screen)
{
if (screen != null)
{
if (!window.IsLoaded)
{
window.WindowStartupLocation = WindowStartupLocation.Manual;
}
var workingArea = screen.WorkingArea;
window.Left = workingArea.Left;
window.Top = workingArea.Top;
}
}
public Screen GetWindowScreen(Window window)
{
return Screen.FromHandle(new System.Windows.Interop.WindowInteropHelper(window).Handle);
}
And then, call it like this from the constructor of the Window you want to show on the same monitor as the MainWindow:
SetWindowScreen(this, GetWindowScreen(App.Current.MainWindow));
Well, I'm writing a simple application which will have multiple forms inside it. Now say one form will be the base window i.e. the parent and all other will be child of it. So for this I'm trying to opening the child window by below method.
//Inside class FormBaseWindow
private void linkLabelReservation_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
Point childLocation = new Point(this.Location.X + 100, this.Location.Y + 120);
FormReservation formReserve = new FormReservation();
formReserve.Location = childLocation;
formReserve.MdiParent = this.MdiParent;
formReserve.Show();
}
And also set the IsMdiContainer attribute of FormBaseWindow class to true. Now the things is, I want to have feel like dragging the FormBaseWindow window will drag the whole thing including the child window inside it. Currently once I click on the FormBaseWindow window its coming foreground and the child is going behind it.
In MFC I was able to do it by setting the window style Child and set it as a child window, but here the story is bit different.
I'm using C# Winforms with VS 2012.
Change :
formReserve.MdiParent = this.MdiParent;
to
formReserve.MdiParent = this;
NB: You should use
formReserve.MdiParent = this.MdiParent;
when you open a child form from another child form to make it under the same MdiParent.
I have a main Window.Then if you touch settings button a new modal Window is shown with some options..Then you click an option and a new Window (lets call it "Settings") is shown.Both windows have as owner the main Window. Our new window(Settings) may open new window having itself as owner for example if you want to add a new staff member.And here is the problem. When you close the new window and then close Settings window the main Window is minimized... However this doesnt happen if Settings window dont open any other window...
this is how i show the forms
UserForm f = new UserForm();
f.Owner = this;
f.Show();
Use MainWindow.Activate() on close of Child Window.
it should work
I ran into the same problem and found an apt solution here.
Set the window owner to null before the Settings window closes.
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
Owner = null;
}
Can't you catch when the window is closed (OnFormClose I think) and then unminimise your main window?
I'm creating a wpf application in c#, I know to close/open a window you have to use the .Close() and .Show() methods but for some reason the home screen, the first window that appears when I launch the application, won't close.
Home window1 = new Home();
window1.Close();
Name window2 = new Name();
window2.Show();
Window2 appears, but window1 won't close. What's the problem.
Where is your code for showing window1? If you show your home window somewhere else in your code, you need to use that reference in order to close it. Making a new Home object and calling its Close method will not close a window shown using another Home object.
Presumably because if you close the window you'll close the application.
If you just want to hide the main window use the window.Hide() method.
This from the help on Window.Close:
A Window can be closed using one of
several, well-known, system-provided
mechanisms located in its title bar,
including:
ALT+F4.
System menu | Close.
Close button.
A Window can also be closed using one
of several well-known mechanisms
within the client area that are
provided by developers, including:
File | Exit on a main window.
File | Close or a Close button on a
child window.
UPDATE
Tormod Fjeldskår has a good point in his answer. I assumed that the code was given as an example rather than being what was actually being used.
This is a bug in WPF. Window.Close will fail silently if the SourceInitialized event has not yet occurred. Subsequent calls to Window.Close will also fail.
https://connect.microsoft.com/WPF/feedback/ViewFeedback.aspx?FeedbackID=299100
For a workaround, add this to your Window:
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
// check if we've already been closed
if (m_bClosed)
{
// close the window now
Close();
}
}
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
// make sure close wasn't cancelled
if (!e.Cancel)
{
// mark window as closed
m_bClosed = true;
// if our source isn't initialized yet, Close won't actually work,
// so we cancel this close and rely on SourceInitialized to close
// the window
if (new WindowInteropHelper(this).Handle == IntPtr.Zero)
e.Cancel = true;
}
}
bool m_bClosed;
Or you could have Window2 be the main window (you can change this in app.xaml in the StartUpUri property) and either have Window2 show and close Window1 or not show Window1 at all.
<Application x:Class="Invitrogen.TheGadget.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window2.xaml">
</Application>