I have this click event in my main window to open a new window
private void Button_Click(object sender, RoutedEventArgs e)
{
cm = new CanalesMain();
cm.Show();
cm.Canales.setValues();
}
My cm variable is defined as a member class in my main window because I need to load/refresh the setValues() method every 5 minutes (there's a TimeSpan and a EventHandler for that)
The thing is, in my "refresh data" method I have this if statement to ask if the cm variable is loaded and is not null (I mean, if the window was ever opened or if is opened, ask if isn't closed)
if (cm!=null && cm.IsLoaded)
{
cm.Canales.setValues();
}
Is this the correct or best way to ask if my window is open?
Strictly speaking no, it's not the right way. IsLoaded doesn't mean that Window is visible, just loaded (even if this may be equivalent in most of scenarios but it means this window has been created once, it has a handle, no mention to its visibility).
What you have to check is the Visibility property (it's what, finally, Show() will change), it'll be Visible if the Window is currently visible or Hidden if it has not been loaded (or it has been loaded and it still is but actually hidden).
To summarize:
if (cm != null && cm.Visibility == Visibility.Visible)
{
}
Please note that if Window is visible then it's implicit it has been loaded (it has a handle) but it's not true the vice-versa (a loaded window may not be visible and maybe it was even not in the past).
There is another way of checking which Windows are currently active:
foreach (Window window in Application.Current.Windows)
{
// Check for your Window here
}
If your Window is of a particular type, then you can do this instead:
foreach (Window window in Application.Current.Windows.OfType<YourWindow>())
{
// Do something with your Window here
}
Your Window will not appear here before it is displayed.
I think extension method will be very useful in that case, try this:
public static class WindowsExtensions
{
public static bool IsOpened(this Window window)
{
return Application.Current.Windows.Cast<Window>().Any(x => x.GetHashCode() == window.GetHashCode());
}
}
Which gives you ability to make call on every window like this:
var wind = new ChildWindow();
wind.ShowDialog();
var isOpened = wind.IsOpened();
Also you can check out this: How do I know if a WPF window is opened
More info about Application.Windows
If you call myWindow.Show() and myWindow.Close(), myWindow.IsLoaded should get you a value that you can use to indicate if the window is open or not.
Related
Is there any way I could close all windows after the user closes one of them, and here is the important part: without using App.Current.Shutdown(), but rather by invoking close on remaining windows individually?
My idea was to just invoke close on each of the remaining windows in the Window_Closing event handler method. There's one problem, though. Suppose I have two window classes, A and B. I create one instance of A and B - a and b respectively. If I close window A, then it invokes the Window_closing event handler method and calls b.close() there. Then in the B class (A and B are window classes, they both inherit from Window) Window_closing method is invoked (because I've just called B.close()), and B::Window_closing calls a.close() and it results in exception cause I've already closed a.
What is the right way to solve this?
If you are interested in having a "main window" and "tool windows", so that closing the main window closes all of them and closing tool windows does, well, only just that - then in the App.xaml there's a friendly option just for that!
That's Application.ShutdownMode
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml"
ShutdownMode="OnMainWindowClose"
>
</Application>
Note that here, the "MainWindow" in question, is the main window the application starts with (or the one you set as main, if you played with that during the app's lifetime).
EDIT: afterthought: That will of course close all the windows, but I'm not actually sure if it calls normal "Close", or just shutsdown the whole application. Sorry, you'd need to check it for yourself. If you're interested in my opinion, that's the way you should/could do that easily, and if you really-really-need to shutdown the app by "Close()"ing every window, then I sense you're doing something wrong here, as if I remember correctly, "Window.Close()" may be cancelled.
EDIT2: yup, Window.Close() can be cancelled. Please see this article:
Closing a window causes the Closing event to be raised. If the Closing
event isn't canceled, the following occurs: (...)
So looping over the window collection and calling 'Close' doesn't really guarantee that the windows will really be closed and the app may still be left running afterwards.
Something like this:
private static bool WindowsClosing;
public static void CloseAllWindows()
{
if(WindowsClosing) return;
WindowsClosing = true;
var windows = Application.Current.Windows;
foreach (var wnd in windows.OfType<Window>())
{
wnd.Close();
}
}
And call that method from your Window_Closing (or rather Window_Closed if you don't need to cancel) events
Handle Window.Closedevent instead of Window.Closing which would be closing all the opened windows other than MainWindow. I am not calling Close method for MainWindow because this is the main thread which will cause application terminating.
private void Window_Closed(object sender, EventArgs e)
{
var windows = Application.Current.Windows;
foreach (var item in windows)
{
if ((item as Window).Title.ToLower() == "mainwindow") continue;
(item as Window).Close();
}
}
In pseudocode, this is what I am trying to do from a main window, with many non-modal sub-windows that can be opened and closed independently of the main window.
(Think "preferences" or "find")
On pressing "OPEN WINDOW"
STEP 1: If window does not exist, create it.
STEP 2: Window now exists, so bring it to the front & make it visible.
(Step 2 is NB in case OPEN WINDOW is pressed while window is already open - I don't want multiple instances of it, just bring it to the front.)
On pressing "CLOSE WINDOW"
STEP 3: Close the window
ALT STEP 3: Hide the window
This is the code I have tried. I got as far as being able to open the window, and bring it to the front if OPEN WINDOW is pressed again while the window is open. However, once I close the window, I CANNOT get it to open a second time. I get an error stating that Window.Show() cannot be used once the window is closed.
public static void OpenWindowOnce(Window windowToOpen)
{
foreach (Window n in Application.Current.Windows)
{
//Checks if the window is already open, and brings it to the front if it is
if (n.GetType() == windowToOpen.GetType())
{}
else
{ windowToOpen.Show(); }
}
windowToOpen.Activate();
}
Where am I going wrong in my code/logic? Thank you, I am pretty new to coding and have spent weeks trying to get this right.
You cannot use a Window that has been closed because its resources are disposed at that time. The solution is to simply create a new Window each time that you want to display it.
I can see that you are passing in a Window object and then trying to find the particular type of Window... in this case, you can use reflection to instantiate your Windows from their Type. In particular, please see the Activator.CreateInstance Method page on MSDN. You could use it something like this:
public static void OpenWindowOnce(Window windowToOpen)
{
foreach (Window n in Application.Current.Windows)
{
//Checks if the window is already open, and brings it to the front if it is
if (n.GetType() == windowToOpen.GetType()) { ... }
else
{
windowToOpen = Activator.CreateInstance(typeof(YourWindow)) as YourWindow;
windowToOpen.Show();
}
}
windowToOpen.Activate();
}
In my winforms application I am trying to get a main window handle, so I can set it as parent to my wpf modal window. I am not too experienced with winforms, so after a bit of googling I found two ways to get it.
System.Windows.Forms.Application.OpenForms[0].Handle
System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle
(1) seems to always return the same value which appears to be correct (at least my modal window behaves as expected), while (2) sometimes returns the same value as (1), but sometimes - an entirely different pointer, which does not seem to work (my modal window appears on top of every other window, not just the parent window).
Can someone explain the difference between the two methods? Is it normal that sometimes they return different results?
Edit:
In case anyone else is wondering: once you get the handle, you can use it by creating WindowInteropHelper class:
public static void SetInteropParent(this Window wpfDialogWindow, IntPtr winformsParentHandle)
{
new WindowInteropHelper(wpdDialogWindow) { Owner = winformsParentHandle };
}
It is certainly not unusual for Process.MainWindowHandle to return the wrong handle. The Process class has to make a guess at which window is the "main" one. There is no mechanism in the native winapi to designate a window as such. So Process makes a guess that the first window is the main one. This has a knack for going wrong in apps that use a splash screen or a login dialog, etc, or create a window on another thread.
Application.OpenForms doesn't have this problem, but has a failure mode, it will lose track of a window when it is recreated. Which happens when the program changes certain properties of the Form that can only be specified when the window is created. The ShowInTaskbar, TransparencyKey and Opacity properties are the most common troublemakers.
The most reliable way is to override the OnHandleCreated() method of the form you want to be the parent. Which is called whenever the Handle property changes. Do note that you want to make sure that this doesn't happen while your WPF window is active, that will kill the WPF window as well. Otherwise easy to observe of course :)
protected override void OnHandleCreated(EventArgs e) {
base.OnHandleCreated(e);
SetWpfInteropParentHandle(this.Handle);
}
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>
I have a C#/.NET app and I want to implement the following behavior:
I have a popup menu. Whenever the user clicks on anything within the application that is not the popup menu, I want the popup menu to close.
However, whenever a user is not in the application I don't want anything to happen.
I'm trying to manage this through the LostFocus event, but I'm having trouble determining whether my application is the active window. The code looks something like this.
private void Button_LostFocus(object sender, System.EventArgs e)
{
if (InActiveWindow()) {
CloseMenu()
}
else {
// not in active window, do nothing
}
}
What I need to know is how to implement the InActiveWindow() method.
You could P/Invoke into GetForegroundWindow(), and compare the HWND returned to the application's form.Handle property.
Once you have the handle, you can also P/Invoke GetAncestor() to get the root owner window. This should be the handle of your application's main, startup window, if this is in your application.
I stumbled upon your question while working on a project and based on Reed Copsey's answer, I wrote this quick code which seems to do the job well.
Here's the code:
Public Class Form1
'''<summary>
'''Returns a handle to the foreground window.
'''</summary>
<Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function GetForegroundWindow() As IntPtr
End Function
'''<summary>
'''Gets a value indicating whether this instance is foreground window.
'''</summary>
'''<value>
'''<c>true</c> if this is the foreground window; otherwise, <c>false</c>.
'''</value>
Private ReadOnly Property IsForegroundWindow As Boolean
Get
Dim foreWnd = GetForegroundWindow()
Return ((From f In Me.MdiChildren Select f.Handle).Union(
From f In Me.OwnedForms Select f.Handle).Union(
{Me.Handle})).Contains(foreWnd)
End Get
End Property
End Class
I didn't have too much time to convert it to C# as I'm working on a project with a deadline in 2 days but I believe you can quickly do the conversion.
Here is the C# version of the VB.NET code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
///<summary>Gets a value indicating whether this instance is foreground window.</summary>
///<value><c>true</c> if this is the foreground window; otherwise, <c>false</c>.</value>
private bool IsForegroundWindow
{
get
{
var foreWnd = GetForegroundWindow();
return ((from f in this.MdiChildren select f.Handle)
.Union(from f in this.OwnedForms select f.Handle)
.Union(new IntPtr[] { this.Handle })).Contains(foreWnd);
}
}
}
It seems like the biggest reason this is tricky is because the popup loses focus before the main form is deactivated, so the active window will always be in the application at the time of this event. Really, you want to know whether it will still be the active window after the event is over.
You could set up some kind of scheme where you remember that a popup is losing focus, set aside the fact that you will need to close it, and in the LostFocus or Deactivate event of the application's main form cancel the note that tells you you need to close it; but the problem is when will you process the note?
I'm thinking it might be easier, at least if the popup is a direct child of the main form (which I suspect in your case it may be) to hook the Focus or maybe even Click event of the main form and use it close the popup if it is open (perhaps by scanning its list of child forms for any that implement an ICloseOnLostFocus interface, so that the popup will have a chance to participate in the decision and do anything else it needs to do).
I wish I knew of a better document explaining what all these events actually mean and how they are sequenced with respect to one another, MSDN leaves a lot to be desired in describing them.