i've a WPF openned from a button in the ribbon of Microsoft Outlook, for an addin i'm developing.
private void _HandlerUserSettings(Office.CommandBarButton ctrl, ref bool cancel)
{
var windowUserSettings = new WpfUserSettings();
windowUserSettings.Show();
}
if i close the WPF from its close button, if i click again the button to handle the show of the window, it doesn't work.
i guess the close action does something more than a simple Hide, so my goal is to override the closing action. i tried to work on Closing method *WpfUserSettings_Window_Closing* of my xaml window:
<Window x:Class="TrainingForgeOutlookAddin.View.WpfUserSettings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="350" d:DesignWidth="700" Title="User Settings"
WindowStartupLocation="CenterScreen" Loaded="WpfUserSettings_OnLoaded"
Closing="WpfUserSettings_Window_Closing">
....
</Window>
do you have any suggestions or howto to read, that i didn't find?
thank you
As far as I know, the close function will destroy the window as you cannot call
window.Show()
multiple times. This means that all the data in the window has been lost. This makes sense, since a Window is just like your normal window that hosts the root of your application. The way to do this, is to simply create a whole new window before calling .Show(). However, this creates the issue that your data is not saved. Once your open your new window, it will be blank again. While this is not quite part of your question, I think some might find this useful.
For this you will need to make the two Windows talk to each other in order to save data. The parent will hold onto the child's data and then give it to the newly created window to repopulate.
Some people prefer delegates, I prefer to use interfaces for this.
Define a simple interface. This sets up the relationship between the child and the parent window and allows the child to inform it's closing and give it's data back for safe keeping.
public interface IParentWindow
{
void ChildClosed(SomeData data);
}
this will inform the parent window that the child has been closed and now the window can hold on to the data until the window is opened again.
Assign this to the parent window:
public partial class MainWindow : Window, IParentWindow
this will force you to implement the ChildClosed function. Before we do this, you will notice that I have defined SomeData. This can be anything you like as long as both windows use this to send data back and forth.
For me, the implementation of SomeData looks like this:
public struct SomeData
{
public string name;
public string email;
}
We can debate on why I made this a struct, but that is besides the point. You can make this a class if you would like.
Next, implement the child closed function. For this, create a SomeData object in your parent class scope:
SomeData lastWindowData;
And this function can how tell the parent to hold on to it's data
public void ChildClosed(SomeData data)
{
lastWindowData.email = data.email;
lastWindowData.name = data.name;
}
Ok, before we move onto the child window, we have to implement the opening of the window. Since, as I have found (please tell me there is another way around this?), that the Window cannot be reopened once closed. This means that we have to make a whole new window. Put this in your event, whichever opens the window:
childWindow = new ChildWindow(lastWindowData, this);
childWindow.Show();
as you will notice, the window takes the data and "this", which is our parent window. It does this so that it is able to call back to the parent window before being closed and allow the parent window to save the data.
Lets move onto the child window.
Modify the child window's constructor to accept a SomeData and an IParentWindow object. Looking back, the IParentWindow is the window which is opening the child.
public ChildWindow(SomeData data, IParentWindow parent)
Now we get to save the data passed from the parent!
Define a data holder for your window which is the some SomeData object (inside the child window class)
SomeData myData;
and in the constructor
myData.email = data.email;
myData.name = data.name;
so we can repopulate everything. You can figure out the re population part for yourselves. This way, the window has now received the data of the previously closed child window and we can restore it as though it's the previous window!
When you do something in the window (could be anything, anywhere), you can mutate your data. Say I assign a name and email:
myData.email = "gordon#freeman.com";
myData.name = "Gordon Freeman";
Finally but not least, once we close the child window, we can inform the parent window that the child is being closed and we can give it the newly updated data to save once again!
protected override void OnClosed(EventArgs e)
{
parent.ChildClosed(myData);
base.OnClosed(e);
}
rinse and repeat!
NOTE; For the first open, before you have any saved data, you can pass in an object with null variables. You can then, in the child window, figure out whether you need to update the UI or not based on this.
Related
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 am getting the error message Cannot access a disposed object.
Object name: 'ApplicationProperties'. when I tryo to re-open a form after closing it. I have noticed this is from exiting forms, and exiting is "Disposing" of them, so I have put the following code in all of my accept buttons, and cancel buttons (any button that closes a form).
this.Hide();
this.Parent = null;
This code is just hiding the form. Not closing the form.
So what my problem is, is that when I click the 'x' button on the form, then try to re-open the form I still get the error message. I have tried a couple differnet ways to modify the exiting funciton of the form such as:
private void ApplicationProperties_FormClosing(object sender, FormClosingEventArgs e)
{
//Hiding the window, because closing it makes the window unaccessible.
this.Hide();
this.Parent = null;
}
But this has brought me no luck. I was wondering if anyone knows how to solve this problem. Here is the code that is working for me inside my cancel and accept buttons. It is the same for all of my buttons that close forms.
private void OptionsCancelbtn_Click(object sender, EventArgs e)
{
//Hiding the window, because closing it makes the window unaccessible.
this.Hide();
this.Parent = null;
}
I have declared the instance at the top of my class on form1, and have a button inside form1 that opens form2.
public partial class MainBox : Form
{
//Making a name for the ApplicationProperties form. It can be opened when called.
ApplicationProperties ApplicationPropertiesWindow = new ApplicationProperties();
private void ApplicationPropertiesbtn_Click(object sender, EventArgs e)
{
//Show the properties window.
ApplicationPropertiesWindow.Show();
}//End ApplicationProperties button.
}
After I close the program with the 'x' button on the second form I cannot access form2 again because of the error message firing at ApplicationPropertiesWindow.Show();
Inside form2 I have the following code:
public partial class ApplicationProperties : Form
{
//Creates and sets the instance MainBoxWindow.
public MainBox MainBoxWindow { get; set; }
Try this in your FormClosing event:
private void ApplicationProperties_FormClosing(object sender, FormClosingEventArgs e)
{
//Hiding the window, because closing it makes the window unaccessible.
this.Hide();
this.Parent = null;
e.Cancel = true; //hides the form, cancels closing event
}
cause right now it is still closing the form, thus still causing the error.
After I close the program with the 'x' button on the second form I
cannot access form2 again because of the error message firing at
ApplicationPropertiesWindow.Show();
When a form is closed (Form.Close), the form itself and all of its associated resources are freed. There are only two exceptions to this automatic disposal, as noted in the documentation:
The form is part of a MDI application and not visible.
The form was displayed as a modal dialog using ShowDialog (as opposed to Show). It is designed this way so that you can access properties of the dialog (e.g. to retrieve user input) after the user has closed it.
In both of these special cases, you are responsible for manually calling the Dispose method of the form. The second case is by far the most common (no one really uses the MDI paradigm anymore), and is handled easily with a using statement:
using (MyDialogBox dlg = new MyDialogBox())
{
DialogResult result = dlg.ShowModal(this);
if (result == DialogResult.Yes)
{
// access members of "dlg", and
// do whatever the user asked
}
} // the Dispose method is automatically called here
In your case, as is typical, the call to the Close method is closing and destroying the form. You already know that you cannot access a disposed object (because it no longer exists!), so that's why you're getting the exception when you try to show it. In order to show the form again after it has been closed, you need to create a new instance of the form class:
MyForm frm = new MyForm();
frm.Show();
// ...
frm.Close();
This is really the best method. A new instance of the form will be identical to the one you're closing because it was created from the same class. You would do best to start thinking in object-oriented terms and avoid singleton-based designs whenever possible. Each form that shows up on the screen is a new, independent object. Once that object is closed and destroyed, you cannot use it any longer. If you want to use it again, you need to create a new object and display it.
The Hide method is more of a hack, useful only when you want to temporarily hide the current instance of a form while still retaining its state (e.g., the values of its member properties, its control states, etc.). This works only with singleton objects that will never be destroyed, and must be carefully designed and maintained. It also means that this form object consumes resources all the time, whether it is being used or not, which is wasteful.
If you must do this, you'll need to track down what is causing your form instance to be disposed. Without seeing all of your code, it's hard for me to do anything but guess at where the problem might be. It is likely related to your use of the AcceptButton and/or CancelButton properties. Either way, the best and cleanest solution is to override the OnFormClosing method and prevent your form from ever being closed. You'll hide it instead:
public class MyForm : Form
{
protected virtual void OnFormClosing(FormClosingEventArgs e)
{
// Prevent the form from closing.
e.Cancel = true;
// Hide it instead.
this.Hide();
}
// ...other code in your form class
}
The advantage of this is that you only have to have the code in one place, local to the responsible class, rather than exposed to external code and scattered throughout your application. And of course, it also prevents the form from ever being closed by framework code outside of your control.
I have no idea why you're setting the Parent property to null. A top-level window (which is what all forms are) can never have a parent. Only child windows (e.g. controls) have parents. It can have an owner, but it will not necessarily. It depends on whether you pass an owner window as an argument when calling the Show method.
First remove this.Parent=null bcose this is not required when you hiding the Form
Now when you hiding the Form and if you still want to access this Form store form in static var . Bcous when object is no longer in use garbage collector dispose it and it will no longer available .
The problem, at least perhaps part of it, looks like a bit of class interdependence.
When you initialize an ApplicationProperties instance, you're creating a reference to a MainBox object, but in the definition of the MainBox class, you're creating a new ApplicationsProperties object, which references a MainBox.... I'm even confusing myself. Is MainBox your parent form, the one that loads when you start the application?
I wouldn't be surprised if some stuff in MainBox is getting destroyed inadvertently if you set a reference to it in the ApplicationProperites class... which is disposing when you click "X". Again, this is an educated guess; I'm not sure if that's the reason but even if the code isn't wrong per se, it looks quirky to me.
If you want your ApplicationProperties window to last forever and to simplify things, just initialize a static instance of it at the beginning of whatever form is going to persist the duration of your application, and show/hide it similar to what you were attempting originally. If you want to be efficient and fundamental, construct and dispose ApplicationProperties whenever you need to let the user modify it, but kill the circular dependency.
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.
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...
How do you use a child window in Silverlight such that it is not done asynchronous? The way you use a child window in Silverlight (so far as I know) is to use the show method:
ChildWindow update_dlg = new Update();
update_dlg.Show();
But you cannot get the input from the dialog because the call is asynchronous and keeps on running while the child window us up.
You can't, you need to listen for the child window closing then grab the dialog result.
From MSDN page:
To get the DialogResult value from a child window, handle the Closed event in the code-behind page of the calling window. In the Closed event handler, cast the sender parameter to a ChildWindow or a derived class to access the DialogResult property.
I like to pass an Action to the child window, if the user cancels the window nothing is called, if the user accepts the window the action is executed with the parameters from the window. This way the code to run when the user accepts is still written in the calling window.
There are three ways of doing this as I see it.
1) launch a seperate thread in the main app that waits until some third party class member is switched from true to false to indicate that the child window has been closed. Then in the child window, when a shutdown or OK or Cancel is done, this third party class member is switched too false etc. (this is the way i did this)
2) have a call back function in the main program that is called by the child window.
3) kind of cheat and don't have a child window at all. Instead, in the XAML code of the main window, have a rectangle that looks like a pop up or child window that is hidden (visual set to collapse) that comes up (but really becomes visual) and all other fields in the main program are set to inactive until the right butons are puched.**