Reputation: 382
I'm starting to use C# and I'm trying to create something similar to a messageBox to show the progress while my application is working. My app gets the entries from a database and adds that data to a grid.
After a lot of searching, I've found a way to do it without freezing the UI; using Threads:
ProgressDialog progressDialog = new ProgressDialog();
progressDialog.ProgressMax = (int)total;
Thread backgroundThread = new Thread(
new ThreadStart(() =>
{
try
{
psp_grid.DataSource = null;
psp_grid.Rows.Clear();
progressDialog.Title = "Cargando listado...";
progressDialog.Status = "Se está cargando el listado de juegos. Por favor espere...";
progressDialog.Message = "Recuperando juegos de la base de datos";
using (SQLiteCommand cmd = new SQLiteCommand(query, handle))
{
using (SQLiteDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
progressDialog.ProgressActual = Actual;
progressDialog.Message = "Añadidos " + Actual + " juegos de " + total;
psp_grid.Rows.Add();
psp_grid.Rows[psp_grid.RowCount - 1].Cells[0].Value = (Int64)rdr["ID"];
psp_grid.Rows[psp_grid.RowCount - 1].Cells[1].Value = (byte[])rdr["ICON0"];
psp_grid.Rows[psp_grid.RowCount - 1].Cells[2].Value = "Hola Primo\n" + rdr["ID"].ToString() + (string)rdr["TITLE"];
switch (int.Parse(rdr["RATE"].ToString()))
{
case 1:
psp_grid.Rows[psp_grid.RowCount - 1].Cells[3].Value = GameStation_Game_Manager.Properties.Resources._1star;
break;
case 2:
psp_grid.Rows[psp_grid.RowCount - 1].Cells[3].Value = GameStation_Game_Manager.Properties.Resources._2star;
break;
case 3:
psp_grid.Rows[psp_grid.RowCount - 1].Cells[3].Value = GameStation_Game_Manager.Properties.Resources._3star;
break;
case 4:
psp_grid.Rows[psp_grid.RowCount - 1].Cells[3].Value = GameStation_Game_Manager.Properties.Resources._4star;
break;
case 5:
psp_grid.Rows[psp_grid.RowCount - 1].Cells[3].Value = GameStation_Game_Manager.Properties.Resources._5star;
break;
}
if (psp_grid.RowCount > 7 && changed == false)
{
psp_grid.Columns[2].Width = 434;
changed = true;
}
Actual++;
}
}
}
psp_grid.Rows[0].Cells[0].Selected = true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.Write(ex.ToString());
}
if (progressDialog.InvokeRequired)
progressDialog.BeginInvoke(new Action(() => progressDialog.Close()));
}));
// Start the background process thread
backgroundThread.Start();
// Open the dialog
progressDialog.ShowDialog();
That code is working fine on final program, but while debugging, it fails at second run and I want to learn the best way to do it without errors.
The error is (in Spanish):
Activado Evento Hora Duración Subproceso
Excepción: Se detectó una excepción: "System.InvalidOperationException" en System.Windows.Forms.dll ("Operación no válida a través de subprocesos: Se tuvo acceso al control '' desde un subproceso distinto a aquel en que lo creó."). Se detectó una excepción: "System.InvalidOperationException" en System.Windows.Forms.dll ("Operación no válida a través de subprocesos: Se tuvo acceso al control '' desde un subproceso distinto a aquel en que lo creó.") 12,02s [11616] <Sin nombre>
I think the problem is because I add the entries from another thread, but I don't know how I can fix it.
Can someone help me?
Thanks!!
Upvotes: 0
Views: 348
Reputation: 382
Finally is working without problem.
ProgressDialog progressDialog = new ProgressDialog();
Thread backgroundThread = new Thread(
new ThreadStart(() =>
{
try
{
psp_grid.DataSource = null;
psp_grid.Rows.Clear();
progressDialog.ProgressMax = (int)total;
progressDialog.Title = "Cargando listado...";
progressDialog.Status = "Se está cargando el listado de juegos. Por favor espere...";
progressDialog.Message = "Recuperando juegos de la base de datos";
using (SQLiteCommand cmd = new SQLiteCommand(query, handle))
{
using (SQLiteDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
progressDialog.ProgressActual = Actual;
progressDialog.Message = "Añadidos " + Actual + " juegos de " + total;
if (psp_grid.InvokeRequired)
{
//MessageBox.Show("Invocando...");
psp_grid.Invoke(new Action(() => psp_grid.Rows.Add()));
Int64 _ID = (Int64)rdr["ID"];
byte[] _ICON0 = (byte[])rdr["ICON0"];
string _Text = "Hola Primo\n" + rdr["ID"].ToString() + (string)rdr["TITLE"];
psp_grid.Invoke(new Action(() => psp_grid.Rows[psp_grid.RowCount - 1].Cells[0].Value = _ID));
psp_grid.Invoke(new Action(() => psp_grid.Rows[psp_grid.RowCount - 1].Cells[1].Value = _ICON0));
psp_grid.Invoke(new Action(() => psp_grid.Rows[psp_grid.RowCount - 1].Cells[2].Value = _Text));
switch (int.Parse(rdr["RATE"].ToString()))
{
case 1:
psp_grid.Invoke(new Action(() => psp_grid.Rows[psp_grid.RowCount - 1].Cells[3].Value = GameStation_Game_Manager.Properties.Resources._1star));
break;
case 2:
psp_grid.Invoke(new Action(() => psp_grid.Rows[psp_grid.RowCount - 1].Cells[3].Value = GameStation_Game_Manager.Properties.Resources._2star));
break;
case 3:
psp_grid.Invoke(new Action(() => psp_grid.Rows[psp_grid.RowCount - 1].Cells[3].Value = GameStation_Game_Manager.Properties.Resources._3star));
break;
case 4:
psp_grid.Invoke(new Action(() => psp_grid.Rows[psp_grid.RowCount - 1].Cells[3].Value = GameStation_Game_Manager.Properties.Resources._4star));
break;
case 5:
psp_grid.Invoke(new Action(() => psp_grid.Rows[psp_grid.RowCount - 1].Cells[3].Value = GameStation_Game_Manager.Properties.Resources._5star));
break;
}
if (psp_grid.RowCount > 7 && changed == false)
{
psp_grid.Invoke(new Action(() => psp_grid.Columns[2].Width = 434));
changed = true;
}
}
else
{
psp_grid.Rows.Add();
psp_grid.Rows[psp_grid.RowCount - 1].Cells[0].Value = (Int64)rdr["ID"];
psp_grid.Rows[psp_grid.RowCount - 1].Cells[1].Value = (byte[])rdr["ICON0"];
psp_grid.Rows[psp_grid.RowCount - 1].Cells[2].Value = "Hola Primo\n" + rdr["ID"].ToString() + (string)rdr["TITLE"];
switch (int.Parse(rdr["RATE"].ToString()))
{
case 1:
psp_grid.Rows[psp_grid.RowCount - 1].Cells[3].Value = GameStation_Game_Manager.Properties.Resources._1star;
break;
case 2:
psp_grid.Rows[psp_grid.RowCount - 1].Cells[3].Value = GameStation_Game_Manager.Properties.Resources._2star;
break;
case 3:
psp_grid.Rows[psp_grid.RowCount - 1].Cells[3].Value = GameStation_Game_Manager.Properties.Resources._3star;
break;
case 4:
psp_grid.Rows[psp_grid.RowCount - 1].Cells[3].Value = GameStation_Game_Manager.Properties.Resources._4star;
break;
case 5:
psp_grid.Rows[psp_grid.RowCount - 1].Cells[3].Value = GameStation_Game_Manager.Properties.Resources._5star;
break;
}
if (psp_grid.RowCount > 7 && changed == false)
{
psp_grid.Columns[2].Width = 434;
changed = true;
}
}
Actual++;
}
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.Write(ex.ToString());
}
if (progressDialog.InvokeRequired)
progressDialog.BeginInvoke(new Action(() => progressDialog.Close()));
}));
Is a bit slower, but works without problem even in debug mode.
Thanks again ElectroByt.
Upvotes: 0
Reputation: 1222
Microsoft explains how to make thread-safe calls.
Here is the example from their website:
private delegate void SetTextCallback(string text);
private void SetText(string text)
{
if (textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
Invoke(d, new object[] { text });
}
else
{
textBox1.Text = text;
}
}
private void ThreadProcSafe()
{
SetText("This text was set safely.");
}
Upvotes: 1