Hi all.
I wonder if there is someone that can help me?
I am a fairly new developer. I have been developing for about two years now and only over the past 2 months with C# in depth. I have come across a situation which requires that I implement multithreading in my application. So far, so good and easy. I have done a lot of research about it but nothing that I have come across actaully helps me with what I want and need to do. Let me explain.
I am writing an application which is used to import data from an excel spread sheet into a datagridview on the form. Once all the data is in the grid, the user will then specify the database connection and basically hit "GO". The idea is that on go, the program iterates through every record on the data grid and imports it in the database.
Now, that part is all wonderfull and it works beautifully in terms of functionality. However, the datagrid will at some times have up to 4500 rows. Thus making it a "long running function" while it commits the data. The issue is that I need to find a way to show a progress bar. I created a new form, added a Progress bar to it and set its Style to Marquee. Then All I do is declare a new instance of the form, show the form as a dialog, Commit the data, and then close the instance of the ProgressForm. Sounds simple.
However, The problem with this was that I couldn't "paint" my UI while the "Main thread" was sending the data to the DB. So I researched multi threading. Came across some good examples, but was still stuck with an issue. Basically I need the "worker thread" to be able to access the information which is in the "UI Thread". The data which is being commited, needs to come from the datagridview. But it can't be done. I get an error which says, control accessed from a different thread to the one on which is was created. Not only did I get the error, the general consensus across the internet is that it is bad coding practice.
I don't know how else I can get this right and if there is anyone who can help me, I would appreciate it. Also, should you require any further information about the work that I am trying to do, Please let me know....
Thanks in advance.
Ricardo Carvalho
Loading
Ricardo CarvalhoPosted Nov 27, 2007, 9:34 AM
I actually have a few methods which use the progress bar on the form.
Let me show you quick.
private delegate string[] InternalDelegate();
private InternalDelegate intlDelg;
In My Constructor I have:
ticker = new System.Timers.Timer();
ticker.Elapsed += new ElapsedEventHandler(ticker_Elapsed);
ticker.Interval = 250;
intlDelg = new InternalDelegate(sqlInfo.EnumerateSQLServers);
then one of the form click events is as follows:
private void btnGetAllDataBases_Click(object sender, EventArgs e)
{
ticker.Start();
intlDelg.BeginInvoke(new AsyncCallback(CallBackMethod), intlDelg);
//There is other code which goes in here too.
}
This here is the event that takes place with the intDelg.BeginInvoke()
private void CallBackMethod(IAsyncResult result)
{
if (this.InvokeRequired)
{
this.Invoke(new AsyncDelegate(CallBackMethod), result);
}
else
{
try
{
prgProgress.Value = prgProgress.Maximum;
switch (called)
{
case CallFor.SqlServerList:
string[] sqlServers = intlDelg.EndInvoke(result);
cmbSqlServers.Items.AddRange(sqlServers);
if (cmbSqlServers.Items.Count > 0)
{
cmbSqlServers.Sorted = true;
cmbSqlServers.SelectedIndex = 0;
}
this.Cursor = Cursors.Default;
btnLoadSqlServers.Enabled = true;
txtUserName.Select();
txtUserName.Focus();
break;
case CallFor.SqlDataBases:
string[] sqlDatabases = intlDelg.EndInvoke(result);
cmbAllDataBases.Items.AddRange(sqlDatabases);
if (cmbAllDataBases.Items.Count > 0)
{
cmbAllDataBases.Sorted = true;
cmbAllDataBases.SelectedIndex = 0;
}
this.Cursor = Cursors.Default;
btnGetAllDataBases.Enabled = true;
break;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
if (called == CallFor.SqlTables)
{
this.Cursor = Cursors.Default;
}
prgProgress.Value = 0;
prgProgress.Refresh();
ticker.Stop();
}
}
}
That then gets all the sql Servers on the network.
The ticker elapsed event is as follows:
void ticker_Elapsed(object sender, ElapsedEventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new TimerDelegate(ticker_Elapsed), sender, e);
}
else
{
if (prgProgress.Value == prgProgress.Maximum)
{
prgProgress.Value = 0;
}
else
{
prgProgress.Value += 20;
}
}
}
This is where the progress bar gets updated.
So now, I have another button event which I want to do the same thing:
private void btnOK_Click(object sender, EventArgs e)
{
err = "";
if (ValidateDB())
{
if (ValidateForm())
{
this.Cursor = Cursors.WaitCursor;
ticker.Start();
CommitData(); // This is the function which requires the data from the form again.
this.Cursor = Cursors.Default;
}
else
{
MessageBox.Show(err);
}
}
else
{
MessageBox.Show(err);
}
}
This is the function which then commits the data:
private void CommitData()
{
try
{
//The code which will go here is far to long to post here but it where
//There is a for loop to iterate through the datagridview.
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
prgProgress.Value = 0;
prgProgress.Refresh();
ticker.Stop();
}
}
The above button click event is where I am trying to update my progress bar again, but it needs access to the main thread when I do it the with multiple threads. So what you are saying is, that if in that loop in the commit data function, If I call the Form.update(), it will work?
Thanks
ChongoPosted Nov 27, 2007, 8:59 AM