Background:
I am using a simple progress dialog in an Outlook Add-in to show progress while performing long operations. Since I cannot run code that uses Outlook objects in a separate thread, I cannot implement a more traditional background worker process. My add-in has been working OK until Outlook 2013 where in certain instances my progress dialog hangs. When I run the add-in in the VS debugger and cause the hang, then do a break, it appears to be stuck on the DoEvents() line that tries to force the progressbar to update.
My Question:
Can someone suggest a better system to show progress with the restriction above (long running code must run in main Outlook thread). Is there a better way to make the progress dialog responsive without using DoEvents()?
The following simple code demonstrates how I am doing this now. In the add-in code that is performing long operations on Outlook objects:
private void longRunningProcess()
{
int max = 100;
DlgStatus dlgstatus = new DlgStatus();
dlgstatus.ProgressMax = max;
dlgstatus.Show();
for (int i = 0; i < max; i++)
{
//Execute long running code that MUST best run in the main (Outlook's) thread of execution...
System.Threading.Thread.Sleep(1000); //for simulation purposes
if (dlgstatus.Cancelled) break;
dlgstatus.SetProgress("Processing item: " + i.ToString(), i);
}
}
Here's the code for the simple progress dialog window:
public partial class DlgStatus : Form
{
private bool _cancelled;
public DlgStatus()
{
InitializeComponent();
}
public int ProgressMax
{
set
{
progress.Maximum = value;
Application.DoEvents();
}
}
public bool Cancelled
{
get { return _cancelled; }
}
public void SetProgress(string status, int val)
{
lblStatus.Text = status;
progress.Value = val;
Application.DoEvents(); //Seems to hang here
}
private void btnCancel_Click(object sender, EventArgs e)
{
_cancelled = true;
Application.DoEvents();
this.Visible = false;
}
}
I was able to accomplish this by doing the following. The Custom form has a progressbar with the style set to Marquee.
I got the general method from http://social.msdn.microsoft.com/Forums/en/vsto/thread/59993421-cbb5-4b7b-b6ff-8a28f74a1fe5 but found that I did not need to use all the custom window handles.
private void btn_syncContacts_Click(object sender, RibbonControlEventArgs e)
{
Thread t = new Thread(SplashScreenProc);
t.Start();
//long running code
this.SyncContacts();
syncingSplash.Invoke(new Action(this.syncingSplash.Close), null);
}
private SyncingContactsForm syncingSplash = new SyncingContactsForm();
internal void SplashScreenProc(object param)
{
this.syncingSplash.ShowDialog();
}
It is important to note that the form does not work with the Outlook object model. It is not recommended by Microsoft to use the object model on separate threads.
Related
I developed a simple windows form application showing a realtime updated chart:
In the following code:
private void Start_Click(object sender, EventArgs e)
{
cpuThread = new Thread(new ThreadStart(this.ThreadUpdateChart));
cpuThread.IsBackground = true;
cpuThread.Start();
}
private void ThreadUpdateChart()
{
if (chart1.IsHandleCreated)
{
this.Invoke((MethodInvoker) delegate { UpdateChart();});
}
else
{
//......
}
Thread.Sleep(1000);
}
private void UpdateChart()
{
for (int i = 0; i < sampleSize; i++)
{
chart1.Series["Signal"].Points.AddXY(i, 100 * var.NextDouble());
chart1.Update();
//Thread.Sleep(500);
}
}
My problem is that when I press the start button the chart is update correctly, but this freeze my GUI till the end of the updated, I would like to update the chart without freezing other GUI functions, is it possible? Any solution?
To start of, the threading does nothing useful in your example, it just runs some code on a background thread, that immediately asks the main thread to update the chart. So get rid of it unless you are doing something computationally expensive not shown in the example.
You should also get better performance by doing all changes to your chart at once, avoiding expensive things like rendering until all changes have been made. So I would try something like this:
private void UpdateChart()
{
var series = chart1.Series["Signal"];
for (int i = 0; i < sampleSize; i++)
{
series.Points.AddXY(i, 100 * var.NextDouble());
}
chart1.Update();
}
If this does not help I would recommend doing some profiling to find out what is actually blocking the UI thread.
I am wondering what would be the best solution to a practical problem.
I am not using any threads in this small project.
It is a simple UI polling data from the serial port on a fixed timer.
Data is analyzed, filtered, and then displayed on a line chart.
Everything is working fine other than data polling "hanging" (i.e. not being executed, no error or anything) when the form is moved around on the desktop.
I don't necessarily need the chart to be updated when the form is being moved but I would at least want the timer to still tick while it's being moved (so that data polling continues).
My timer is declared as follow:
private System.Windows.Forms.Timer timer1;
I don't have any threads defined myself, but my understanding is that timer ticks are taking place on a separate thread. Is that right?
I am handling the timer tick event like this.
private void timer1_Tick(object sender, EventArgs e)
{
if (chkBoxPosition.Checked)
{
tBoxPosition.Text = ExecuteCommand("r 1\n", tBoxPosition.Text, false, false);
Axis.position = TryParseDouble(Axis.position, tBoxPosition.Text);
}
}
I have more stuff in this event (a chart).
It's working decently, however when I drag the UI winform on the desktop it "freezes" the controls temporarily until I let go of the UI. It doesn't crash or anything, it just doesn't refresh as I am moving the window.
Not a huge deal as far as the controls are concerned, however, I just realized that the entire thread or timer were also hanging, as for the entire time where I am holding the mouse button dragging the window around nothing seems to be taking place.
The question does not specify what chart package is being used, so i'm guessing the built in one from .Net.
The background worker is a good option, but it's pretty old school and takes more implementation than using async Task.
Note: The only time it's OK to use async void is on top level event handlers, otherwise you should use async Task, see this
I made it so the checkbox enables and disabled the timer, not sure what it does in your app.
Here are 2 versions of your code, v1 is where the text box gets updated after the ExecuteCommand has finished and v2 is where the text box gets updated within the ExecuteCommnad method.
Version 1: (update text on timer tick)
public partial class Form1 : Form
{
Series Series1 { get; set; }
int val = 0;
public Form1()
{
InitializeComponent();
timer1.Interval = 2000;
chart1.Series.Clear();
Series1 = new Series
{
Name = "Series1",
Color = System.Drawing.Color.Green,
IsVisibleInLegend = false,
IsXValueIndexed = true,
ChartType = SeriesChartType.Line
};
this.chart1.Series.Add(Series1);
}
private async void timer1_Tick(object sender, EventArgs e)
{
tBoxPosition.Text = await ExecuteCommand("r 1\n", tBoxPosition.Text, false, false);
tBoxPosition.Select(tBoxPosition.Text.Length - 1, 0);
tBoxPosition.ScrollToCaret();
}
//not sure what you are doing her but lets say its something that takes some time..maybe a data fetch of some kind
async Task<string> ExecuteCommand(string str, string text, bool value1, bool value2)
{
StringBuilder returnString = new StringBuilder(text);
for (int i = 0; i < 10; i++)
{
await Task.Delay(100);
returnString.AppendLine($"value : {val++}");
Series1.Points.AddXY(i, (i + i + i));
}
return returnString.ToString();
}
private void chkBoxPosition_CheckedChanged(object sender, EventArgs e)
{
if (chkBoxPosition.Checked)
timer1.Enabled = true;
else
timer1.Enabled = false;
}
}
version 2: (update text box and chart in execute method)
private async void timer1_Tick(object sender, EventArgs e)
{
await ExecuteCommand("r 1\n", tBoxPosition.Text, false, false);
}
//not sure what you are doing her but lets say its something that takes some time..maybe a data fetch of some kind
async Task ExecuteCommand(string str, string text, bool value1, bool value2)
{
for (int i = 0; i < 10; i++)
{
await Task.Delay(100);
Series1.Points.AddXY(i, (i + i + i));
tBoxPosition.Text += $"value : {val++} {Environment.NewLine}";
tBoxPosition.Select(tBoxPosition.Text.Length - 1, 0);
tBoxPosition.ScrollToCaret();
}
}
I'm trying to implement a BackgroundWorker for monitoring a FileSystemWatcher service.
My code is divided as:
A Classes.cs wich contains all methods , variables and FileSystemWatcher implementation. And the main Form1 , wich contains form data and calls for the buttons\etc. When i run my program all that happens is the cursor to change (this was already expected) - the action happens in the background (things get done) but no report is shown on my progress bar. I got the example from a website and adapted it to my code - is there anything wrong i'm doing ? I believe there's something involved with the fact the only thing i call is the filesystemwatcher - but i expected that it would report the progress based on the action running "on background".
Any help is appreciated. Thanks
My form1 code (the BackgroundWorker part) and the FileSystemWatcher follows:
namespace PPF_Converter_v10
{
public partial class Form1 : Form
{
private FileManipulation prg;
//private FileManipulation FileOp;
public Form1()
{
InitializeComponent();
//FileOp = new FileManipulation();
prg = new FileManipulation();
//Load config before the program begins - loading sample config or newly generated config
prg.LoadConfig();
FillTextBox();
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
}
BackgroundWorker CODE:
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
if (!textBox1.Text.Contains("\\"))
{
MessageBox.Show("Please define the input folder before starting");
}
else if (!textBox2.Text.Contains("\\"))
{
MessageBox.Show("Please define the XML Output folder before starting");
}
else if (!textBox3.Text.Contains("\\"))
{
MessageBox.Show("Please define the Converted PPF Output Folder before starting");
}
else if (!textBox4.Text.Contains("\\"))
{
MessageBox.Show("Please define the Invalid PPF Output Folder before starting");
}
else
{
// calls the watcher
// prg.FileWatcher.SynchronizingObject = progressBar1.
prg.ProgramProcessing(textBox1.Text);
}
// do some long-winded process here
// this is executed in a separate thread
int maxOps = 1000000;
for (int i = 0; i < maxOps; i++)
{
//rtbText.AppendText(i.ToString() + "\r\n");
// report progress as a percentage complete
bgWorker.WorkerReportsProgress = true;
bgWorker.ReportProgress(100 * i / maxOps);
}
}
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// update the progress bar
pbProgress.Value = e.ProgressPercentage;
}
private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// return to "normal" mode of operation
this.Cursor = Cursors.Default;
btnGo.Enabled = true;
}
private void btnGo_Click_1(object sender, EventArgs e)
{
// give the appearance of something happening
this.Cursor = Cursors.WaitCursor;
btnGo.Enabled = false;
// call RunWorkerAsync to start the background thread
bgWorker.RunWorkerAsync();
}
Exception thrown when the RichtextBox is enabled:
Additional information: Cross-thread operation not valid: Control 'rtbText' accessed from a thread other than the thread it was created on.
You're invoking a MessageBox from the background thread on the foreground thread. That is like doing UI in two separate threads which is a no-no.
What you can do is either use events or an event aggregator from your background thread. I would probably go for the latter. This way, your background thread, when something is wrong, can (and should) abort immediately and notify through a message that it was unable to process the file.
Think of a background task as something that has no UI whatsoever. It is there in the background and can only communicate to the UI thread using events or messages.
I have created 4 projects to online examination time monitoring
ASP.NET application to examination
Class Library for Business Process and Data Access
Class Library to database execution
Windows form application to monitor examination time
in Windows form application I use threads for monitor when new exam start and I want to indicate notification before 5 munites to end the examination.
when using visual studio for debug the app its not getting any error. but manually click the .exe file and run the application it getting an error "application stopped working"
This is my code for windows form application
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Thread t;
Thread t1;
private void Form1_Load(object sender, EventArgs e)
{
fillList();
CheckForIllegalCrossThreadCalls = false;
t= new Thread(()=>getTrigger());
t1 = new Thread(() => TimeOption());
t.Start();
t1.Start();
// getTrigger();
}
private void getTrigger()
{
int temp = StudentExamDB.getPendingCount();
while (true)
{
if (temp != StudentExamDB.getPendingCount())
{
fillList();
temp = StudentExamDB.getPendingCount();
}
}
}
List<string> added = new List<string>();
private void TimeOption()
{
while(true)
{
DataTable dt = StudentExamDB.getFinishingList();
foreach (DataRow dr in dt.Rows)
{
try
{
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
if (dataGridView1.Rows[i].Cells["enrollmentid"].Value.ToString() == dr["enrollmentid"].ToString())
{
if (added.Contains(dr["enrollmentid"].ToString()))
{
}
else
{
notifyIcon1.BalloonTipTitle = "Ending Some Examinations";
notifyIcon1.BalloonTipText = "click here to show more details about examination time";
notifyIcon1.ShowBalloonTip(5000);
added.Add(dr["enrollmentid"].ToString());
}
dataGridView1.Rows[i].DefaultCellStyle.BackColor = Color.Tomato;
dataGridView1.Rows[i].DefaultCellStyle.ForeColor = Color.White;
}
}
}
catch
{
}
}
}
}
private void fillList()
{
try
{
dataGridView1.DataSource = StudentExamDB.getPendingList();
}
catch
{
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
t.Abort();
t1.Abort();
}
private void setToFinishedToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
StudentExamDB.updateStatus(int.Parse(dataGridView1.CurrentRow.Cells["enrollmentid"].Value.ToString()));
fillList();
}
catch
{
}
}
}
Are you aware of what this does?
CheckForIllegalCrossThreadCalls = false;
You're explicitly turning off checks for what you're doing wrong. That suggests that you know you shouldn't be modifying the UI on a non-UI thread, but you're doing it anyway. If you didn't have that line of code, you certainly would get exceptions when running in Visual Studio.
That's at least one of the problems. Your TimeOption method is running on a non-UI thread, but it's modifying the UI. Just don't do that. There are various other options, including BackgroundWorker and Control.Invoke/BeginInvoke.
Then...
You've got tight loops in both TimeOption and getTrigger. Basically you're going to be pounding the database hard, forever. That's a really bad idea. You should at least have a delay between iterations
You've got empty catch blocks all over the place: catch {}. That's basically claiming that whatever goes wrong, you're fine - you can just keep going and ignore it, without even logging what's happened. Don't do that.
You're using Thread.Abort to kill your threads. Don't do that. In this case it would be pretty simple to use a volatile bool field indicating when you want to finish, and check it on each iteration of the loop.
I suspect the problems are due to your inappropriate access to the UI from a different thread - but I can't say for sure. Fix all of the above and you'll have a much better codebase to then diagnose any problems which still remain.
I made a form that plays a progressbar role here's the code i made
public partial class PXProgressBar : Form
{
public delegate bool CancelEvent();
public event CancelEvent cancel_e;
public Boolean ProcessCancelled
{
get;
set;
}
public PXProgressBar(bool EnableCancel)
{
InitializeComponent();
ProcessCancelled = false;
progressBar1.Minimum = 0;
if (!EnableCancel)
Cancelbtn.Visible = false;
}
public void increament(int step)
{
if (progressBar1.Value < progressBar1.Maximum-1)
{
progressBar1.Value++;
progressBar1.Caption = progressBar1.Value.ToString() + " of " + progressBar1.Maximum;
progressBar1.Refresh();
}
else
{
progressBar1.Value++;
progressBar1.Caption = progressBar1.Value.ToString() + " of " + progressBar1.Maximum;
if (this.TopMost)
this.TopMost = false;
this.Update();
this.Hide();
this.WindowState = FormWindowState.Minimized;
// this.Dispose();
}
}
public void SetMaximum(int MaximumValue)
{
if (MaximumValue <= 0)
{
progressBar1.Maximum = 0;
return;
}
if (progressBar1.Minimum != 0 && MaximumValue < progressBar1.Minimum)
{
progressBar1.Maximum = progressBar1.Minimum;
return;
}
progressBar1.Maximum = MaximumValue;
}
public void SetMinimum(int MinimumValue)
{
progressBar1.Value = 0;
if (MinimumValue <= 0)
{
progressBar1.Minimum = 0;
return;
}
if (progressBar1.Maximum != 100 && MinimumValue > progressBar1.Maximum)
{
progressBar1.Minimum = progressBar1.Maximum;
return;
}
progressBar1.Minimum= MinimumValue;
}
public void SetTitle(string ProcessTitle)
{
this.ProgressTitlelb.Text =ProcessTitle;// ProcessTitle;
//this.ProgressTitlelb.Left = (this.panel1.Width - this.ProgressTitlelb.Width) / 2;
//this.ProgressTitlelb.Top = (this.panel1.Height - this.ProgressTitlelb.Height) / 2;
this.Update();
}
private void Cancelbtn_Click(object sender, EventArgs e)
{
ProcessCancelled = true;
bool disposeRequired =cancel_e();
if(disposeRequired)
this.Dispose();
}
private void PXProgressBar_Shown(object sender, EventArgs e)
{
this.Update();
}
}
and i call the form through this code
if (ProgressBar == null)
ProgressBar = new PXProgressBar(true);
ProgressBar.SetTitle("Saving ...");
ProgressBar.SetMinimum(0);
ProgressBar.SetMaximum(100);
ProgressBar.TopMost = true;
ProgressBar.Show();
Application.DoEvents();
regarding that the past few lines are in a unction that is called throught a thread
but when i run it the form hangs so i cant set a Cancel Button in the form to let the user cancel the operation
Your code looks like it should be fine so I can only assume that you are doing a long running operation on the UI thread which would cause the UI to look like its hung. You need to perform long running operations on a background thread so that the UI thread remains responsive enough to respond to button clicks etc. There are many many articles about this if you consult your friend Google.
More info on that here http://www.idevforfun.com/index.php/2010/01/10/windows-ui-threading/
I agree with Skizz on the DoEvents call ... there's only a very few rare cases where that call is needed and mostly its in the framework itself that it gets used.
You need to make sure the GUI elements are created on the main form thread and not from a separate thread. So, you need to get you thread that is doing the work to get the main form thread to display and update the progress bar. This is going to take a bit of refactoring.
So, in your worker thread:
void DoWork () // of whatever it's called
{
main_form.CreateProgressBar ();
while (doing stuff)
{
main_form.IncrementProgressBar ();
do stuff
}
main_form.DestroyProgressBar ();
}
And in the main form:
delegate void Callback ();
void CreateProgressBar ()
{
if (InvokeRequired)
{
Invoke (new Callback (CreateProgressBar));
}
else
{
progress_bar = CreateProgressBar ();
}
}
void IncrementProgressBar ()
{
if (InvokeRequired)
{
Invoke (new Callback (IncrementProgressBar ));
}
else
{
progress_bar.IncrementProgressBar ();
}
}
void DestroyProgressBar ()
{
if (InvokeRequired)
{
Invoke (new Callback (DestroyProgressBar));
}
else
{
progress_bar.Close ();
progress_bar = null;
}
}
The InvokeRequired determines if the calling thread is the same as the GUI thread. If the calling thread is not the GUI thread, the Invoke is used to changed thread context. This is the synchronous version and won't complete until the invoked method is finished. There is an asynchronous version called BeginInvoke but this isn't really needed for what your doing.
The problem might be the DoEvents method call. From this MSDN page:
Calling this method causes the current thread to be suspended while
all waiting window messages are processed. If a message causes an
event to be triggered, then other areas of your application code may
execute. This can cause your application to exhibit unexpected
behaviors that are difficult to debug. If you perform operations or
computations that take a long time, it is often preferable to perform
those operations on a new thread. For more information about
asynchronous programming, see Asynchronous Programming Overview.
I don't think the DoEvents call is necessary. If you need to halt the code after the Show for the operation to complete, then use a System.Threading.EventWaitHandle instead.
There's some link maybe helpful for you about progressbar:
How do I implement a progress bar in C#?
Hope this help.
Just create other thread for progressbar and use it in background