I have a window where I need to fire a specific method when this window closing, I did:
public FooWindow()
{
Closing += (x, y) => Exit();
}
private void Exit()
{
if (someVariable)
{
Environment.Exit(1);
}
else
{
Close();
}
}
when the Exit event is called the close method is reached but I get
System.InvalidOperationException: Cannot set Visibility or call Show, ShowDialog, or WindowInteropHelper.EnsureHandle after a Window has closed.
what am i doing wrong?
Seems you are calling Close(); when the Closing event is called. Which sounds to me that you are trying to close a window that is already in the process of closing itself.
That said, if you still want the Exit() method to close if your someVariable is false. Track the 'closing' status of your form with a boolean, something like the following:
private bool _isClosing = false;
public FooWindow()
{
Closing += (x, y) => {
_isClosing = true;
Exit();
};
}
private void Exit()
{
if (someVariable)
{
Environment.Exit(1);
}
else
{
if (!_isClosing) Close();
}
}
The problem is that you are calling Close() while the window is already closing. So WPF checks for that scenario and launches you the exception to notice the error. Here the call stack I got reproducing your problem. See that the code launching the exception has a self evident name VerifyNotClosing:
in System.Windows.Window.VerifyNotClosing()
in System.Windows.Window.InternalClose(Boolean shutdown, Boolean ignoreCancel)
in System.Windows.Window.Close()
in WpfApp1.MainWindow.MainWindow_Closing(Object sender, CancelEventArgs e) in MainWindow.xaml.cs:line 32
in System.Windows.Window.OnClosing(CancelEventArgs e)
in System.Windows.Window.WmClose()
Window.Close() calls InternalClose() which calls VerifyNotClosing() which throws:
private void VerifyNotClosing()
{
if (_isClosing == true)
{
throw new InvalidOperationException(SR.Get(SRID.InvalidOperationDuringClosing));
}
if (IsSourceWindowNull == false && IsCompositionTargetInvalid == true)
{
throw new InvalidOperationException(SR.Get(SRID.InvalidCompositionTarget));
}
}
_isClosing is set to true by InternalClose() on your first visit. SR.Get(SRID.InvalidCompositionTarget) is the error message you see.
Related
In my MainWindow constructor I ovverided the Closing event because I need to call another method that perform some task, like:
public MainWindow()
{
InitializeComponent();
Closing += (x, y) =>
{
y.Cancel = true;
_discard = true;
CheckSettings();
};
}
public void CheckSettings(bool x)
{
if(x)
Close();
}
on the Close line I get:
cannot set visibility or call show or showdialog after window has closed
why??
(as requested in you comment...)
You cannot call Close from a Closing event handler.
If the logic determining if the form can be closed is implemented in CheckSettings:
public MainWindow()
{
InitializeComponent();
Closing += (sender, args) =>
{
args.Cancel = !CheckSettings();
...
};
}
public bool CheckSettings()
{
// Check and return true if the form can be closed
}
Until you return from your event handler (that's made the call to CheckSettings), the UI framework you're using may not evaluate the content of the EventArgs that you've named as y and set Cancel = true on.
If you're using WPF, for example, the Close method eventually calls down into another method called VerifyNotClosing (via InternalClose) which at the time of writing looks like this:
private void VerifyNotClosing()
{
if (_isClosing == true)
{
throw new InvalidOperationException(SR.Get(SRID.InvalidOperationDuringClosing));
}
if (IsSourceWindowNull == false && IsCompositionTargetInvalid == true)
{
throw new InvalidOperationException(SR.Get(SRID.InvalidCompositionTarget));
}
}
The relevant bit there is the first if that checks a member variable called _isClosing and throws an exception if the form is in the process of closing.
The InternalClose method reacts to the state of the Cancel property of the EventArgs after the event handlers have been called:
CancelEventArgs e = new CancelEventArgs(false);
try
{
// The event handler is called here
OnClosing(e);
}
catch
{
CloseWindowBeforeShow();
throw;
}
// The status of the .Cancel on the EventArgs is not checked until here
if (ShouldCloseWindow(e.Cancel))
{
CloseWindowBeforeShow();
}
else
{
_isClosing = false;
// 03/14/2006 -- hamidm
// WOSB 1560557 Dialog does not close with ESC key after it has been cancelled
//
// No need to reset DialogResult to null here since source window is null. That means
// that ShowDialog has not been called and thus no need to worry about DialogResult.
}
The code above (from the InternalClose method) is after the call to VerifyNotClosing which is why the subsequent call to Close, before the first one has finished, results in the exception being thrown.
I am having an odd problem with protecting a section of code. My application is a tray app. I create a NotifyIcon inside my class (ApplicationContext). I have assigned a balloon click handler and a double click handler to the NotifyIcon object. there is also a context menu but I am not showing all code. Only important pieces.
public class SysTrayApplicationContext: ApplicationContext
{
private NotifyIcon notifyIcon;
private MainForm afDashBoardForm;
public SysTrayApplicationContext()
{
this.notifyIcon = new NotifyIcon();
this.notifyIcon.BalloonTipClicked += notifyIcon_BalloonTipClicked;
this.notifyIcon.MouseDoubleClick += notifyIcon_MouseDoubleClick;
// ... more code
}
Both handlers launch or create/show my form:
private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
openDashboard();
}
}
private void notifyIcon_BalloonTipClicked(object sender, EventArgs e)
{
openDashboard();
}
private void openDashboard()
{
if (dashBoardForm != null)
{
log.Debug("Dashboard form created already, so Activate it");
dashBoardForm.Activate();
}
else
{
log.Debug("Dashboard form does not exist, create it");
dashBoardForm = new MainForm();
dashBoardForm.Show();
}
}
There is a problem with the above code. Maybe more than 1. Issue: it is possible to display 2 dashboard forms which is not what I want. If user double clicks on tray icon while balloon message is displaying causes a race condition in openDashboard. I can reproduce this easily. So I added a lock around the code in openDashboard code and, to my surprise, that did NOT prevent 2 dashboard forms from displaying. I should not be able to create 2 MainForms. Where am I going wrong here?
here is the updated code with lock statement:
private void openDashboard()
{
lock (dashBoardFormlocker)
{
if (dashBoardForm != null)
{
log.Debug("Dashboard form created already, so Activate it");
dashBoardForm.Activate();
}
else
{
log.Debug("Dashboard form does not exist, create it");
dashBoardForm = new MainForm();
dashBoardForm.Show();
}
}
}
Note: lock object was added to the class and initialized in constructor.
private object dashBoardFormlocker;
UPDATE: Showing more code. this is how code gets started :
static void Main()
{
if (SingleInstance.Start())
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
XmlConfigurator.Configure();
// For a system tray application we don't want to create
// a form, we instead create a new ApplicationContext. The Run method takes
Application.Run(new SysTrayApplicationContext());
SingleInstance.Stop();
SingleInstance.Dispose();
}
}
}
UPDATE 2: Provide more code for clarity
public partial class MainForm : Form
{
public MainForm()
{
log.Trace("MainForm constructor...");
InitializeComponent();
// ... code not shown
this.label_OSVersion.Text = getOSFriendlyName();
// .. more code
}
private string getOSFriendlyName()
{
try
{
string result = string.Empty;
var mgmtObj = (from x in new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem").Get().OfType<ManagementObject>()
select x.GetPropertyValue("Caption")).FirstOrDefault();
result = mgmtObj != null ? mgmtObj.ToString() : string.Empty;
OperatingSystem os = Environment.OSVersion;
String sp = os.ServicePack ?? string.Empty;
return !string.IsNullOrWhiteSpace(result) ? result + sp : "Unknown";
}
catch (System.Exception ex)
{
log.Error("Error trying to get the OS version", ex);
return "Unknown";
}
}
}
The main UI thread must always pump a message loop to support communication from COM components.
So when you do a blocking operation from the UI thread like locking or joining a thread, (EDIT: edited based on Peter Duniho's fix) the UI thread will enter an 'alertable' state, allowing COM to dispatch certain type of messages, which in turn can cause re-entrancy issues like in your scenario.
Look at the answer to this question (Why did entering a lock on a UI thread trigger an OnPaint event?) for a much more accurate explanation.
Looking at the source code of ManagementObjectSearcher.Get there is a lock (inside Initialize), and since you call it from the constructor of your form, it may lead to the second event triggering while the form's constructor has not finished. The assignment to the dashBoardFormlocker variable only happens after the constructor finishes, so that would explain why it was null on the second entry.
The moral of the story is never do blocking operations on the UI thread.
Without a good, minimal, complete code example that reliably reproduces the problem, it's impossible to know for sure what the problem is. But the guess by answerer tzachs seems reasonable. If so, you can fix your problem by changing your method to look like this:
private bool _dashboardOpen;
private void openDashboard()
{
if (_dashboardOpen)
{
if (dashBoardForm != null)
{
log.Debug("Dashboard form created already, so Activate it");
dashBoardForm.Activate();
}
}
else
{
log.Debug("Dashboard form does not exist, create it");
_dashboardOpen = true;
dashBoardForm = new MainForm();
dashBoardForm.Show();
}
}
In that way, any re-entrant attempt to open the window will be detected. Note that you still need the check for null before actually activating; you can't activate a window that hasn't actually finished being created yet. The subsequent call to Show() will take care of activation anyway, so ignoring the activation in the re-entrant case shouldn't matter.
I have two forms.
One of them is the main form (let's call it MainForm)
the other one is for showing some warning (let's call it dialogForm)
. dialogForm has a label in it. When i click a button in MainForm, dialogForm opens.
But label in dialogForm is blank. It doesn't have time to load actually. I want to check if the dialogForm fully loaded then proccess can continue in MainForm.
For example:
dialogForm tempFrm = new dialogForm();
tempFrm.Show(); // I want to wait till the dialogForm is fully loaded. Then continue to "while" loop.
while(..)
{
...
}
Why not create a boolean value, and a method to access it..
private bool Ready = false;
public ConstructorMethod()
{
// Constructor code etc.
Ready = true;
}
public bool isReady()
{
return Ready;
}
you can try the following
private bool Is_Form_Loaded_Already(string FormName)
{
foreach (Form form_loaded in Application.OpenForms)
{
if (form_loaded.Text.IndexOf(FormName) >= 0)
{
return true;
}
}
return false;
}
you can also look in this
Notification when my form is fully loaded in C# (.Net Compact Framework)?
So you need to consume the forms Shown event:
tempFrm.Shown += (s, e) =>
{
while(..)
{
}
}
But you're going to have another problem. It's going to block the thread. You need to run this while loop on another thread by leveraging a BackgroundWorker or Thread.
Your while(...) blocks the UI thread so child form will never got messages and will not be loaded.
To achive you goal you should subscribe to the Load event and continue your code in the handler.
void Click()
{
var tempFrm = new dialogForm();
tempFrm.Load += frmLoad;
tempFrm.Show();
}
void frmLoad(object s, EventArgs ea)
{
// form loaded continue your code here!
}
You can use Form.IsActive property.
Or just;
public bool IsFormLoaded;
public MyForm()
{
InitializeComponent();
Load += new System.EventHandler(FormLoaded);
}
private void FormLoaded(object sender, EventArgs e)
{
IsFormLoaded = true;
}
and check if YourForm.IsFormLoaded, true or false
I have written some code that works quite well : the program opens an async socket with the server, and writes in a textarea whatever the server sends.
The problem is, when i close the form, I get a lot of errors, because the callback is trying to write in the textarea that, obviously, is not there anymore.
Here is the method that writes on the textarea :
private void appendText(string s)
{
if (InvokeRequired)
{
this.Invoke(new Action<string>(appendText), new object[] { s });
return;
}
SocketStream.AppendText(s + "\r\n");
}
and here is the part of the callback's code calling said method :
string[] arr = txt.Split(new char[1]);
foreach (string t in arr)
{
if (!String.IsNullOrEmpty(t) && !String.IsNullOrWhiteSpace(t))
{
appendText( t);
}
}
is there a way to prevent those errors from happening?
I've already tried adding a
if(SocketStream != null)
but it didn't seemed to work.
When you close your form you probably need to stop reading from your Async socket, as well as stop writing to your TextBox. You'll need to have some state, some boolean perhaps, that makes all the processes stop. Now I don't know the specifics of your situation, but you could think of something like:
public class YourForm
{
private bool _formClosing = false; // Keep track of form closing
public YourForm()
{
this.FormClosing += FormClosingHandler;
}
protected void FormClosingHandler(object sender, FormClosingEventArgs e)
{
_formClosing = true;
}
private void appendText(string s)
{
if (_formClosing) // If form is closing, we dont want to append anymore
return;
if (InvokeRequired)
{
this.Invoke(new Action<string>(appendText), new object[] { s });
return;
}
SocketStream.AppendText(s + "\r\n");
}
// Socket handling; also check for _formClosing
}
You need to include the same check for your socket as well, to stop it from reading more data and gracefully disposing of the socket/connection. Again I'm making some assumptions/guesses here, but this might push you in the right direction.
Can you unsubscribe from the callback event before you close the Form?
You can do this with the -= operator, in the Closing handler of your Form.
I've got a .Net 3.5 C# Winforms app. It's got no GUI as such, just a NotifyIcon with a ContextMenu.
I've tried to set the NotifyIcon to visible=false and dispose of it in the Application_Exit event, as follows:
if (notifyIcon != null)
{
notifyIcon.Visible = false;
notifyIcon.Dispose();
}
The app gets to the code inside the brackets, but throws a null ref exception when it tries to set Visible = false.
I've read in a few places to put it in the form closing event, but that code never gets hit (maybe as I don't have a form showing as such?).
Where can I put this code so it actually works? If I don't put it in, I get the annoying lingering icon in the tray until you move the mouse over it.
Cheers.
EDIT
Just something extra I've noticed...........
I'm using ClickOnce in the app.........if I just exit the app via the ContextMenu on the NotifyIcon, no exception is logged.
Just when the Application_Exit event is fired after the applicaiton has checked for an upgrade here..
private void CheckForUpdate()
{
EventLogger.Instance.LogEvent("Checking for Update");
if (ApplicationDeployment.IsNetworkDeployed && ApplicationDeployment.CurrentDeployment.CheckForUpdate())
{
EventLogger.Instance.LogEvent("Update available - updating");
ApplicationDeployment.CurrentDeployment.Update();
Application.Restart();
}
}
Does this help?
On Windows 7, I had to also set the Icon property to null. Otherwise, the icon remained in the tray's "hidden icons" popup after the application had closed. HTH somebody.
// put this inside the window's class constructor
Application.ApplicationExit += new EventHandler(this.OnApplicationExit);
private void OnApplicationExit(object sender, EventArgs e)
{
try
{
if (trayIcon != null)
{
trayIcon.Visible = false;
trayIcon.Icon = null; // required to make icon disappear
trayIcon.Dispose();
trayIcon = null;
}
}
catch (Exception ex)
{
// handle the error
}
}
This code works for me, but I don't know how you are keeping your application alive, so... without further ado:
using System;
using System.Drawing;
using System.Windows.Forms;
static class Program
{
static System.Threading.Timer test =
new System.Threading.Timer(Ticked, null, 5000, 0);
[STAThread]
static void Main(string[] args)
{
NotifyIcon ni = new NotifyIcon();
ni.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
ni.Visible = true;
Application.Run();
ni.Visible = false;
}
static void Ticked(object o) {
Application.Exit();
}
}
This is what I'm doing in WPF.
I am using this in conjunction to David Anson's Minimize to tray sample app, which lets you hook up a tray icon to a window (you may have multiple windows open).
Just added this code to the constructor for MinimizeToTrayInstance.
_window.Closed += (s, e) =>
{
if (_notifyIcon != null)
{
_notifyIcon.Visible = false;
_notifyIcon.Dispose();
_notifyIcon = null;
}
};
Sometimes Application_Exit event can be raised several times
Just put notifyIcon = null; in the end
if (notifyIcon != null)
{
notifyIcon.Visible = false;
notifyIcon.Dispose();
notifyIcon = null;
}
This code worked for me
this.Closed += (a, b) =>
{
if (notifyIcon1 != null)
{
notifyIcon1.Dispose();
notifyIcon1.Icon = null;
notifyIcon1.Visible = false;
}
};
Have you overridden the dispose method of the object where you've initialised the notifyIcon to also dispose the notifyIcon?
protected override void Dispose(bool disposing)
{
if (disposing)
{
notifyIcon.Dispose();
notifyIcon = null;
}
base.Dispose(disposing);
}
before im sorry for my bad english.
if u use "end" for exit program. then dont close notify icon.
before u will close notifyicon later close form.
u need to use me.close() for run form closing
example
its work...
notifyIcon1.Icon = Nothing
notifyIcon1.Visible = False
notifyIcon1.Dispose()
Me.Close()
but its not work
End
or only
Me.Close()