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.
Related
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 wrote a simple app that adds 100000 lines of "Hello World" to a list using a BackgroundWorker.
Below is the code of the work that my backgroundworker is doing in a separate thread:
private void BgWorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
int min = 0;
foreach (var hw in hwList)
{
//new ManualResetEvent(false).WaitOne(1);
Thread.Sleep(1);
int progress = Convert.ToInt32((Double)min / hwList.Count * 100);
min++;
bgWorker.ReportProgress(progress);
}
}
// Updating the progress
private void BgWorkerOnProgressChanged(object sender, ProgressChangedEventArgs progressChangedEventArgs)
{
ProgressBar.Value = progressChangedEventArgs.ProgressPercentage;
}
All is working fine, except that if I remove the Thread.Sleep(1) the BackgroundWorker doesn't report the progress anymore. (I suppose it needs some time). Suspending the thread for 1 ms actually makes the BackgroundWorker report the progress but it's very slow.
My question is, is there a way I can get rid of thread sleeping but at the same time making the BackgroundWorker report the progress correctly?
From my understanding, suspending the BackgroundWorker is inevitable, since the thread needs some time to perform the task, but I'm wondering if there's a workaround.
I had issues where i was reporting progress to often, furthermore there is no reason to report the same progress so many times waste of cpu cycles.
private void BgWorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
int min = 0;
int oldProgress = 0;
foreach (var hw in hwList)
{
// new ManualResetEvent(false).WaitOne(1);
// Thread.Sleep(1);
int progress = Convert.ToInt32((Double)min / hwList.Count * 100);
min++;
// Only report progress when it changes
if(progress != oldProgress){
bgWorker.ReportProgress(progress);
oldProgress = progress;
}
}
}
// Updating the progress
private void BgWorkerOnProgressChanged(object sender, ProgressChangedEventArgs progressChangedEventArgs)
{
ProgressBar.Value = progressChangedEventArgs.ProgressPercentage;
}
Instead of BackgroundWorker, I developed a class named SxProgress with a very simple interface that you may use in the following way :
int DesiredLinesCount = 100000 ;
List<string> Lines=new List<string>() ;
object[] UserObjects = new object[] { Lines } ;
SxProgress.Execute("Building lines",DesiredLinesCount,true,false,
BuildLines_ExecInThread,UserObjects)) ;
private bool BuildLines_ExecInThread(int ItemIndex,object[] UserObjects)
{
// some sleep to slow down the process (demonstration purpose only)
// if (ItemIndex % 10 ==0) System.Threading.Thread.Sleep(1) ;
List<string> Lines= (List<String>)UserObjects[0] ;
Lines.Add("Hello world") ;
return true ;
}
The SxProgress code is in the last message of this link
Note also that the class provides the same easy interface for parallel process, creating as many threads as cores in the computer with transparent dispatch of the items to the different threads.
will this work for WPF?
Unfortunately no : The class encompasses a form (winforms) with progressbar, labels and stop button.
Possible solution : I never tried it.
It may work by adding to WPF project 2 references from the "Add reference" dialog form (in ".NET" tab),i.e. "System.Windows.forms" and "WindowsFormsIntegration".
Then, in source code, add "using System.Windows.Forms;"
and "using System.Windows.Forms.Integration;"
Im building a little app which has a long loading time.
I want to display this loading time in a progressbar to see how long i have to wait till the programm is loaded.
I hope you understand what i want..
I tried the backgroundworker already but dont understand how to use it, in every example they use in the DoWork Event a simple
for (int i = 0; i < 100; i++)
{
//method etc here
backgroundWorker.ReportProgress(i);
}
But in my eyes this is senseless for me because this only repeats my method...
Thank you in advance!
EDIT:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Aktie dataAktie = new Aktie(aktien_name);
for (int i = 0; i < 100; i++)
{
dataAktie.ReadFromDatabase();
dataAktie.FetchData();
backgroundWorker.ReportProgress(i);
}
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Controls that have to be filled
}
But this Controls dont get data im veryyyyy confused
The following code example demonstrates the use of the ReportProgress method to report the progress of an asynchronous operation to the user.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// This method will run on a thread other than the UI thread.
// Be sure not to manipulate any Windows Forms controls created
// on the UI thread from this method.
backgroundWorker.ReportProgress(0, "Working...");
Decimal lastlast = 0;
Decimal last = 1;
Decimal current;
if (requestedCount >= 1)
{ AppendNumber(0); }
if (requestedCount >= 2)
{ AppendNumber(1); }
for (int i = 2; i < requestedCount; ++i)
{
// Calculate the number.
checked { current = lastlast + last; }
// Introduce some delay to simulate a more complicated calculation.
System.Threading.Thread.Sleep(100);
AppendNumber(current);
backgroundWorker.ReportProgress((100 * i) / requestedCount, "Working...");
// Get ready for the next iteration.
lastlast = last;
last = current;
}
backgroundWorker.ReportProgress(100, "Complete!");
}
** http://msdn.microsoft.com/en-us/library/a3zbdb1t%28v=vs.110%29.aspx
A BackgroundWorker and it's ReportProgress method are no magic wanda that simply shows you any progress you want, you actually have to change your code to do so.
The DoWork event handler should contain the code you want to execute in the background. Ideally this is something for progress can be measured easily. For example if you have to process 10 items then after each item you could say I'm now 10% further done. That's why the example code contains a for loop.
Your code only contains two method calls, ReadFromDatabase and FetchData. So you could simply do
dataAktie.ReadFromDatabase();
backgroundWorker.ReportProgress(50); // 50% done
dataAktie.FetchData();
backgroundWorker.ReportProgress(100); // 100% done
Obviously that not really perfect. The only way to have more accurate progress is to change the ReadFromDatabase and FetchData methods, for example let them take the BackgroundWorker object as a parameter so that they can also report progress, or provide a callback for that.
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.
Im trying to update a progress bar while doing some data type checks on a separate thread and there seems to be a delay between what value the progress bar is at and the value which is actually show.
The following code is executed by the non-GUI thread and is used to raise the event.
protected virtual void OnUpdateProgressBar(object sender, ProgressBarEventArgs e)
{
EventHandler<ProgressBarEventArgs> TempHandler = UpdateProgressBar;
//Avoid possible race condition.
if (TempHandler != null)
{
TempHandler(this, e);
}
}
I have created a separate class for updating the progress bar and when i create an instance of it, i pass a reference to the progress bar. Below is the entire class.
public class ProgressBarChanged
{
ProgressBar statusBar;
public ProgressBarChanged(ProgressBar pb)
{
statusBar = pb;
statusBar.Value = 0;
}
public ProgressBarChanged()
{
}
public void subscribeToEvent(DataVerification test)
{
test.UpdateProgressBar += new EventHandler<ProgressBarEventArgs>(incrementPB);
}
public void incrementPB(object sender, ProgressBarEventArgs e)
{
Action action = () =>
{
if (e.CurrentRow == e.FinalRow - 10)
{
int i = 5;
}
statusBar.Maximum = e.FinalRow;
statusBar.Value = e.CurrentRow;
};
if(statusBar.InvokeRequired)
statusBar.Invoke(action);
else
action();
}
}
I have uploaded a screen shot showing the progress bar and the actual values.
Any ideas???
Thanks
The progessbar is a simple feedback to the user, not a piece of exact instrumentation. It's a pacifier.
It also incorporates it's own async logic to update the screen (independent of the message loop). This makes that it may run a little behind.
What's the big deal?
To get more accurate results, divide your range into < 100 segments and do fewer updates.
A delay is pretty normal. After all, invoking a method in the UI thread means Windows will dispatch a message and if your thread is fast enough (and CPU consuming) then it'll appear faster than UI.