I'm writing a chat application for a school project. One of the forms I have is the Contacts form, from which I open different SingleDialogue forms (they are the 1 on 1 chats). I want to make it so that when I close the Contacts form, it closes all the SingleDialogue forms I have opened.
For this I made it so that the Contacts_FormClosing event triggers the Disconnected event in my client object, and in the Contacts form I made a method:
private void On_ImDisconnected(object sender, EventArgs e)
{
foreach (SingleDialogue sd in singleChats)
sd.Close();
}
singleChats is the name of a list of SingleDialogue forms opened from the Contacts form. Every time I open another SingleDialogue, it's added to singleChats.
And in the constructor I subscribed it to the Disconnected event in the client object:
im.Disconnected += new EventHandler(On_ImDisconnected);
So far, what should happen is that when I trigger the Disconnected event in the client object On_ImDisconnected fires up and closes all the SingleDialogues, correct?
But I get an exception saying "Cross-thread operation not valid: Control 'SingleDialogue' accessed from a thread other than the thread it was created on".
Alright, then I changed On_ImDisconnected:
private void On_ImDisconnected(object sender, EventArgs e)
{
this.BeginInvoke(new MethodInvoker(delegate
{
foreach (SingleDialogue sd in singleChats)
sd.Close();
}));
}
But now nothing happens. Can you guys help me solve this?
EDIT:
Declaration of the list:
List<SingleDialogue> singleChats = new List<SingleDialogue>();
Adding new members:
private void chatButton_Click(object sender, EventArgs e)
{
SingleDialogue sd = new SingleDialogue(im, chatTextBox.Text);
singleChats.Add(sd);
chatTextBox.Text = "";
sd.Show();
}
I'd say skip the list completely. In the constructor of your SingleDialogue Form you could add a handler to when the passed parent disconnects, and then close the SingleDialogue from within itself.
So if I had to guess what the constructor looks like, you could do something like this:
public SingleDialogue(<someFormType> im, string stringThingy)
{
...some initialization code...
im.Disconnected += new EventHandler(im_Disconnected); //Subscribe to the parent's Disconnected event.
}
private void im_Disconnected(object sender, EventArgs e) //When the parent disconnects.
{
if(this.InvokeRequired) { this.Invoke(() => this.Close()); }
else { this.Close(); }
}
Note that I'm not a native C# developer, so if I did something wrong please tell me. :)
As I understood the scenario, you're keeping a list of forms in Contact form and want to close it.
I suppose your approach is wrong, when you're subscribing every form to the Disconnected event. Why not close the form on event trigger from inside of it?
You just need this code in your every SingleDialogue instance:
//subscribe to event handler, most probably in constructor
im.Disconnected += new EventHandler(On_ImDisconnected);
//close on event fire
private void On_ImDisconnected(object sender, EventArgs e)
{
this.Close();
}
You don't need to go through the list and close the forms from a parent form.
If I got the question wrong, please correct me.
Related
I have 2 forms, one is MainForm and second is DebugForm. The MainForm has a button that sets up and shows the DebugForm like this, And passes a reference to an already opened SerialPort:
private DebugForm DebugForm; //Field
private void menuToolsDebugger_Click(object sender, EventArgs e)
{
if (DebugForm != null)
{
DebugForm.BringToFront();
return;
}
DebugForm = new DebugForm(Connection);
DebugForm.Closed += delegate
{
WindowState = FormWindowState.Normal;
DebugForm = null;
};
DebugForm.Show();
}
In the DebugForm, I append a method to handle the DataReceived event of the serialport connection (in DebugForm's constructor):
public DebugForm(SerialPort connection)
{
InitializeComponent();
Connection = connection;
Connection.DataReceived += Connection_DataReceived;
}
Then in the Connection_DataReceived method, I update a TextBox in the DebugForm, that is using Invoke to do the update:
private void Connection_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
_buffer = Connection.ReadExisting();
Invoke(new EventHandler(AddReceivedPacketToTextBox));
}
But I have a problem. As soon as I close the DebugForm, it throws an ObjectDisposedException on the Invoke(new EventHandler(AddReceivedPacketToTextBox)); Line.
How can I fix this? Any tips/helps are welcome!
UPDATE
I found out if I remove the event in a button event click , and close the form in that button click, everything is fine and my debugform gets closed without any exception...how odd!
private void button1_Click(object sender, EventArgs e)
{
Connection.DataReceived -= Connection_DebugDataReceived;
this.Close();
}
Closing a form disposes of the Form object but cannot forcibly remove references that other classes have to it. When you register your form for events, you are basically giving a reference to your form object to the source of the events (the SerialPort instance in this case).
This means that, even though your form is closed, the event source (your SerialPort object) is still sending events to the form instance and the code to handle these events is still being run. The problem then is that when this code tries to update the disposed form (set its title, update its controls, call Invoke, &c.) you will get this exception.
So what you need to do is ensure that the event gets deregistered when your form closes. This is as simple as detecting that the form is closing and unregister the Connection_DataReceived event handler. Handily you can detect the form is closing by overriding the OnFormClosing method and unregistering the event in there:
protected override OnFormClosing(FormClosingEventArgs args)
{
Connection.DataReceived -= Connection_DataReceived;
}
I would also recommend moving the event registration to an override of the OnLoad method as otherwise it may receive events before the form has been fully constructed which could cause confusing exceptions.
You haven't shown the code for the AddReceivedPacketToTextBox method.
You could try checking for a disposed form in that method:
private void AddReceivedPacketToTextBox(object sender, EventArgs e)
{
if (this.IsDisposed) return;
...
}
Detaching the DataReceived event handler when closing the form is probably a good idea, but isn't sufficient: there is still a race condition which means your AddReceivedPacketToTextBox can be called after the form is closed/disposed. The sequence would be something like:
Worker thread: DataReceived event fired, Connection_DataReceived starts executing
UI thread: Form closed and disposed, DataReceived event detached.
Worker thread: calls Invoke
UI thread: AddReceivedPacketToTextBox executed while form is disposed.
I found out if I remove the event in a button event click , and close the form in that button click, everything is fine and my debugform gets closed without any exception...how odd!
That's not odd. Multithreading bugs ("Heisenbugs") are timing-related and small changes like that can affect the timing. But it's not a robust solution.
The problem could be solved by adding a timer:
bool formClosing = false;
private void Connection_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (formClosing) return;
_buffer = Connection.ReadExisting();
Invoke(new EventHandler(AddReceivedPacketToTextBox));
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
if (formClosing) return;
e.Cancel = true;
Timer tmr = new Timer();
tmr.Tick += Tmr_Tick;
tmr.Start();
formClosing = true;
}
void Tmr_Tick(object sender, EventArgs e)
{
((Timer)sender).Stop();
this.Close();
}
Thanks to JohnWein from MSDN
It's been a while since I've worked with Windows Forms applications. I have a Checkbox on the Main form and, based upon a certain condition, if the Second form needs to be opened to request additional data from the user, how should I pass (or get) back a message to the Main form from the Second form so I can tell whether or not it's okay to Check or Uncheck the Checkbox?
From what I can remember, I could use something like Pass by ref. Or is there a better way to accomplish this?
Since you are showing the child form as a dialog, and the parent form doesn't need it until the form as closed, all you need to do is add a property with a public getter and private setter to the child form, set the value in the child form whenever it's appropriate, and then read the value from the main form after the call to ShowDialog.
One way to do this would be to use an event.
In your child form, declare an event to be raised upon specific user interaction, and simply "subscribe" to this event in your main form.
When you instantiate and call you child form, you'd do like this:
private void button1_Click(object sender, EventArgs e)
{
Form2 frm = new Form2();
frm.MyEvent += frm_MyEvent;
frm.ShowDialog();
frm.MyEvent -= frm_MyEvent;
}
private void frm_MyEvent(object sender, EventArgs e)
{
textBox1.Text = "whatever"; //just for demo purposes
}
In your child form, you declare the event and raise it:
public event EventHandler MyEvent;
private void button1_Click(object sender, EventArgs e)
{
if (MyEvent!= null)
MyEvent(this, EventArgs.Empty);
}
Hope this helps
I have a Main page that contains a listBox.
When a user selects a profile form the list box, this opens up a child window called pWindow.
This window as the option to delete the current profile via a hyperlink button that opens up a another confirmation window called dprofile.
My question being is it possible that once a user has confirmed to delete the current profile they are in, and confirmed it in the button click on dProfile, how can I update the listBox in the first Main page so that the list no longer contains the deleted profile (which it is not doing at present.
In the dProfile window I have created an event -
public event EventHandler SubmitClicked;
Where in the OK button click I have-
private void OKButton_Click(object sender, RoutedEventArgs e)
{
if (SubmitClicked != null)
{
SubmitClicked(this, new EventArgs());
}
}
So on the Main page I have added-
private void deleteProfile_SubmitClicked(object sender, EventArgs e)
{
WebService.Service1SoapClient client = new WebService.Service1SoapClient();
listBox1.Items.Clear();
client.profileListCompleted += new EventHandler<profileListCompletedEventArgs>(client_profileListCompleted);
client.profileListAsync(ID);
}
I thought this may have updated the listBox as it was confirmed in the dProfile form however when the form closes, the listBox stays the same and I have to manually refresh the webpage to see the update. How can I do this?
If I understood it correctly then you have three pages. Main, pWindow and dProfile. Earlier you were trying to close pWindwow from dProfile and that was working properly. Now you want to refresh the listBox1 on Main Page.
To achieve that you may follow a similar strategy. You are probably opening pWindow from Main page with something on the following line
pWindow pWin = new pWindow();
pWin.Show();
Now you may define a new event in pWindow class.
public event EventHandler pWindowRefeshListBox;
Then in your event handler for deleteProfile_SubmitClicked you may raise the event to refresh listbox1, something on the following line:
private void deleteProfile_SubmitClicked(object sender, EventArgs e)
{
if(pWindowRefreshListBox != null)
pWindowRefreshListBox(this, new EventArgs());
this.Close();
}
Then in your main page register the event against pWin object, which you defined earlier.
pWin.pWindowRefreshListBox += new new EventHandler(pWindow_pWindowRefreshListBox);
Then define the event in Main page.
private void pWindow_pWindowRefreshListBox(object sender, EventArgs e)
{
listBox1.Items.Clear();
}
This should refresh the listbox. I haven't test the code or the syntax. So you may check it
before implementing.
EDIT
you may define the event in dProfile as static
public static event EventHandler SubmitClicked;
Then you will be able to register it in Main and pWindow against Class Name
dProfile.SubmitClicked += new ..............
Then implement it accordingly, in pWindow, close the window and in main refresh listbox
EDIT:
You may create instance of deleteProfile on the main page register the following in your main
deleteProfile.SubmitClicked += new EventHandler(deleteProfile _SubmitClicked)
this should work
I have one form that has an option to open another (dialogue). I want an event to fire when the second window closes. The first form is named frmMain() the other is frmAddEmployee(). Heres what I have:
in frmMain()
//create and open the second window
public void (object sender, EventArgs e)
{
frmAddEmployee addEmp = new frmAddEmployee();
addEmp.ShowDialogue();
}
//create event to handle addEmp being closed
public void addEmp_Closing(object sender, EventArgs e)
{
PopulateEmployeeList();
}
I'm not sure the event is being recognized as an event. What am I doing wrong?
Events in C# have to be registered manually - the C# compiler will not automatically register method as an event handler based just on the name of the method. You need:
frmAddEmployee addEmp = new frmAddEmployee();
addEmp.Closing += addEmp_Closing; // Register event handler explicitly
addEmp.ShowDialogue();
Automatic registration of events is done in ASP.NET and Visual Basic has Handles clause, but in C#, you need to use the += operator to specify that some method should be called when an event occurs.
Assuming ShowDialogue means ShowDialog, then it shows the form modally and you don't need an event handler:
//create and open the second window
public void (object sender, EventArgs e)
{
frmAddEmployee addEmp = new frmAddEmployee();
addEmp.ShowDialog();
PopulateEmployeeList();
}
If you don't show the second form modally, then you can hook up the event handler before showing the form like this:
public void (object sender, EventArgs e)
{
frmAddEmployee addEmp = new frmAddEmployee();
addEmp.FormClosed += AddEmpClosed;
addEmp.Show();
}
private void AddEmpClosed(object sender, FormClosedEventArgs e)
{
PopluateEmployeeList();
}
There is Closing and Closed events which you can register for on the Form. You are registered for neither, unless your registration is taking place somehwere else?
Before you call addEmp.ShowDialog() you need to set your method to handle the Closing event:
frmAddEmployee addEmp = new frmAddEmployee();
addEmp.Closing += addEmp_Closing;
addEmp.ShowDialogue();
I have a button click event handler with the following pseudo code:
private void btnSave_Click(object sender, EventArgs e)
{
if(txt.Text.length == 0)
this.Close();
else
// Do something else
// Some other code...
}
This is just some simple code, but the point is, when the text length equals zero, I want to close the form. But instead of closing the form the code executes the part // Some other code. After the click event handler is completely executed, then the form is closed.
I know, when I place return right after this.Close() the form will close, but I'd like to know WHY the form isn't direclty closed when you call this.Close(). Why is the rest of the event handler executed?
The rest of the event handler is executed because you did not leave the method. It is as simple as that.
Calling this.Close() does not immediately "delete" the form (and the current event handler). The form will be collected later on by the garbage collector if there are no more references to the form.
this.Close() is nothing than a regular method call, and unless the method throws an exception you will stay in the context of your current method.
Close only hides the form; the form is still alive and won't receive another Load event if you show it again.
To actually delete it from memory, use Dispose().
Answer is simple as you are executing your current method so this.Close() will be enqueued until either you explicitly returned or your current excuting method throws an exception.
Another possible solution is that if you open a new Form and want to close the current one: if you use newForm.ShowDialog() instead of newForm.Show() it doesn't close the currentForm with currentForm.Close() until the newForm is also closed.
Unless the Form is a modal form(opened with .ShowDialog()), Form.Close() disposes the form, as well. So, you cannot reopen it under any circumstances after that, despite of what others may have said. There is Form.Visible for this behavior(hiding/showing the form).
The point here is that .Close() does not return from the section it is called for several reasons. For example, you may call SomeForm.Close() from another form or a class or whatever.
Close() is just a method like any other. You have to explicitly return from a method that calls Close() if this is what you want.
Calling MessageBox.Show(frmMain,"a message","a title") adds the form "TextDialog" to the application's Application.OpenForms() forms collection, along-side the frmMain Main form itself. It remains after you close the Messagebox.
When this happens and you call the OK button delegate to close the main form, calling frmMain.Close() will not work, the main form will not disappear and the program will not terminate as it usually will after you exit the OK delegate. Only Application.Exit() will close all of the garbage messagebox "TextDialog"s.
private void btnCloseForm_Click(object sender, EventArgs e)
{
FirstFrm.ActiveForm.Close();
}
and if you want close first form and open secound form do this :
private void btnCloseForm_Click(object sender, EventArgs e)
{
FirstFrm.ActiveForm.Close();
}
private void FirstFrm_FormClosed(object sender, FormClosedEventArgs e)
{
SecounfFrm frm = new SecounfFrm ();
frm.ShowDialog();
}
or you can do somting like that :
private void btnCloseForm_Click(object sender, EventArgs e)
{
this.Hide();
}
private void FirstFrm_VisibleChanged(object sender, EventArgs e)
{
if(this.Visible == false)
{
this.Close();
}
}
private void FirstFrm_FormClosed(object sender, FormClosedEventArgs e)
{
SecounfFrm frm = new SecounfFrm ();
frm.ShowDialog();
}