I need to be able to let multiple instances of the same form be open as my application can be used in different places at once. On the other hand I need to be able to process the operations during the “OK” event one at a time to ensure data is stored safely and not overwritten by another form instance by accident.
I show my form using the .Show() method as I am using a few delegates in it:
private void newToolStripMenuItem_Click(object sender, EventArgs e)
{
bookingForm = new BookingForm(AddMemberBooking, AddUserBooking, CloseBooking);
bookingForm.Show();
}
I have tried to use the mutex to allow only one event of the OK button being pressed happen at a time, i have combined this with a Thread to meet the criteria i need.
When i click on the “OK” button I am given the following error:
Cross-thread operation not valid: Control ‘comboBoxDay’ accessed from a thread other than the thread it was created on.
This is the code for my booking form class:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace Collection
{
//Allows the class to be serialized
[Serializable()]
public delegate void AddMemberBookingMethod(int date, int time, int mNo);
public delegate void AddUserBookingMethod(int date, int time, string fName, string lName, string pCode);
public delegate void CloseBookingFormMethod();
public partial class BookingForm : Form
{
public CloseBookingFormMethod CloseBookingForm;
public AddMemberBookingMethod AddMemberBooking;
public AddUserBookingMethod AddUserBooking;
private Mutex bookingMut = new Mutex();
private Thread thread;
public bool IsUser;
public BookingForm(AddMemberBookingMethod ambm, AddUserBookingMethod aubm, CloseBookingFormMethod cbfm)
{
InitializeComponent();
AddMemberBooking = ambm;
AddUserBooking = aubm;
CloseBookingForm = cbfm;
checkBoxMember.Checked = true;
//Control.CheckForIllegalCrossThreadCalls = false;
}
private void checkBoxUser_CheckedChanged(object sender, EventArgs e)
{
if (checkBoxUser.Checked)
{
IsUser = true;
checkBoxMember.CheckState = CheckState.Unchecked;
textBoxMNo.Enabled = false;
textBoxFName.Enabled = true;
textBoxLName.Enabled = true;
textBoxPCode.Enabled = true;
}
else
{
IsUser = false;
checkBoxMember.CheckState = CheckState.Checked;
textBoxMNo.Enabled = true;
textBoxFName.Enabled = false;
textBoxLName.Enabled = false;
textBoxPCode.Enabled = false;
}
}
private void checkBoxMember_CheckedChanged(object sender, EventArgs e)
{
if (checkBoxMember.Checked)
{
IsUser = false;
checkBoxUser.CheckState = CheckState.Unchecked;
textBoxFName.Enabled = false;
textBoxLName.Enabled = false;
textBoxPCode.Enabled = false;
}
else
{
IsUser = true;
checkBoxUser.CheckState = CheckState.Checked;
textBoxMNo.Enabled = false;
textBoxFName.Enabled = true;
textBoxLName.Enabled = true;
textBoxPCode.Enabled = true;
}
}
private void buttonOK_Click(object sender, EventArgs e)
{
this.thread = new Thread(new ThreadStart(MakeBooking));
this.thread.Name = "bookingThread";
this.thread.Start();
}
private void MakeBooking()
{
this.bookingMut.WaitOne();
int date = this.comboBoxDay.SelectedIndex;
int time = this.comboBoxTime.SelectedIndex;
if (IsUser)
{
string fName = textBoxFName.Text;
string lName = textBoxLName.Text;
string pCode = textBoxPCode.Text;
AddUserBooking(date, time, fName, lName, pCode);
}
else
{
int mNo = int.Parse(textBoxMNo.Text);
AddMemberBooking(date, time, mNo);
}
this.bookingMut.ReleaseMutex();
CloseBookingForm();
}
private void buttonClose_Click(object sender, EventArgs e)
{
CloseBookingForm();
}
}
}
I realise I may not be doing this in the most efficient way but time is a bit of a factor.
I’ve researched the error and have heard of using delegates and .Invoke() but I’m still not entirely sure how to fix it.
EDIT:
I’ve found this code snippet when searching for a fix to my problem. I don’t understand where/how I would use it.
if(this.InvokeRequired)
{
this.Invoke(new MyEventHandler(this.CreateAForm()));
return;
}
EDIT2:
Seems the guy finally saw sense, by creating the from with the new word it apparently passes the criteria. I wish I’d have known this before trying to reinvent the wheel.
You are getting this exception because your thread is accessing controls. That’s not legal, control properties must only ever be accessed from the UI thread. You’re okay on the TextBox.Text property, that one happens to be cached. But not ComboBox.SelectedIndex. And closing the form from another thread is going to bomb too.
Your mutex has nothing to do with it, but keep it if you want to prevent threads from overlapping. Using a delegate’s Invoke method isn’t going to solve it, that just starts a thread as well. You’ll need to collect the info that the thread is going to need in a little helper class and pass that as the argument to the Thread.Start() method.
Closing the form is a bit tricky too, the user might well have already closed it while the thread was running. That’s going to cause an ObjectDisposed exception. A quick fix is to set the form’s Enabled property to false so the user can’t close it. You’ll need to use the form’s Invoke() method to ensure the closing is done on the UI thread.
Last but not least, if these threads don’t take a lot of time (a second or so), consider not using threads at all and display a wait cursor instead.