I would like to know if there is a better way than the following to check if the window got closed, or if a Closing cancled the closing procedure?
Here we go with my way:
var window = Application.Current.Windows.FirstOrDefault(x => x is FooWindow);
if (window != null)
{
var gotClosed = false;
window.Closed += (sender, args) => gotClosed = true;
window.Close();
if (gotClosed == false)
{
//Close got cancled, by closing...
}
}
From checking the .NET source, I'm not too sure that IsDisposed is safe. There don't seem to be a lot of safe options though. The one I have been using so far without issues is checking the Visibility property for Visible after closing.
A cleaner approach might be creating your own class and overriding OnClosing() or OnClosed() though:
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
// Fires Closing event
base.OnClosing(e);
if (!e.Cancel)
{
// Window was allowed to close.
// Set IsClosed = true or something like that
}
}
There you can store the result in a property for example.
I'm not sure it's better than your solution, but after calling window.Close() the property IsDisposed gets true. So, you can check it:
if(window.IsDisposed)
{
....
}
Related
I have a Order form. Once a order is complete, I use a thread to email the order to the supplier. The Thread is use to prevent the system hanging while the order is exported to pdf and sent.
The Problem: I would like to place an message on the MDIParent Toolstripstatuslabel once the threat completes without error to confirm the order was sent. But I get an error: "System.NullReferenceException: Object reference not set to an instance of an object". Which I could be wrong, refers to the Child windows disposed the toolstripstatuslabel reference on the parent form when it closed, so the threat cant access it anymore. I know the easy solution would be to use a MessageBox to confirm all went good and well...but why make it easy if you can do it elegant?
So my question: How can I reference a control in the parent form from the threat? I tried looking at invoke, but not sure how to implement it or if it is actually the correct direction.
EDIT:
My code from the childform
public partial class frm_n_order : Form
{
.
.
private void bProcess_Click(object sender, EventArgs e)
{
.
.
.
new Thread(new ThreadStart(delegate
{
fExportOrder(strOrderNo);
fSendMailv2(strPlant, strSupCode, strOrderNo);
})).Start();
this.close();
}
private void fExportOrder(string strOrderNo)
{
//export order to pdf
}
private void fSendMailv2(string strPlant, string strSupCode, string strOrderNo);
{
// get pdf
// get email address
try
{
// send email
((MDIParent1)MdiParent).tsslMain.Text = "Order No:" + strOrderNo + " was successfully send to " + strEmails; //here I need to find a different way of accessing the Toolstripstatuslabel in the parent form
}
catch
{
MessageBox.Show("Email did not send");
}
}
}
EDIT:
Ok, so after spending more than a day trying to figure out how to use Invoke, I realize while it seems like good practice when working with threads, its not my answer. My problem is directly related to the childform closing disposing all the controls so it looses its reference to the MdiParent. To solve the problem I did the following:
In my child class I added:
public static Form IsFormAlreadyOpen(Type FormType)
{
foreach (Form OpenForm in Application.OpenForms)
{
if (OpenForm.GetType() == FormType)
return OpenForm;
}
return null;
}
I dont think it is the most elegant solution but the theory is that my Parent form will always be open when I need to access the Toolstripstatuslabel. So I basically loop through all the open forms to find the reference to the active MdiParent instance which then gets passed back to the caller. In the thread I then use the following code.
MDIParent1 fm = null;
if ((fm = (MDIParent1)IsFormAlreadyOpen(typeof(MDIParent1))) != null)
{
fm.Toolstripstatuslabel1.Text = "Order No:" + strOrderNo + " was successfully send to " + strEmails;
}
I'm still looking for a better approach, but for now this works.
Ok, so after spending more than a day trying to figure out how to use Invoke, I realize while it seems like good practice when working with threads, its not my answer. My problem is directly related to the childform closing disposing all the controls so it looses its reference to the MdiParent. To solve the problem I did the following:
In my child class I added:
public static Form IsFormAlreadyOpen(Type FormType)
{
foreach (Form OpenForm in Application.OpenForms)
{
if (OpenForm.GetType() == FormType)
return OpenForm;
}
return null;
}
I dont think it is the most elegant solution but the theory is that my Parent form will always be open when I need to access the Toolstripstatuslabel. So I basically loop through all the open forms to find the reference to the active MdiParent instance which then gets passed back to the caller. In the thread I then use the following code.
MDIParent1 fm = null;
if ((fm = (MDIParent1)IsFormAlreadyOpen(typeof(MDIParent1))) != null)
{
fm.Toolstripstatuslabel1.Text = "Order No:" + strOrderNo + " was successfully send to " + strEmails;
}
I'm still looking for a better approach, but for now this works.
It's hard for me to overlook someone saying "but why make it easy if you can do it elegant?"
Awesome!
I figure if we can do something elegantly, then in the future it should be easy.... right?
Anyways, hoping you find the below helpful.
A note: It looked to me like you were declaring your thread as a local variable and not storing it outside the local scope. If we want something to live past the end of the scope, it's a good idea to store a reference to it (which is done using a private Task field in the below example).
Sure, the thread would get added to the threadpool and stored somewhere in the framework even if it's just a local variable, so I think it wouldn't abort due to garbage collection as you have it, but I don't like the idea of instances floating around that I don't have references to.
public class MyChildForm : Form
{
private Task longRunningTask;
private Task closeTask;
public string ResultOfTimeConsumingOperation { get; private set; }
protected override Dispose(bool disposing)
{
if (disposing)
{
longRunningTask?.Dispose();
closeTask?.Dispose();
}
base.Dispose(disposing);
}
private void TimeConsumingOperation1()
{
Thread.Sleep(TimeSpan.FromSeconds(8));
ResultOfTimeConsumingOperation = "Hooray we finished the work lol";
this.closeTask =
Task.Factory.FromAsync(
BeginInvoke(new Action(Close)),
EndInvoke);
}
protected override void OnLoad()
{
base.OnLoad();
this.longRunningTask =
Task.Run(TimeConsumingOperation1);
}
}
public class MyParentForm : Form
{
private List<Form> childForms;
public MyParentForm() : base()
{
childForms = new List<Form>();
}
protected override void OnLoad()
{
base.OnLoad();
RunChildForm();
}
private void RunChildForm()
{
var childForm = new MyChildForm();
childForms = childForms.Append(childForm).ToList();
childForm.FormClosing += ChidlForm_FormClosing;
childForm.Show();
}
private void ChildForm_FormClosing(object sender, FormClosingEventArgs e)
{
var childForm = sender as MyChildForm;
childForm.FormClosing -= ChildForm_FormClosing;
if (childForms.Contains(childForm))
childForms =
childForms.
Except(new Form[] { childForm }).
ToList();
// tada
myStatusLabel.Text = childForm.ResultOfLongRunningProcess;
}
// main window is closing
protected override void OnFormClosing(FormClosingEventArgs e)
{
// let's close any windows we left open
var localForms = childForms.ToList();
childForms = new List<Form>();
foreach (var form in localForms)
form.Close();
}
}
I am creating a wpf form which is going to be used for adding/editing data from datagrid. However when I check for ShowDialog() == true I am getting the above exception.
The code is taken from a book (Windows Presentation Foundation 4.5 Cookbook).
UserWindow usrw = new UserWindow();
usrw.ShowDialog();
if (usrw.ShowDialog() == true)
{
//do some stuff here;
}
And on the WPF window:
private void btn_Save_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Close();
}
How I can handle this?
===============================
The solution to the problem was simply to remove usrw.ShowDialog(); and it start working as expected
UserWindow usrw = new UserWindow();
//usrw.ShowDialog();
if (usrw.ShowDialog() == true)
{
//do some stuff here;
}
You are trying to open your window 2 times with every call to ShowDialog()
try
UserWindow usrw = new UserWindow();
bool result =(bool)usrw.ShowDialog();
if (result)
{
//do some stuff here;
}
or
UserWindow usrw = new UserWindow();
usrw.ShowDialog();
if ((bool)usrw.DialogResult)
{
//do some stuff here;
}
keep in mind that DialogResult is Nullable. If there is a chance that you are closing the window without setting the DialogResult, check for null.
With this code:
private void PlatypusMainForm_FormClosing(object sender, FormClosingEventArgs e) {
if ((UnsavedChanges()) && (!(UserWantsToMoveOnWithoutSaving(CONFIRM_CLOSE_UNSAVED_CHANGES_LOST, "Close Without Saving?")))) {
e.Cancel = true;
return;
}
if (oracleConnectionMainForm.State == ConnectionState.Open) {
oracleConnectionMainForm.Close();
oracleConnectionMainForm.Dispose();
}
}
...if e.Cancel = true is commented out, the form closes anyway.
...if return is commented out, the rest of the code (Close and Dispose) runs (so, if I then try to save changes, I get the err msg that the connection is not open).
So, I have to do both (cancel and return) to get the code to work as I think it should with either one.
Is this normal/as expected?
Yes, that's as expected. The e.Cancel tells the framework that you've handled the event, and you don't want the automatic behavior. Without it, after your method returns, the framework will continue to handle the event, and close the window.
The return aborts the execution of this current method, so the stuff at the end doesn't execute.
Cancel is defaulted to false, so if you comment it out it never gets set to true.
Take return out and it doesn't return until after the rest of the code executes, or in your case errors.
if (SomeCondition)
{
e.Cancel = true;
}
else
{
// Do Something
}
would be clearer, and be slightly less complex.
In a similar question:
What is this pattern called? Soft Lock?
I was asking about the name of the pattern for the code listing below.
public class MyClass
{
public event EventHandler MyEvent;
private bool IsHandlingEvent = false;
public MyClass()
{
MyEvent += new EventHandler(MyClass_MyEvent);
}
void MyClass_MyEvent(object sender, EventArgs e)
{
if (IsHandlingEvent) { return; }
IsHandlingEvent = true;
{
// Code goes here that handles the event, possibly invoking 'MyEvent' again.
// IsHandlingEvent flag is used to avoid redundant processing. What is this
// technique, or pattern called.
// ...
}
IsHandlingEvent = false;
}
}
It seems that most of the conversation was centered around why we should an should not do this, so I think that this question provides a better forum to tackle the problem and address all of the issues. What is the better / proper way to handle this?
There are series of problems with that pattern. If you want to invoke the handler only once, you would do something like this:
protected static object _lockObj = new object();
protected static bool _isHandled = false;
void MyClass_MyEvent(object sender, EventArgs e)
{
if(_isHandled)
return;
lock(_lockObj)
{
if(_isHandled)
return;
_isHandled = true;
MyOtherPossiblyRecursiveMethod(); // Actually does all your work
_isHandled = false;
}
}
void MyOtherPossiblyRecursiveMethod()
{
}
This way, only one thread should be able to access the actual work method.
I will use something like:
using( var sl = new SoftLock() )
{
sl.Execute(()=>{....});
}
the execute will raise the internal boolean to prevent re-entering. In the dispose that flag would be resetted. Execute will call the lambda just if the flag is false. This is to ensure flag go to false even if exception happens ( causing handler never executed ) and maybe is a little better to see. Of course this is not thread safe, as the original code, but this because we are talking about preventing double execution from the same thread.
The original code is a sufficient (and very lightweight) way to prevent recursion in a single-threaded app. So if during your event handling function you could get into code that might be firing the event again you will not enter infinite recursion.
But the code is not sufficient to prevent access from multiple threads, due to the potential for race conditions. If you need to ensure only one thread gets to run this event, then you should use a stronger locking mechanism, like a Mutex or Semaphore.
The following works in single- and multi-threaded scenarios and is exception-safe... also if need be it can be modified to allow for a certain level of reentrancy (for example 3 levels)...
public class MyClass
{
public event EventHandler MyEvent;
private int IsHandlingEvent = 0;
public MyClass()
{
MyEvent += new EventHandler(MyClass_MyEvent);
}
void MyClass_MyEvent(object sender, EventArgs e)
{
// this allows for nesting if needed by comparing for example < 3 or similar
if (Interlocked.Increment (ref IsHandlingEvent) == 1 )
{
try {
}
finally {};
}
Interlocked.Decrement (ref IsHandlingEvent);
}
}
I'm still stuck.
Assume that I've got a user control with a button. And an event called damnIt_ButtonClicked.
In the main window I want to emulate the control's lifetime like it is a modal dialog, although it's not.
I want to wrap everything into one method, it returns true if the Button on the control clicked.
public bool Show() {
var control = new ControlWithSingleButton();
bool result;
control.damnIt_ButtonClicked += (object sender, EventArgs args) =>
{
result = true;
};
MainWindowGrid.Children.Add(control);
MainWindowGrid.Visibility = Visibility.Visible;
return result;
}
Now. As you see the problem is this method will return always false;
But I need to return a result only when damnIt_ButtonClicked event fires. It means I have to put the thread on wait, till the user clicks button.
Right? Or how it should be done. Help me please....
You're going to need to re-architect your solution. Without knowing a broader scope of what you're trying to do, here's a possible solution.
private bool buttonResult;
public void Show() {
var control = new ControlWithSingleButton();
bool result;
control.damnIt_ButtonClicked += (object sender, EventArgs args) =>
{
this.ProcessButtonClick();
};
MainWindowGrid.Children.Add(control);
MainWindowGrid.Visibility = Visibility.Visible;
}
private void ProcessButtonClick()
{
this.buttonResult = true;
//do whatever you would have before if Show had returned true
}
You know what? I give up!
I decided to make the control a window, although it was strictly prohibited in given specifications to use any other windows but the Main. Anyway it's gonna be a chromeless, borderless transparent window, so nobody can see the difference.
Thank you so much.