Display number as it is found - c#

I have simple task in my app, but i have problem.
For example:
for (int i = 1; i <= 1000000; i++)
{
if (i % 2 == 0) {
TextBlock.Text += string.Format("{0},", i);
}
}
While app doing that task it takes long time, so I would like it displays number when it is found and not at the end, all numbers together.
What is the simplest way to do that for newbie like me?
It is WP8, C#.
Thanks a lot.

you can keep adding these numbers in a queue object and have a thread looking for a change in the queue object and simultaneously update the textbox.

So the problem is, that UI doesn't get refreshed until your loop end, even if you are appending text to TextBox. The easy way to fix it - is to add Application.DoEvents() call after TextBlock.Text += string.Format("{0},", i);.
But it has some critical downsides (explained here https://stackoverflow.com/a/5183623/2152334).
The right way is to do calculation in a different thread (using Tasks for example) and update UI thread using Dispatcher:
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
TextBlock.Text += ...
});

You can't refresh the display if your code blocks the UI thread. You need to leave the UI thread for your long task.
// We go to a non-UI thread
TaskEx.Run(() => {
for (int i = 1; i <= 1000000; i++)
{
// some long operation here... -_-"
if (i % 2 == 0)
{
// We return to UI thread after long operation to display the result
Deployment.Current.Dispatcher.InvokeAsync(() => {
TextBlock.Text += string.Format("{0},", i);
});
}
}
});
Also you might consider adding a cancellation token to break the loop in case the user leaves the page being displayed.
[edit: this solution is meant for your comment "my app will display few numbers but calculation takes long time". If you really need 1000000 results, try to Dispatcher.InvokeAsync(...) and TextBlock.Text += ... multiple results at a time.]

Related

C# wpf, how to update a progressbar over 1 second

I cannot for the life of me figure out how to have a progress bar "progress" (fill up) over 1 second.
I do NOT mean one update every one second, what I want is:
After 100 milliseconds, 10% progress
after 200ms, 20% progress
...
after 1000ms, 100% progress.
How does one do this?
await Application.Current.Dispatcher.InvokeAsync(() =>
{
for (int i = 10; i <= 100; i += 10)
{
yourprogressbar.Value = i;
Thread.Sleep(100);
}
});
You need to add async keyword on your method to be able to use await.

UWP ProgressBar not fully updating from a For loop

I am trying to update a ProgressBar from a For loop, in my head, should be easy but I'm struggling a bit.
I've got a Dispatcher method which only seems to fire off twice. Once after the first loop, then not until it has completed. I.e. the loop count should be 6, so each increment would be 16.666667. The ProgressBar will update once to 16.666667 then will wait until the loop has complete to reach 100.
I've read through a few different questions on here, but can't find the answer. I'm still relatively new to this, so any help explained in the dumbest way possible would be appreciated :)
double progressIncrease = (double)100 / TodaysFixtures.Count;
ProcessingBar.Value = 0;
for (int fixture = 0; fixture < TodaysFixtures.Count; fixture++)
{
// ...
await UpdateProcessingBarAsync(progressIncrease);
}
private async Task UpdateProcessingBarAsync(double progressIncrease)
{
await Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
ProcessingBar.Value += progressIncrease;
});
}

Interface not getting changed - winforms

I have a function, when the function starts, I want to display some UI components and then start working and at the end, I want to erase those components from there. Problem is that I don't see the change in UI on the form.
The function in which I am doing this is:
public void Processor()
{
// ------------- SETTING UI COMPONENTS
lblProgress.Visible = true;
progressBar.Visible = true;
btnStop.Visible = true;
// ------------- WORKING
int counter = 0, percent = 0;
foreach (string url in Urls)
{
.... WORKING THAT TAKES TIME
counter += 1;
percent = ((counter * 100) / Urls.Count());
// ------------- MODIFYING UI COMPONENTS
// Modification doesn't appear on the form while running
lblProgress.Text = "Progress: " + (percent > 100 ? 100 : percent) + "%";
progressBar.Value = percent;
}
// ------------- SETTING UI COMPONENTS
lblProgress.Visible = false;
progressBar.Visible = false;
btnStop.Visible = false;
lblDone.Visible = true;
}
Can someone help with this. Kindly let me know if I am doing something wrong.
#user2831683, I think you know now that using Application.DoEvents is not much advisable. Here is another alternative you can use:
async public void Processor() //see the "async" keyword
{
//SETTING UI COMPONENTS
foreach (string url in Urls)
{
await Task.Run(() =>
{
//.... WORKING THAT TAKES TIME
});
//MODIFYING UI COMPONENTS
}
}
While DoEvents makes your code seem to work, your UI is still blocked by your work most of the time. This approach will allow your UI to make more smooth updates (for ex, while moving your window etc.)
PS: The only change in the original code is the part //.... WORKING THAT TAKES TIME, which is replaced by a Task
As an addition to Vimalan's response:
Application.DoEvents() forces to pump the message on the windows message queue. This means that any pending request(s) for UI update are invoked via Application.DoEvents() so this is a brute-force method. I would suggest to make your UI responsive by removing the block of code which is taking time to process and run it on a seprate thread. Right now the code that takes time is also running on the main thread (where the UI is running too.) which I would say choke the main thread and is heaving difficulty to swallow and digest it ;)
Try reading the following asynchronous programming pattern to learn more:
https://msdn.microsoft.com/en-us/library/jj152938(v=vs.110).aspx
Or you can use a background worker:
https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
Please do not use .DoEvents(). It can cause all sorts of re-entrancy issues in your code and can break third-party code. Actually it can break the built-in Windows Forms code too, depending on what you're doing.
I would certainly suggest BackgroundWorker as the stock standard approach to resolve this issue.
I, however, prefer to use Microsoft's Reactive Framework for this kind of thing. It makes things like this very simple.
Here's how I would have tackled this:
public void Processor()
{
// ------------- SETTING UI COMPONENTS
lblProgress.Visible = true;
progressBar.Visible = true;
btnStop.Visible = true;
// ------------- WORKING
Urls
.ToObservable(Scheduler.Default) // Goes to background thread
.Do(url =>
{
/* .... WORKING THAT TAKES TIME */
})
.Select((url, counter) => counter * 100 / Urls.Count())
.DistinctUntilChanged()
.ObserveOn(this) // back to the UI thread
.Subscribe(
percent => // Done each change in percent
{
lblProgress.Text = "Progress: " + (percent > 100 ? 100 : percent) + "%";
progressBar.Value = percent;
},
() => // Done when finished processing
{
lblProgress.Visible = false;
progressBar.Visible = false;
btnStop.Visible = false;
lblDone.Visible = true;
});
}
You can NuGet "Rx-WinForms" to get all of this goodness.

troubles threads in winforms

I need to calculate sum of elements in the textbox and number of elements at the same time. So I decided to create two threads - one for length of the number, and one for sum of elements. But when I start only one thread - it works correct. But when I start the second thread - form begins to work slow or stops working at all.
I create two threads
thrd = new Thread(GetLength);
thrd.Start();
thrd1 = new Thread(SetSum);
thrd1.Start();
And these are threads' functions for calculation length of the number in textbox and for calculation sum of its elements.
private void SetSum()
{
while (true)
{
if (this.label3.InvokeRequired)
this.Invoke(new Action(() => label3.Text = this.GetSum().ToString()));
}
}
private int GetSum()
{
string n = textBox1.Text;
int sum = 0;
for (int i = 0; i < n.Length; i++)
{
try
{
sum += int.Parse(n[i].ToString());
}
catch (FormatException) { };
}
return sum;
}
private void GetLength()
{
while (true)
{
if (this.label2.InvokeRequired)
this.Invoke(new Action(() => label2.Text = " | Length = " + textBox1.Text.Length.ToString()));
}
}
Where is the problem? Synchronization?
I have found a solution - I add Thread.Sleep(1) in while loop in GetLength method
Several problems here.
The task at hand is much too small for a (full) Thread. Threads are expensive to create.
By Invoking the main action, all work is done on the Main thread. Your solution is not multi-threaded after all.
Counting is easily done as a by-product of Summing (or vice versa) so 2 threads/tasks is overkill.
The while(true) ... loop will drag your process down, consuming too much CPU time for nothing
The simple answer here is not to use any threads, just run some logic in textBox1.TextChanged.
Yes, the problem is in fact synchronization: there's too much of it.
You're spawning threads that only do Invokes, which means the UI thread is doing all the work.
This part of your code is an infinite loop without any Thread.Sleep or any other Wait. This will bring CPU to 100%. You should tie this to some event or any other activity which will trigger GetLength
private void GetLength()
{
while (true)
{
if (this.label2.InvokeRequired)
this.Invoke(new Action(() => label2.Text = " | Length = " + textBox1.Text.Length.ToString()));
}
}

Can't change the text on an object?

I can't seem to change the text on any object - I've tried both labels and buttons so far. Why doesn't this work?
void Button1Click(object sender, EventArgs e)
{
for(int i = 60; i >=1; i--){
Thread.Sleep(1000);
i -= 1;
label1.Text = i.ToString();
}
}
It doesn't work because you are using a busy loop to update the text.
This code runs in the main thread, so it's busy setting the Text property for a whole minute, and can't update the user interface.
You would use a timer to update the text, so that the main thread regains the control in between changes.
Try taking out the Thread.Sleep() command to see if the label is updated. You may find that the UI thread exits the function even though you requested it to sleep.
What you can do is use a Timer control, and set the interval to 1000 (1 second). Then you can set the label1.Text to a counter or static field value (or hidden field).
As long as you stay in the Button1Click the UI thread seams to be sleeping. Delete the Thread.Sleep and you will see that the text is shown in the labels.
put Application.DoEvents(); in your code after last line, then it will work for sure.
for (int i = 60; i >= 1; i--)
{
Thread.Sleep(1000);
i -= 1;
label1.Text = i.ToString();
Application.DoEvents();
}
First of all you have to learn How to: Make Thread-Safe Calls to Windows Forms Controls.
You need to create a thread and use Invoke delegate.
Thread th = new Thread(test);
th.Start(); //start the thread
This method will update the lable.text
void test()
{
for (int i = 60; i >= 1; i--)
{
Thread.Sleep(1000);
if (label1.InvokeRequired)
{
label1.Invoke(new Action(() => {
label1.Text = i.ToString();
}));
}
}
}

Categories