ShowDialog(IWin32Owner) from Class Library - c#

I have a class library that holds a "MessageBox" equivalent with a few more bells and whistles.
If I call the ShowDialog(IWin32Owner) method, this works, and the form will display in the centre of my parent form.
Sometimes, however, this form is invoked from a class in my project, and so I don't have access to the form owner. In this situation, I can pass null to the ShowDialog() method, however it appears this doesn't recognize the "Currently Active Window" and display it in the centre. I am assuming because it is in another class library.
Is there any way then to get the currently active form (or at least the screen) the user is working on?
EDIT
Ok this is more to do with the FormStartPosition Enumeration.
If I use CentreScreen this should default to the currently active monitor as per MSDN. However this seems to default to the default monitor if the form is in a class library.
Ok:
This is the code in question: It Fails to set the form to centre screen:
public static DialogResult ShowYesNoCancel(string message)
{
using (frmMessage form = new frmMessage())
{
form.Text = #"Input Required";
form.lblMessage.Text = message;
form.btnNo.Visible = true;
form.btnOK.Text = #"Yes";
form.btnOK.DialogResult = DialogResult.Yes;
form.StartPosition = FormStartPosition.CenterScreen;
return form.ShowDialog();
}
}
A solution:
/// <summary>
/// Overridden to ensure its in the centre of the current screen
/// </summary>
/// <returns></returns>
public new DialogResult ShowDialog()
{
Screen current = Screen.FromPoint(MousePosition);
Rectangle s = current.WorkingArea;
StartPosition = FormStartPosition.Manual;
Location = new Point(s.Left + s.Width / 2 - Width / 2, s.Top + s.Height / 2 - Height / 2);
return base.ShowDialog();
}

this should default to the currently active monitor
Problem is: which is the "currently active monitor" if you have more than one? If you have two or more then that gets to be a muddled question, a secondary monitor isn't more or less "active" then the primary one, it is equally capable of displaying windows.
The heuristic that Winforms uses is "the monitor that displays the mouse cursor". The underlying call is:
Screen desktop = Screen.FromPoint(Control.MousePosition);
If you want to emulate the behavior that MessageBox.Show() uses to find an owner then write the code so it finds the currently active window. The underlying winapi call is GetActiveWindow(). Which ShowDialog() already uses so there is probably more going on than meets the eye, like an active window that is not a Winforms window. Which the native MessageBox() winapi function doesn't mind, but ShowDialog() does. Use Spy++ to diagnose this.

You can use Form.ActiveForm:
Gets the currently active form for this application.
However, the fact that your class library is showing this message on its own without already knowing about the UI is probably not a good sign.

Form.ActiveForm will only work if any form of your application has the focus. So if your application is in background, that will be of no help.
Use Application.OpenForms instead. Retrieve the last item in the collection, which represents the last openend form.
Caution with special form properties, like ShowInTaskBar... they might not show up in this collection! Described here:
https://stackoverflow.com/a/3751748/2243584

Related

WinForms main window handle

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);
}

WPF window should be modal to native owner window, but isn't

I have the following C# code in a WPF project:
private static void RunConfig(string owner)
{
long ownerHandle;
var settingsWindow = new SettingsWindow();
if (long.TryParse(owner, out ownerHandle))
{
WindowInteropHelper helper = new WindowInteropHelper(settingsWindow);
helper.Owner = new IntPtr(ownerHandle);
}
settingsWindow.ShowDialog();
}
The SettingsWindow isn't properly modal to the owner window (i.e. I can focus on, interact with, and even close the owner window while the SettingsWindow is still open). What am I doing wrong?
For context, this code is part of a screen saver program, and the owner window is the Control Panel screen saver selection window (which passes in the handle to use as owner via command line parameter). I know the IF statement is evaluating true and correctly parsing the handle.
I have also tried using the SetWindowLongPtr method from user32.dll (compiling for x64, hence not using SetWindowLong) which is briefly described here and shown in use here. This method works in WinForms, but doesn't seem to work here in WPF. Help me Obi-Wan Kenobi, you're my only hope.
It turns out that using WindowInteropHelper to set the native window as owner of the WPF Window does work, it just doesn't do the whole job. When set this way, the WPF Window will remain visible on top of the native window, even if the native window has focus. However, that is the only effect obtained. The WPF Window does not prevent interaction with the native Window, and the native window can even be closed, without the WPF Window closing or being affected.
In order to get the rest of the desired behaviour, we need to use the EnableWindow function in user32.dll to disable the native Window before calling ShowDialog on the WPF Window, and again to re-enable it once the WPF Window closes.
The modified code looks like this:
private static void RunConfig(string owner)
{
long ownerHandle;
var settingsForm = new SettingsWindow();
if (long.TryParse(owner, out ownerHandle))
{
WindowInteropHelper helper = new WindowInteropHelper(settingsForm);
helper.Owner = new IntPtr(ownerHandle);
NativeMethods.EnableWindow(helper.Owner, false);
settingsForm.ShowDialog();
NativeMethods.EnableWindow(helper.Owner, true);
}
else
{
settingsForm.ShowDialog();
}
}
(Note: The above code is correct in general, but incomplete in the case of screen savers, which is what this code is actually being used for. In the case that this code is being used for the config window of a screen saver, the string passed in for the owner handle is not the handle of the Control Panel window to be used as owner, but rather a handle for a control that is a child of the Control Panel window. The extra step in this case is to get the handle of the parent of that control. We can do this by calling GetParent, also in user32.dll, on the passed-in handle. This will return the real handle we want to use for the owner and EnableWindow calls.)
If anyone from Microsoft ever finds this, maybe consider modifying WindowInteropHelper to properly set all of this up when Owner is assigned and ShowDialog used, since this is the proper complete behavior for modal windows.

Why calling Hide in a child form's FormClosing event handler hides the parent form?

When Form2 is closed, via it's X button, the Main form is sometimes hidden as well, but not always. Often times the Main form is hidden after initial 'newForm' button click and other times many open-close operations are required before the Main form gets hidden on Form2's closing. Why is this happening? Why is it irregular?
This is a small test code for a larger application I'm working on. In that application a thread continuously reads the network stream and when a particular message is encountered a modal form is displayed. The user can close that modal form or it can be told to close itself by a different network message. In this event, to give the user some time to view the data that the form is displaying I implemented a delayed form closing for that form. When the form is running its delay closing code, another message can come in over the network that will open up a new instance of this form in which case, I observed, that once the timer of the original form runs out, the original form is left displayed until the new instance is closed. Calling Hide in the FormClosing event handler closes the original form if more than one instances of it are running, but it has this side effect of hiding the entire application (the Main form) when the last instance of this form is closed, either by the user or by the delayed closing code. And again, the entire application is not always hidden, but it does happen.
//Main form's 'newForm' button
private void btn_newForm_Click(object sender, EventArgs e)
{
Form2 f = new Form2();
f.ShowDialog();
}
public partial class Form2 : Form
{
private void Form2_FormClosing(object sender, FormClosingEventArgs e)
{
Hide();
}
}
Update (from the application I'm working on):
The problem is shown visually below. The top part of the picture, labeled "A", represents the situation where the first modal dialog (greyed out) was instantiated and it is in the process of being auto closed after 4 seconds have elapsed. The second instance (blue window heading) is active and awaiting input. In the lower part of the picture, labeled "B", the counter to closing of the first instance has completed, yet the first instance remains visible. Adding Hide does not change picture "A" but picture "B" would only be showing the active modal dialog, which is what I want. If Hide is not used and we have the case shown in "B", once the active modal dialog is closed the inactive dialog will disappear together with the active one, but no sooner. At this time my main form will be hidden as well, sometimes.
Your main form doesn't get hidden, it disappears behind another window. The flaw in your code is that for a brief moment none of your windows can get the focus. Your main window can't get the focus, it got disabled by your dialog and won't get re-enabled until the dialog is fully closed. Your dialog can't get the focus, you hide it. So Windows goes looking for another window to give the focus to and can only pick a window owned by another application. Like Visual Studio, nice and big so your main window is well covered by it.
Not sure what you are trying to do, it doesn't make sense to call Hide() since the dialog will close a couple of microseconds later. Just delete the statement.
I am not sure if I am right but maybe you forgot to add e.Cancel = true; to your closing event.
Second, I think using a modal form is only usefull when you expect an action like OK or CANCEL from user, that is where DialogResults comes handy. It sounds strange if this happens time to time not all the time! maybe you can try like this:
//Main form's 'newForm' button
//Define form in your mainform
private Form2 f;
private void btn_newForm_Click(object sender, EventArgs e)
{
if(f != null) { f.Show(); return; }
f = new Form2()
f.FormClosing += delegate { f.Hide(); };
f.Show();
}
I know the topic is quite old, but I recently had to look for answers for this precise question.
Why hiding the (child modal) form instead of closing it ?
I may be wrong, but I think that in some cases, hidding the modal child form instead of closing it is sometimes useful.
For example, I'm using a class that is a custom tree of grids. Think of something like an Excel Document with multiples tables (sheets) and each table can have child tables. A very powerful manner to store datas that can be used by multiple objects and multiple forms at a time.
Now, this "TreeTable_Class" object has an inbuilt custom form that actually shows the content of one of its tables at a time in a GridView, and you can select which table to show by selecting it in a Treeview. You can see here that the "Database Editor" is actually and MDI Form that can load the Form from any TreeTable_Class.
And this is the Form I use to edit the content of a Cell for a given (selected) Table (I've chosen another cell with long text content from another table in this database)
Now, when you choose to close the custom form instead of hiding it, that form will be unaccessible, you can't show it anymore, and you get an exception (no instance of the object) Somewhat, it isn't disposed yet (so the check If MyForm Is Nothing Then... is useless) I know I have to implement the GarbageCollector and dispose the Child Form manually, but it's outside the scope of this topic.
Anyway, my class could use a large amount of memory, of datas, and if I had to rebuilt ALL the contents each time I want to show a new instance of that form, that would be a large amount of workload in my application. That's why I have chosen to hide the form instead of closing it until the main application exits or when a specific CloseFormAndDispose() method is explicitly called, either by the program, or if I make this option available for the user via an user interface.
Workaround try :
This is the workaround I've found to override the "form replaced by another because none of the parent/child ones could be retrieved" :
Sorry, I'm in VB.. but you can use a tool to convert this to C#, or do it manually, it's pretty simple..
// This is the child, a Cell Editor that can edit the content of a Cell.
Protected WithEvents _CellEditor As CellEditor_Form = Nothing
This Editor form is a member of TreeTable_Form, a form that can actually show and edit the content of the whole Database File (a single file)
And this TreeTable_Form class contains a routine that handles CellEditor closing event
Public Partial Class TreeTable_Form
// Sorry. The "WithEvents" in C# is a litte bit complex to me... So, in VB :
Protected WithEvents _CellEditor As CellEditor_Form = Nothing
// ...
// CellEditor handling method (I used a Code converter...) :
// The original VB declaration is :
// Protected Sub RecallFormAfterCellEditorHidden() Handles _CellEditor.Closed
// You'll have to write specific Event handler for _CellEditor object declared above...
protected void RecallFocusAfterCellEditorHidden()
{
Application.DoEvents();
this.Focus();
}
End Class
This tiny protected void RecallFormAfterCellEditorHidden() method in your Class (if you are using a class that contains Forms) or in your Main From, assuming that your main form contains the child forms (dialogs) will try to force the focus on your Application or MainForm...
By the way, TreeTable_Form is actually a component of TreeTable_Class. The later is an object that can be used anywhere you want. In a Main Form Application, in another Class, in a dialog, anywhere... and could be passed by reference to share its contents between several items. And this TreeTable_Class contains a RecallFocusAfterTreeViewerHidden() method that handles the closing of that form. That means, the Form or Application that actually uses the class will get the focus each time you close the its child Form. I've made it that way to get an object that could be used anywhere
We still get problems !
However, this method will make your application flicker a bit each time you close your child dialog, and doesn't succeed at 100% ! Sometimes, my parent form still disappear from screen and gets struck behind another window. Alt+TAB wont helt either. But this happens less than without this method trick. I don't have any better answer at this time, still searching... I'll come back here if I find out how. I'm using this custom made application in my work to write memos during meetings for example, and produce PV (procès verbal - french sorry) in PDF or DOCx on the fly...
And I'm sorry, I'm in VB, not C#. Hope this helps a little, until we find a better workaround for this...

c# - create invisible user control

I need to create a user control in C#.Net, which can be added to the application without being visible - just like the FolderBrowserDialog. It's a new window which I'll be using often so I think this is the right way. The window will be opened by envoking the showDialog-Method as known from the other dialog.
Any Idea?
Thanks and regards,
Daniel
Since all these "invisible" controls derive from Component class, you should start by reading the MSDN article on it: http://msdn.microsoft.com/en-us/library/system.componentmodel.component.aspx.
simply set Visible to false or isn't this what you're asking for ?
A UserControl is by definition not a Form; I think what you really want is a Component. That said, couldn't you really just create a new Form class that has the functionality you want? Whenever you want to display it, create a new instance and call ShowDialog. Or, if you want to preserve state, add an instance as a class member to your parent form, call its Show method whenever you want to display it, and add an event handler to its FormClosing event to check:
if (e.CloseReason == CloseReason.UserClosing)
and, if so,
e.Cancel = true;
Hide();
(This last part is to prevent errors if the user closes the form and then tries to display again after it's been disposed.)
I think more information may be needed, but if your crating a custom user control, the control should have a .Visible property. The follow is an example of how a button can be located on the form but hidden from a user.
button.Visible = true; // shows the button
button.Show(); // Shows the button
button.Visible = false; // hides the button
button.Hide(); // Hides the button
While the button may still be on the form/control, it will not be interactible by the user. You can still perform programmatic control on the button, but essentially it is not a user control while it is 'hidden'. If you want there to be a sort of hidden button that the user can click you will need to do other things to obtain this but It doesn't should like that is what you want.
This show/hide thought process sounds a lot like pains and confusion leftover from classic VB. The old form methods of show and hide, etc., were confusing and often left me as a developer in a position to not know whether an object existed or if was merely invisible. And checking was only trivial if you used On Error Goto to prevent a null reference. So right off I would advise not to think in terms of visibility unless you are doing something with a web page and need to maintain space and state.
First, create a Windows form and add it to your project, assuming that is the type of project that you are describing. Decorate the form with the proper controls, and where applicable, create properties to allow public access to the control values. Also set the DialogResult property of the buttons that either "OK" or "Cancel" the form. Give it an appropriate border style of either Fixed3D or FixedDialog. Maybe also set the property for where you want the form to appear on startup -- center parent, center screen, Windows default, etc. The event handlers for both "OK" and "Cancel" should invoke this.Close(); to close the window.
From the calling point in the code, here's some hypothetical code to get you going in the right direction. Write something like this in the place where you want to invoke your Dialog.
int intResult = 0;
string strResult = null;
MyDialogForm frm = new MyDialogForm();
frm.Title = "Select an Item";
frm.SomeProperty = 0;
frm.SomeOtherProperty = true;
if (frm.ShowDialog() == DialogResult.OK)
{
intResult = frm.Result;
strResult = frm.StringResult;
}
else if (frm.ShowDialog() == DialogResult.Cancel)
{
// User clicked the cancel button. Nothing to do except maybe display a message.
MessageBox.Show("Canceled Task");
}
...
// Somewhere further on down, but within scope, simply repeat
// what you just did, but without having to reinstantiate the
// form Window. But if you make it that far within the same
// scope, this method might be too busy and may need to be
// factored down.
So in short:
Scrap show/hide -- its not a good
practice.
Save the form data without
using an invisible form to save it;
that's the class's job.
If the UI requires a lot of flipping back and
forth between windows, check your
design for other alternatives for
solving the original problem. Maybe a design pattern such as MVC is for you, depending upon the size and complexity of your application.
Sound good?
You can put that control in a Panel. Set the panel height = 0 visible = false when you dont want to show the control.
And do the vice versa when you want to show it.
Derive from Control, not UserControl, and in the constructor set Visible = false.
Also create an event handler in the constructor.
VisibleChanged += new EventHandler(SetVisibleFalse);
Create a method named SetVisibleFalse.
private void SetVisibleFalse(object sender, EventArgs e)
{
if (Visible) Visible = false;
}

Determine Whether Program is the Active Window in .NET

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.

Categories

Resources