Notify Modal forms' parent that it needs to action something - c#

I have a parent form open a modal form which basically allows a user to change the database settings of the application.
When the user clicks the save button on the modal (child) form it saves the Settings object with the new settings but I need to have the Main form check that the database settings are correct.
I currently do this through a function which tries to simply connect to the database and if successful return true, false if this failed. That function I execute within the applications constructor so it runs fine whenever the application is closed and restarted.
I tried the following within the modal form after saving the settings but get a NullReference exception for object myManager.
This is the function which gets the new settings and saves them and then attempts to call the parent forms CheckDatabaseIsSetup() public function to test the db connection.
/// <summary>
/// Save the settings and then hide the Settings window
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_Save_Click(object sender, EventArgs e)
{
// TRUE: User indicates that we are to connect using a trusted connection
// FALSE: User wants to use Integrated security to connect.
if (rb_UseTrustedConnection.Checked)
{
AppSettings.DatabaseName = tb_Trusted_DbName.Text;
AppSettings.Server = tb_Trusted_Server.Text;
AppSettings.UseIntergratedSecurity = false;
}
else
{
AppSettings.DatabaseName = tb_Secure_DbName.Text;
AppSettings.Server = tb_Secure_Server.Text;
AppSettings.Username = tb_Secure_Username.Text;
AppSettings.Password = tb_Secure_Password.Text;
AppSettings.UseIntergratedSecurity = true;
}
try
{
AppSettings.SaveSettings();
BushBreaksLodgeManagerMain myManager = (BushBreaksLodgeManagerMain)this.ParentForm;
myManager.CheckDatabaseIsSetup();
}
catch (Exception ex)
{
log.LogAppendWithException(ex);
}
this.Hide();
}

You should instead use the Owner property in the modal form rather than the ParentForm property as follows:
BushBreaksLodgeManagerMain myManager = (BushBreaksLodgeManagerMain)this.Owner;
The Owner property defines the actual relationship between the owned(modal) form and the parent(owner) form.

Its better to define an event in child form and handle this event in main form
and when ever you raise this event in child form mainform can does its own job

BushBreaksLodgeManagerMain myManager = (BushBreaksLodgeManagerMain)this.ParentForm;
You can check the above line whether ParentForm is of Type/can cast to BushBreaksLodgeManagerMain. I presume the case wasn't successful hence return null

I'd usually do this by using object intercommunication such as provided with my Emesary library; The design is to use notifications in such a way that the request is sent and handled by anything that knows it needs to process these notifications, so for example it's easy to add in extra event handlers that are disconnected.
In which case the code for checking the database settings would become:
if (ReceiptStatus.OK ==
GlobalNotifier.NotifyAll(new CheckDatabaseIsSetupNotification(tb_Secure_DbName.Text,
tb_Secure_Server.Text,
tb_Secure_Username.Text,
tb_Secure_Password.Text,
true))
{
// do something.
}
To make this work you'd need to implement IReceiver in BushBreaksLodgeManagerMain and in the constructor call
GlobalTransmitter.Register(this);
then implement the interface receive:
public ReceiptStatus Receive(INotification _message)
{
if (_message is CheckDatabaseIsSetupNotification)
{
var message = _message as CheckDatabaseIsSetupNotification;
if (connect_to(message.DatabaseName, message.Server, Message.Username, message.Password, message.UseIntergratedSecurity))
return ReceiptStatus.OK;
else
return ReceiptStatus.Fail;
}
return ReceiptStatus.NotProcessed;
}
You can do this using Windows events - but this way is clearer and allows inter operation with objects that don't necessarily have windows.

Related

how can i get this c# server side wait window to quit when exporting is finished?

Team!!
I have a website that let's people download data into an excel or pdf file. The download takes long enough that I want to display a "Please Wait" modal while the processing is happening. I found some code that does this, but the problem is it just counts from 1 to 100 and then quits. I'd like to alter that so that it displays until the download is complete.
When the use clicks the button to begin the export, the expot_click function is called. The WorkerMethod function below it gets passed into the class and is where the count from 1 to 100 is occurring. I'm not sure what to change, or where to change it.
protected void export_click(object sender, EventArgs e)
{
string exportType = hidExportType.Value;
string exportSections = hidSections.Value;
string exportStudents = hidStudents.Value;
//Display a "Please Wait" message while the download is happening
object result = WaitWindow.Show(this.WorkerMethod);
switch (exportType)
{
case "csv":
export_csv(exportSections, exportStudents);
break;
case "excel":
export_excel(exportSections, exportStudents);
break;
case "pdf":
export_pdf(exportSections, exportStudents);
break;
default:
break;
}
//I'm guessing here is where I'd want to do something to make the wait display quit?
}
private void WorkerMethod(object sender, Jacksonsoft.WaitWindowEventArgs e)
{
// Do something
for (int progress = 1; progress <= 100; progress++)
{
System.Threading.Thread.Sleep(20);
// Update the wait window message
e.Window.Message = string.Format
("Please wait ... {0}%", progress.ToString().PadLeft(3));
}
// Use the arguments sent in
if (e.Arguments.Count > 0)
{
// Set the result to return
e.Result = e.Arguments[0].ToString();
}
else
{
// Set the result to return
e.Result = "Hello World";
}
}
Below is the class that gets called:
/*
* Created by SharpDevelop.
* User: mjackson
* Date: 05/03/2010
* Time: 09:36
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace Jacksonsoft
{
/// <summary>
/// Displays a window telling the user to wait while a process is executing.
/// </summary>
public class WaitWindow
{
/// <summary>
/// Shows a wait window with the text 'Please wait...' while executing the passed method.
/// </summary>
/// <param name="workerMethod">Pointer to the method to execute while displaying the wait window.</param>
/// <returns>The result argument from the worker method.</returns>
public static object Show(EventHandler<WaitWindowEventArgs> workerMethod){
return WaitWindow.Show(workerMethod, null);
}
/// <summary>
/// Shows a wait window with the specified text while executing the passed method.
/// </summary>
/// <param name="workerMethod">Pointer to the method to execute while displaying the wait window.</param>
/// <param name="message">The text to display.</param>
/// <returns>The result argument from the worker method.</returns>
public static object Show(EventHandler<WaitWindowEventArgs> workerMethod, string message){
WaitWindow instance = new WaitWindow();
return instance.Show(workerMethod, message, new List<object>());
}
/// <summary>
/// Shows a wait window with the specified text while executing the passed method.
/// </summary>
/// <param name="workerMethod">Pointer to the method to execute while displaying the wait window.</param>
/// <param name="message">The text to display.</param>
/// <param name="args">Arguments to pass to the worker method.</param>
/// <returns>The result argument from the worker method.</returns>
public static object Show(EventHandler<WaitWindowEventArgs> workerMethod, string message, params object[] args){
List<object> arguments = new List<object>();
arguments.AddRange(args);
WaitWindow instance = new WaitWindow();
return instance.Show(workerMethod, message, arguments);
}
#region Instance implementation
private WaitWindow(){}
private WaitWindowGUI _GUI;
internal delegate void MethodInvoker<T>(T parameter1);
internal EventHandler<WaitWindowEventArgs> _WorkerMethod;
internal List<object> _Args;
/// <summary>
/// Updates the message displayed in the wait window.
/// </summary>
public string Message{
set{
this._GUI.Invoke(new MethodInvoker<string>(this._GUI.SetMessage), value);
}
}
/// <summary>
/// Cancels the work and exits the wait windows immediately
/// </summary>
public void Cancel(){
this._GUI.Invoke(new MethodInvoker(this._GUI.Cancel), null);
}
private object Show(EventHandler<WaitWindowEventArgs> workerMethod, string message, List<object> args){
// Validate Parameters
if (workerMethod == null){
throw new ArgumentException("No worker method has been specified.", "workerMethod");
} else {
this._WorkerMethod = workerMethod;
}
this._Args = args;
if (string.IsNullOrEmpty(message)){
message = "Please wait...";
}
// Set up the window
this._GUI = new WaitWindowGUI(this);
this._GUI.MessageLabel.Text = message;
// Call it
this._GUI.ShowDialog();
object result = this._GUI._Result;
// clean up
Exception _Error = this._GUI._Error;
this._GUI.Dispose();
// Return result or throw and exception
if (_Error != null){
throw _Error;
} else {
return result;
}
}
#endregion Instance implementation
}
}
Any suggestions on what to do to get this working would be greatly appreciated!!
You can't use a server side window.
but, what you can do is pop or display a dialog WHEN the user clicks on the button. That pop dialog will stay open while the web page is up on the server being processed. When the server page is done, then that new fresh server page is then send back to the client side browser and thus the display message will go away on its own.
So, say your button to start process is a simple button. When clicked on that "longer" server side code runs.
So, you keep the buttion, but add a client side script to pop up a dialog message.
In this case we will use a jQuery.UI dialog.
So, you will need in the web page jQquery, and jQuery.UI.
So, now the simple button code becomes this:
<asp:Button ID="cmdBigProcess" runat="server" Text="Start the reactor!"
OnClientClick="showait();return true;"
OnClick="cmdBigProcess_Click" />
<div id="mywait" style="display:none;border:solid">
<h2>Big process running - please wait</h2>
</div>
<script>
function showait() {
// lets pop jquery.UI dialog
var mydiv = $("#mywait")
mydiv.dialog({
modal: true, appendTo: "form",
title: "Please wait", closeText: "",
width: "400px"
});
}
</script>
So, we added a simple "div" that will hold the message.
Now, when you click on the button, the "on client click" code runs FIRST, and displays the dialog, then your server side code runs. As noted, we don't have to dismiss or do anything to the dialog, since when the web page is posted, it travels up to the server. The code behind runs (may or may not change thing on the web page), and THEN when the page is done, the whole web page now travels back down to the web side, browser re-plots and re-loads the web page (this is the so called round trip, or often called the page life-cycle. it is BEYOND critical that you grasp and understand this round trip concept. Your code behind NEVER directly interacts with the user. But upon hitting a buttion, the WHOLE web page travels up to server, code runs. And EVEN when the code modifies the controls and things in the web page? They will NOT show up, NOT update, and user will not see any changes to that web page until ALL YOUR code behind is done. Once code behind is done, then the WHOLE page makes the trip back to browser and is re-loaded.
So, the re-load of the page is what in fact will case the popped up dialog to be dismissed. Note that jQuery.UI dialogs (and most web dialogs) do NOT halt code when called - code continues.
The result looks like this:

VSTO Word post save event

Sorry for the length of this, In the past I've been asked before to included everything I've tried when asking these sort of questions.
I'm writing a Word Add-in and need to make changes to the document that I cannot achieve using the Word object model. Therefore, after the document is save to disk, I need to capture that event, close the file, do what I need to do and reopen it. (I know, not elegant, but that's what I have to work with.)
Word has Before Save and Before Close, but no after save event. I found tricks online to simulate an after save event by creating another thread and using COM's IMessageFilter (not from System.Windows.Forms) to handle COM Retry calls, or posting a message back on the main thread so I can execute code after it's saved. But this doesn't work because if the file is saved as a result of the user attempting to close the document I can't get the file name in my "callback" method because Word.Document object has already been deleted.
So I tried explicitly call Save myself in my BeforeSave event handler and return Cancel = true. That works great when user selects save, or they had once saved to disk. But if the user closes a new document without saving and then selects 'yes' to whether or not they want to save, Word displays another "SaveAs" dialog after I've already handled the save after I return from the BeforeSave event, even though I set Cancel = true in my BeforeSave event handler.
So then I tried doing the something similar with the BeforeClose event. I handle the close and save myself and then returning Cancel = true from my event handler. But doing so stops word from trying to close multiple documents when the user is attempting to shut down the application.
I even tried handling WM_CLOSE, but that lead to similar problems as above.
Can anyone offer a solution?
I came across this a while back, I think it may do what you want. Here's a copy of what's there in case it ever disappears.
When I wrote my first Word AfterSave Event entry, it was designed for Word 2007, and was – as it turns out – not a catch all. So I have updated it here (thanks for the catch go to Pat Lemm).
When the document was closed, you never got access to the Saved filename. So, I have updated the code here and it now works in all conditions and has been tested in Word 2013.
Here is how it works:
Upon initialization you pass it your Word object.
It attaches to the Before Save Event.
When any save event occurs, it kicks off a thread that loops until the Background Save is complete.
Once the background save is done, it checks to see if the document Saved == true:
If Saved == true: then a regular save did occur.
If Saved == false: then it had to be an AutoSave
In each case it will fire a unique event:
AfterSaveUiEvent
AfterSaveEvent
AfterAutoSaveEvent
Additionally, if the document being saved is also being closed, we catch the filename on the WindowDeactivate event on the way out. This can now be accessed by the caller (as you can see in the example below), to get the full filename of the closed document.
Here is the code to the class:
public class WordSaveHandler
{
public delegate void AfterSaveDelegate(Word.Document doc, bool isClosed);
// public events
public event AfterSaveDelegate AfterUiSaveEvent;
public event AfterSaveDelegate AfterAutoSaveEvent;
public event AfterSaveDelegate AfterSaveEvent;
// module level
private bool preserveBackgroundSave;
private Word.Application oWord;
string closedFilename = string.Empty;
/// <summary>
/// CONSTRUCTOR takes the Word application object to link to.
/// </summary>
/// <param name="oApp"></param>
public WordSaveHandler(Word.Application oApp)
{
oWord = oApp;
// hook to before save
oWord.DocumentBeforeSave += oWord_DocumentBeforeSave;
oWord.WindowDeactivate += oWord_WindowDeactivate;
}
/// <summary>
/// Public property to get the name of the file
/// that was closed and saved
/// </summary>
public string ClosedFilename
{
get
{
return closedFilename;
}
}
/// <summary>
/// WORD EVENT fires before a save event.
/// </summary>
/// <param name="Doc"></param>
/// <param name="SaveAsUI"></param>
/// <param name="Cancel"></param>
void oWord_DocumentBeforeSave(Word.Document Doc, ref bool SaveAsUI, ref bool Cancel)
{
// This could mean one of four things:
// 1) we have the user clicking the save button
// 2) Another add-in or process firing a resular Document.Save()
// 3) A Save As from the user so the dialog came up
// 4) Or an Auto-Save event
// so, we will start off by first:
// 1) Grabbing the current background save flag. We want to force
// the save into the background so that Word will behave
// asyncronously. Typically, this feature is on by default,
// but we do not want to make any assumptions or this code
// will fail.
// 2) Next, we fire off a thread that will keep checking the
// BackgroundSaveStatus of Word. And when that flag is OFF
// no know we are AFTER the save event
preserveBackgroundSave = oWord.Options.BackgroundSave;
oWord.Options.BackgroundSave = true;
// kick off a thread and pass in the document object
bool UiSave = SaveAsUI; // have to do this because the bool from Word
// is passed to us as ByRef
new Thread(() =>
{
Handle_WaitForAfterSave(Doc, UiSave);
}).Start();
}
/// <summary>
/// This method is the thread call that waits for the same to compelte.
/// The way we detect the After Save event is to essentially enter into
/// a loop where we keep checking the background save status. If the
/// status changes we know the save is compelte and we finish up by
/// determineing which type of save it was:
/// 1) UI
/// 2) Regular
/// 3) AutoSave
/// </summary>
/// <param name="Doc"></param>
/// <param name="UiSave"></param>
private void Handle_WaitForAfterSave(Word.Document Doc, bool UiSave)
{
try
{
// we have a UI save, so we need to get stuck
// here until the user gets rid of the SaveAs dialog
if (UiSave)
{
while (isBusy())
Thread.Sleep(1);
}
// check to see if still saving in the background
// we will hang here until this changes.
while (oWord.BackgroundSavingStatus > 0)
Thread.Sleep(1);
}
catch (ThreadAbortException)
{
// we will get a thread abort exception when Word
// is in the process of closing, so we will
// check to see if we were in a UI situation
// or not
if (UiSave)
{
AfterUiSaveEvent(null, true);
}
else
{
AfterSaveEvent(null, true);
}
}
catch
{
oWord.Options.BackgroundSave = preserveBackgroundSave;
return; // swallow the exception
}
try
{
// if it is a UI save, the Save As dialog was shown
// so we fire the after ui save event
if (UiSave)
{
// we need to check to see if the document is
// saved, because of the user clicked cancel
// we do not want to fire this event
try
{
if (Doc.Saved == true)
{
AfterUiSaveEvent(Doc, false);
}
}
catch
{
// DOC is null or invalid. This occurs because the doc
// was closed. So we return doc closed and null as the
// document
AfterUiSaveEvent(null, true);
}
}
else
{
// if the document is still dirty
// then we know an AutoSave happened
try
{
if (Doc.Saved == false)
AfterAutoSaveEvent(Doc, false); // fire autosave event
else
AfterSaveEvent(Doc, false); // fire regular save event
}
catch
{
// DOC is closed
AfterSaveEvent(null, true);
}
}
}
catch { }
finally
{
// reset and exit thread
oWord.Options.BackgroundSave = preserveBackgroundSave;
}
}
/// <summary>
/// WORD EVENT – Window Deactivate
/// Fires just before we close the document and it
/// is the last moment to get the filename
/// </summary>
/// <param name="Doc"></param>
/// <param name="Wn"></param>
void oWord_WindowDeactivate(Word.Document Doc, Word.Window Wn)
{
closedFilename = Doc.FullName;
}
/// <summary>
/// Determines if Word is busy essentially that the File Save
/// dialog is currently open
/// </summary>
/// <param name="oApp"></param>
/// <returns></returns>
private bool isBusy()
{
try
{
// if we try to access the application property while
// Word has a dialog open, we will fail
object o = oWord.ActiveDocument.Application;
return false; // not busy
}
catch
{
// so, Word is busy and we return true
return true;
}
}
}
And here is how you set it up and attach to it’s events:
public partial class ThisAddIn
{
WordSaveHandler wsh = null;
private void ThisAddIn_Startup(object sender,
System.EventArgs e)
{
// attach the save handler
wsh = new WordSaveHandler(Application);
wsh.AfterAutoSaveEvent += new WordSaveHandler.AfterSaveDelegate(wsh_AfterAutoSaveEvent);
wsh.AfterSaveEvent += new WordSaveHandler.AfterSaveDelegate(wsh_AfterSaveEvent);
wsh.AfterUiSaveEvent += new WordSaveHandler.AfterSaveDelegate(wsh_AfterUiSaveEvent);
}
void wsh_AfterUiSaveEvent(Word.Document doc, bool isClosed)
{
if (!isClosed)
MessageBox.Show("After SaveAs Event");
else
MessageBox.Show("After Close and SaveAs Event. The filname was: " + wsh.ClosedFilename);
}
void wsh_AfterSaveEvent(Word.Document doc, bool isClosed)
{
if (!isClosed)
MessageBox.Show("After Save Event");
else
MessageBox.Show("After Close and Save Event. The filname was: " + wsh.ClosedFilename);
}
void wsh_AfterAutoSaveEvent(Word.Document doc, bool isClosed)
{
MessageBox.Show("After AutoSave Event");
}
// etc.
}

ShowDialog using invoke is making the application unresponsive

I'm using Control.Invoke() to show a dialog. The code is a handler to get credentials from the user and it can be execute in a thread, this is the reason I perform the call into an InvokeRequired/Invoke snippet.
Sometimes, and only in some machines, when I close the dialog, the application becomes unresposive (it does not manage some mouse clicks, but manage others). If I execute some "allowed" actions, the application start to be responsive again. It seems that processing any event, the application fixes itself.
Do you know any known bug in the .NET framework, or something that could cause this issue?
Thanks in advance.
EDIT: This is the code I'm using:
public class GuiCredentialsHandler
{
// control used to invoke if needed
private static Control mInvokeControl;
// control used as parent for showDialog (could be null)
private static Control mParentControl;
/// <summary>
/// Initialize a GetCredentials handler for current process.
/// This method should be always called from the UI thread, for
/// a correctly handling for invokes (when the handler is called
/// from a thread).
/// </summary>
/// <param name="parentControl">Application top form.
/// Can be null if unknown</param>
public static void Initialize(Control parentControl)
{
if (parentControl != null)
{
mInvokeControl = parentControl;
}
else
{
mInvokeControl = new Control();
// force to create window handle
// otherwise, invoke required always
// return false
mInvokeControl.CreateControl();
}
mParentControl = parentControl;
}
public static Credentials GetCredentials(
string servername, SEIDWorkingMode serverWorkingMode)
{
if (mInvokeControl.InvokeRequired)
{
return mInvokeControl.Invoke(
new GetCredentialsDelegate(DoGetCredentials),
new object[] { servername, serverWorkingMode })
as Credentials;
}
else
{
return DoGetCredentials(servername, serverWorkingMode);
}
}
private static Credentials DoGetCredentials(
string servername, SEIDWorkingMode serverWorkingMode)
{
GetCredentialsDialog dialog = new GetCredentialsDialog();
dialog.Server = servername;
dialog.WorkingMode = serverWorkingMode;
DialogResult result = dialog.ShowDialog(mParentControl);
if (result == DialogResult.Cancel) return null;
UserInfoRetriever retriever = new UserInfoRetriever(
servername, serverWorkingMode,
dialog.UserName, dialog.Password);
SEID seid = retriever.GetCurrentUser();
return new Credentials(seid, serverWorkingMode);
}
public delegate Credentials GetCredentialsDelegate(
string serverName,
SEIDWorkingMode mode);
Is Control.Invoke actually needed in this case?
I was always under the impression that invoke was used to ensure that UI elements are accessed by the thread that creates the control which is usually the UI thread but does not have to be.
In this case it looks like you are trying to create a dialog from a thread and hence you should be able to update it from the thread. (Obviously you can't access it from outside your thread, which will include the main UI thread).
If I'm wrong no doubt this will get downvoted very quickly.
mParentControl will always be set equal to parentControl even if its NULL which does not seem right.
The reason your program becomes unresposive is because your mParentControl is NULL:
DialogResult result = dialog.ShowDialog(mParentControl);
One solution around this problem is to only show the dialog if the parent is known.
if ( mParentControl != NULL )
DialogResult result = dialog.ShowDialog(mParentControl);
else
DialogResult result = dialog.ShowDialog(mInvokeControl);
I based my answer on the following code:
if (parentControl != null)
{
mInvokeControl = parentControl;
}
I think you mean my answer makes no sense. What makes more sense that Hans Passant comment doesn't hold some truth or your code is actually correct and you discovered a bug. Since you are being rude I will just take my experience and help somebody else. Humor yourself and add code to avoid the mParentControl is Null situation because IT CAN HAPPEN. mParentControl is ALWAYS set to parentcontrol even when its NULL.
Application top form. /// Can be null if
unknown

How do you cancel a ToolWindowPane or Visual Studio IDE close operation via a VSPackage?

I have a VSPackage with a dockable tool window containing form data. If there are unsaved changes in this form, I would like to cancel a close to either the tool window and the visual studio IDE if the user clicks cancel on saving changes before closing. I can perform the save test on close, but I don't see any event handler methods or other options to actually cancel the close.
Here is some blurb from the package:
private DTE2 _applicationObject = null;
///--------------------------------------------------------------------------------
/// <summary>This property gets the visual studio IDE application object.</summary>
///--------------------------------------------------------------------------------
public DTE2 ApplicationObject
{
get
{
if (_applicationObject == null)
{
// Get an instance of the currently running Visual Studio IDE
DTE dte = (DTE)GetService(typeof(DTE));
_applicationObject = dte as DTE2;
}
return _applicationObject;
}
}
///--------------------------------------------------------------------------------
/// <summary>
/// Initialization of the package; this method is called right after the package is sited, so this is the place
/// where you can put all the initilaization code that rely on services provided by VisualStudio.
/// </summary>
///--------------------------------------------------------------------------------
protected override void Initialize()
{
Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString()));
base.Initialize();
// add the event handlers
if (ApplicationObject != null)
{
// wire up window events
PackageWindowEvents = (WindowEvents)ApplicationObject.Events.get_WindowEvents(null);
PackageWindowEvents.WindowClosing += new _dispWindowEvents_WindowClosingEventHandler(PackageWindowEvents_WindowClosing);
// wire up solution events
PackageSolutionEvents = ApplicationObject.Events.SolutionEvents;
PackageSolutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(CheckOpenCurrentSolution);
// wire up DTE events
PackageDTEEvents = ApplicationObject.Events.DTEEvents;
PackageDTEEvents.OnBeginShutdown += new _dispDTEEvents_OnBeginShutdownEventHandler(HandleVisualStudioShutdown);
}
}
void PackageWindowEvents_WindowClosing(Window window)
{
// handle save/cancel scenarios
}
And some blurb from the ToolWindowPane that implements IVsWindowFrameNotify3:
protected override void OnClose()
{
base.OnClose();
}
public int OnClose(ref uint pgrfSaveOptions)
{
return (int)__FRAMECLOSE.FRAMECLOSE_PromptSave;
}
The OnClose and WindowClosing methods fire when expected, but I'm not finding a way to cancel the close. What am I missing? Are there different events required to cancel a close?
For completeness, there IS a useful way to cancel a close with a tool window (provided by user Chicobo on the msdn forum and repeated here).
In your ToolWindowPane class, implement the IVsWindowFrameNotify2 interface, which provides a method OnClose.
To cancel the window closing, consider using :
public int OnClose(ref uint pgrfSaveOptions)
{
// Check if your content is dirty here, then
// Prompt a dialog
MessageBoxResult res = MessageBox.Show("This Document has been modified. Do you want to save the changes ?",
"Unsaved changes", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning);
// If the users wants to save
if (res == MessageBoxResult.Yes)
{
// Handle with your "save method here"
}
if (res == MessageBoxResult.Cancel)
{
// If "cancel" is clicked, abort the close
return VSConstants.E_ABORT;
}
// Else, exit
return VSConstants.S_OK;
}
For the purpose of completing the previous answer, the following code snippet targets an unanswered part of the question which is on how to prevent the close operation of the Visual Studio IDE inside a VsPackage:
protected override int QueryClose(out bool pfCanClose)
{
pfCanClose = false;
return VSConstants.S_OK;
}
Use the above code inside your package class.

Hide form instead of closing when close button clicked

When a user clicks the X button on a form, how can I hide it instead of closing it?
I have tried this.hide() in FormClosing but it still closes the form.
Like so:
private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
Hide();
}
}
(via Tim Huffman)
I've commented in a previous answer but thought I'd provide my own. Based on your question this code is similar to the top answer but adds the feature another mentions:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
Hide();
}
}
If the user is simply hitting the X in the window, the form hides; if anything else such as Task Manager, Application.Exit(), or Windows shutdown, the form is properly closed, since the return statement would be executed.
From MSDN:
To cancel the closure of a form, set the Cancel property of the FormClosingEventArgs passed to your event handler to true.
So cancel then hide.
Based on other response, you can put it in your form code :
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
Hide();
}
}
According MSDN, the override is preferred:
The OnFormClosing method also allows derived classes to handle the
event without attaching a delegate. This is the preferred technique
for handling the event in a derived class.
If you want to use the show/hide method I've actually done this myself for a menu structure a game I've recently done... This is how I did it:
Create yourself a button and for what you'd like to do, for example a 'Next' button and match the following code to your program. For a next button in this example the code would be:
btnNext.Enabled = true; //This enabled the button obviously
this.Hide(); //Here is where the hiding of the form will happen when the button is clicked
Form newForm = new newForm(); //This creates a new instance object for the new form
CurrentForm.Hide(); //This hides the current form where you placed the button.
Here is a snippet of the code I used in my game to help you understand what I'm trying to explain:
private void btnInst_Click(object sender, EventArgs e)
{
btnInst.Enabled = true; //Enables the button to work
this.Hide(); // Hides the current form
Form Instructions = new Instructions(); //Instantiates a new instance form object
Instructions.Show(); //Shows the instance form created above
}
So there is a show/hide method few lines of code, rather than doing a massive piece of code for such a simple task.
I hope this helps to solve your problem.
Note that when doing this (several answers have been posted) that you also need to find a way to ALLOW the user to close the form when they really want to. This really becomes a problem if the user tries to shut down the machine when the application is running, because (at least on some OS) this will stop the OS from shutting down properly or efficiently.
The way I solved this was to check the stack trace - there are differences between when the user tries to click the X vs when the system tries to end the application in preparation for shutdown.
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
StackTrace trace = new StackTrace();
StackFrame frame;
bool bFoundExitCommand = false;
for (int i = 0; i < trace.FrameCount; i++)
{
frame = trace.GetFrame(i);
string methodName = frame.GetMethod().Name;
if (methodName == "miExit_Click")
{
bFoundExitCommand = true;
Log("FormClosing: Found Exit Command ({0}) - will allow exit", LogUtilityLevel.Debug3, methodName);
}
if (methodName == "PeekMessage")
{
bFoundExitCommand = true;
Log("FormClosing: Found System Shutdown ({0}) - will allow exit", LogUtilityLevel.Debug3, methodName);
}
Log("FormClosing: frame.GetMethod().Name = {0}", LogUtilityLevel.Debug4, methodName);
}
if (!bFoundExitCommand)
{
e.Cancel = true;
this.Visible = false;
}
else
{
this.Visible = false;
}
}
This is the behavior of Modal forms. When you use form.ShowDialog() you are asking for this behavior. The reason for this is that form.ShowDialog doesn't return until the form is hidden or destroyed. So when the form is hidden, the pump inside form.ShowDialog destroys it so that it can return.
If you want to show and hide a form, then you should be using the Modeless dialog model
http://msdn.microsoft.com/en-us/library/39wcs2dh(VS.80).aspx
form.Show() returns immediately, you can show and hide this window all you want and it will not be destroyed until you explicitly destroy it.
When you use modeless forms that are not children of a modal form, then you also need to run a message pump using Application.Run or Application.DoEvents in a loop. If the thread that creates a form exits, then the form will be destroyed. If that thread doesn't run a pump then the forms it owns will be unresponsive.
Edit: this sounds like the sort of thing that the ApplicationContext is designed to solve. http://msdn.microsoft.com/en-us/library/system.windows.forms.applicationcontext.aspx
Basically, you derive a class from ApplicationContext, pass an instance of your ApplicationContext as an argument to Application.Run()
// Create the MyApplicationContext, that derives from ApplicationContext,
// that manages when the application should exit.
MyApplicationContext context = new MyApplicationContext();
// Run the application with the specific context.
Application.Run(context);
Your application context will need to know when it's ok to exit the application and when having the form(s) hidden should not exit the application. When it's time for the app to exit. Your application context or form can call the application context's ExitThread() method to terminate the message loop. At that point Application.Run() will return.
Without knowing more about the heirarchy of your forms and your rules for deciding when to hide forms and when to exit, it's impossible to be more specific.

Categories

Resources