This is UWP C# application. I want to display the i variable.
private void btn2_Click(object sender, RoutedEventArgs e)
{
for(int i = 0; i<=10000000; i++)
{
textBlock2.Text = i.ToString();
}
}
But after I clicked the button, the text only change one time after the loop is finished.
How can see changing text in every loop?
Actually, your UI is frozen while the long loop is executing, so nothing can be updated on the screen. Try this
private async void btn2_Click(object sender, RoutedEventArgs e)
{
for(int i = 0; i<=10000000; i++)
{
textBlock2.Text = i.ToString();
await Task.Delay(200); //stay 200 ms before showing next number so human eyes can see it.
}
}
When it executes to this line await Task.Delay(200);, the loop is temporarily suspended - for 200 milliseconds, and the UI thread now has a chance to process the drawing tasks pending in the queue, which includes redrawing the textblock.
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();
}
}
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.
I have a method that loops through all ListViewItems and for each one I am trying to simulate a delay with this code:
lvFiles.Items[i].SubItems.Add("In-Progress");
do
{
//loop
} while(DateTime.Now <= stopTime);
lvFiles.Items[i].SubItems[7].Text = "Complete";
The problem is that the form "freezes" for x seconds as expected but the status doesn't seem to update visually until all items get looped through... then all items get marked as "complete" all at once.
What is the problem, and how can I fix this?
Withim your loop try using:
Application.DoEvents();
Edit:
As per #sa_ddam213 correct comment, it is not good to invoke it in every iteration, so you could build a counter so that it is invoked every x iterations of the loop and not with every iteration.
If all you want to do is update the UI throughout the process then this would work:
lvFiles.BeginInvoke(() => lvFiles.Items[i].SubItems[7].Text = "Complete");
I ended up using a BackgroundWorker similar to this solution substituting with updating the listview instead of a progress bar:
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; (i <= 10); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
System.Threading.Thread.Sleep(500);
worker.ReportProgress((i * 10), "some data to pass to other thread");
}
}
}
and
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//listview update code here - e.Argument cast and parsed to update listview
}
I am trying to make a stop button for this loop but it runs indefinite, nothing happens when i click button 2
bool dowhile = false;
private void button1_Click(object sender, EventArgs e)
{
do
{
for (int i = listbox1.Items.Count - 1; i >= 0; i--)
{
string textstring = listbox1.Items[i].ToString();
richTextBox1.AppendText("" + textstring + ": Done\n");
Thread.Sleep(1000);
}
} while (!dowhile);
}
private void button2_Click(object sender, EventArgs e)
{
this.dowhile = true;
}
where do i go wrong ?
sry for the "lvlchanger" typo, code is ok now, nothing missing
i'm also looking for a not-so-long fix on this :))
The system can't process anything from the message queue (i.e. button clicks, repaints, etc) until your button1_Click completes - which it never will. This is exactly what causes all those "{blah} is not responding" messages - code that doesn't respond to the message queue promptly.
Basically, don't do that. A hacky fix would be some DoEvents(), but NO! do it properly; basically, handle the event from button2_Click instead. Perhaps you should run the refreshes from a timer tick?
Thread.Sleep is almost always the incorrect approach; when you find yourself wanting to use something like it or Application.DoEvents in your code, it's time to take a long step back and think about what you're really doing and why it isn't working. A new design should be in your future.
The real problem here is that you're doing work on the UI thread, which blocks the user from interacting with your application. Your click on Button2 is getting recognized and processed after the loop has already finished processing, at which point it does absolutely nothing.
This is because threads can only complete one task at a time. And, in fact, Thread.Sleep(100) only makes this worse, because it forces the thread to spin and do nothing for 100 milliseconds. That's 100 more milliseconds it will take for the loop to complete for absolutely no gain.
The correct solution to this (common) problem is to spin the loop off onto a separate thread, using something like the BackgroundWorker class. The MSDN entry has a very good example of its use, including your specific use case: allowing the user to cancel a long-running background task.
private void button1_Click(object sender, EventArgs e)
{
bool dowhile = false;private void button1_Click(object sender, EventArgs e){
do
{
for (int i = listbox1.Items.Count - 1; i >= 0; i--)
{
string textstring =listbox1.Items[i].ToString();
richTextBox1.AppendText("" + textstring + ":` `Done\n");
Thread.Sleep(1000);
}
} while (!lvlchanger && dowhile == false);
}
private void button2_Click(object sender, EventArgs e)
{
this.dowhile = true;
}
Add an Application.DoEvents() to the loop to allow the application to process events from other sources.
/EDIT
This should work, but...
"This is almost never the correct answer" - Cody Gray
Is there a way to directly "restart" a background worker?
Calling CancelAsync() followed by RunWorkerAsync() clearly won't do it as their names imply.
Background info:
I have a background worker which calculates a total in my .net 2.0 Windows Forms app.
Whenever the user modifies any value which is part of this total I'd like to restart the background worker in case it would be running so that directly the latest values are considered.
The backgriound work itself does not do any cancleing.
When you call bgw.CancelAsync it sets a flag on the background worker that you need to check yourself in the DoWork handler.
something like:
bool _restart = false;
private void button1_Click(object sender, EventArgs e)
{
bgw.CancelAsync();
_restart = true;
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 300; i++)
{
if (bgw.CancellationPending)
{
break;
}
//time consuming calculation
}
}
private void bgw_WorkComplete(object sender, eventargs e) //no ide to hand not sure on name/args
{
if (_restart)
{
bgw.RunWorkerAsync();
_restart = false;
}
}
There are a couple of options, it all depends on how you want to skin this cat:
If you want to continue to use BackgroundWorker, then you need to respect the model that has been established, that is, one of "progress sensitivity". The stuff inside DoWork is clearly required to always be aware of whether or not the a pending cancellation is due (i.e., there needs to be a certain amount of polling taking place in your DoWork loop).
If your calculation code is monolithic and you don't want to mess with it, then don't use BackgroundWorker, but rather fire up your own thread--this way you can forcefully kill it if needs be.
You can hook the change event handler for the controls in which the values are changed and do the following in the handler:
if(!bgWrkr.IsBusy)
//start worker
else if(!bgWrkr.CancellationPending)
bgWrkr.CancelAsync();
Hope it helps you!
I want to leave my requests running, but no longer care about the results. I override the value of the background worker (my busy spinner is using the isBusy flag).
private void SearchWorkerCreate() {
this.searchWorker = new BackgroundWorker();
this.searchWorker.DoWork += this.SearchWorkerWork;
this.searchWorker.RunWorkerCompleted += this.SearchWorkerFinish;
}
private void SearchWorkerStart(string criteria){
if(this.searchWorker.IsBusy){
this.SearchWorkerCreate();
}
this.searchWorker.RunWorkerAsync(criteria);
this.OnPropertyChanged(() => this.IsBusy);
this.OnPropertyChanged(() => this.IsIdle);
}
May this method help someone... I've created a function to reset the backgroundworker in one method. I use it for task to do periodically.
By creating a Task, the backgroundworker is can be stopped with the CancelAsync and restarted inside the Task. Not making a Task wil start the backgroundworker again before it is cancelled, as the OP describes.
The only requirement is that your code runs through some loop, which checks the CancellationPending every period of time (CheckPerMilliseconds).
private void ResetBackgroundWorker()
{
backgroundWorker.CancelAsync();
Task taskStart = Task.Run(() =>
{
Thread.Sleep(CheckPerMilliseconds);
backgroundWorker.RunWorkerAsync();
});
}
Inside the backgroundworker I use a for-loop that checks the CancellationPending.
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while(true)
{
if (backgroundWorker.CancellationPending)
{
return;
}
//Do something you want to do periodically.
for (int i = 0; i < minutesToDoTask * 60; i++)
{
if (backgroundWorker.CancellationPending)
{
return;
}
Thread.Sleep(CheckPerMilliseconds);
}
}
}