Display an information message while the program is running - c#

I am working on a user interface in C#. When user click the 'start' button, a huge 'for loop' starts to work. This process takes several minutes.
I want that just before forloop a message is displayed and after the forloop the message will disappear automaticly. My aim is to give an information message like 'Running, please wait' to the user.
Is there anyway to do that?
Here is the loop. It scans an image and makes some processes.
for (var i = 0; i <3504 ; i++)
for (var j = 0; j < 2306; j++)
{
........
}

You didn't say what GUI framework you're using, so I'm going to assume Forms. WPF would be similar though.
Let's assume the work begins when you click a button named "button1". Create a Form subclass that is your message (called, for the sake of example, "MessageWaitForm"). Then you can use it like this:
private void SomeWork()
{
for (var i = 0; i <3504 ; i++)
for (var j = 0; j < 2306; j++)
{
........
}
}
private void button1_Click(object sender, EventArgs e)
{
using (MessageWaitForm form = new MessageWaitForm())
{
form.Shown += async (sender1, e1) =>
{
await Task.Run(() => SomeWork());
form.Close();
}
form.ShowDialog();
}
}
This causes your processing to be executed in a separate asynchronous task, starting once the message dialog has been shown. When the task finishes, execution will return to the form.Close() statement, allowing the message form to be automatically closed.
Note that I had to make some assumptions and fill out your code example some just so that a usable answer could be provided. The above may not match your exact implementation as it stands now, but hopefully you can understand the basic concept being illustrated and conform it to your needs.

Perhaps your code would look something like this? I haven't had a chance to compile this but it should be enough to get you started.
private void StartBtn_OnClick(object sender, EventArgs e)
{
_isRunning = true;
CodeIsRunning.Visibility = Visibility.Visible;
//Anything else needed (disable buttons, etc)
Task.Factory.StartNew(() => {
for (var i = 0; i < 3504; i++)
for (var j = 0; j < 2306; j++)
{
...
}
_isRunning = false;
});
}

Related

C# "Thread.Sleep" Messing My Program (Form Doesn't Show Up)

i add a loop to my program just to check something...(For verification)
for (int i = 1; i < total; i++){
for (int row = 0; row < 4; row++){
for (int col = 0; col < 4; col++){
pixel = block[i][row][col];
label1.Text = pixel.R.ToString("X");
System.Threading.Thread.Sleep(100);}}}
After Add this loop program works , but form doesnt show up. I Start Debuging and i saw that in this line it stops. Dont go any further.
Application.Run(new Form1());
Basicly begining of the program. So I isolate the System.Threading.Thread.Sleep(100);}}}
Part it is working now. Why this code is causing problem. I used the
using System.Threading.Tasks;.
Any idea or i can use other delay function... İ waiting for your help. Thank you..
You should never, ever, block the UI Thread (by means of sleeping or doing some heavy work) as the Thread can only either handle UI-Events (clicks, rerendering, resizing) or run your code, not both. In cases where you must execute some long running code from a event-handler, you can either start a new thread to do the work or run async code.
For you, something like this should work just fine:
public async void Form1_Load(object sender, EventArgs e) {
for (int i = 1; i < total; i++){
for (int row = 0; row < 4; row++){
for (int col = 0; col < 4; col++){
pixel = block[i][row][col];
label1.Text = pixel.R.ToString("X");
await Task.Delay();
}
}
}
}
While Sleep blocks the thread while it waits, await Task.Delay(); does not. It actually returns and lets the thread continue doing whatever it was doing previously and notifies the thread when it finished waiting so the thread can come back to your function later and continue running your code. (This is a simplification of how async and await works in C#)

Textbox doesn't refresh and is in break mode after a little time

I am making a simple wpf application. It has a button and a textbox and when you click the button it should update the textbox the whole time(see code). I have 3 problems:
It doesn't update the textbox in the loop.
When I click the button It is stuck and I can't close the application(only with taskmanager and stop debugging).
If i leave the textbox stuck and do not close it. After a minute or so I get this error: ContextSwitchDeadlock occurred with the break mode screen.
I have tried to solve the problem by searching on the internet but I didn't succeed. I hope you guys are able to solve it:). And the code is here:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Play_Click(object sender, RoutedEventArgs e)
{
int x = RandomNumber();
}
private string TextboxData;
private int RandomNumber()
{
int x = 0;
int i = 0;
Random rng = new Random();
do
{
x = rng.Next(1, 1000000);
i++;
TextboxData += "\r\nAl zo vaak :O" + i; //the rng loop
textBox.Text = TextboxData;
}
while (x != 1);
TextboxData += "\r\nHij heeft zo vaak geprobeerd 1 te halen " + i;
textBox.Text = TextboxData + Environment.NewLine;
return x;
}
}
The problem is that all your actions are made in main GUI thread.
That means that your program can't redraw interface because it's busy executing code in loop.
In order to achieve desired results you need to execute your loop asynchronous. But we must keep in mind that GUI should be updated only from GUI thread( it's framework requirement).
Due to quick calculations the texbox will be updated too often. You may consider using Thread.Sleep(100).
Here's solution:
private async void Play_Click(object sender, RoutedEventArgs e)
{
int x = await RandomNumber();
}
private async Task<int> RandomNumber()
{
int x = 0;
int i = 0;
string text = string.Empty;
Random rng = new Random();
// This will start work in background. Leaving GUI thread to it's own tasks.
await Task.Factory.StartNew(() =>
{
do
{
x = rng.Next(1, 1000000);
i++;
text = "\r\nAl zo vaak :O" + i.ToString(); //the rng loop
// This will invoke textbox update in GUI thread satisfying update requirement.
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Input,
new Action(() => { textBox.AppendText(text); }));
// We will make it slower in order to see updates at adequate rate.
Thread.Sleep(100);
}
while (x != 1);
});
// thanks to await we will have this code executed after we found our x==1.
text = "\r\nHij heeft zo vaak geprobeerd 1 te halen " + i;
textBox.AppendText(text);
return x;
}
Don't forget to add following usings:
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
EDIT: I don't know why you want to have return value from RandomNumber() (it will always be 1. so it's pontless). But to handle that correctly we need to add async to our signatures in order to use await so we could get a value, execute code after awaited block and return result.
It's a pretty complicated topic, which I will only mention here in order to have correct solution. If you're interested - google '.net async await'. But I suggest to leave it for future :)
Also, I removed TextBoxData field and replaced it with local variable and used textbox.AppendText() as #KevinKal suggested.

Backgroundworker cancel the worker

I'm facing some troubles when trying to cancel the Backgroundworker.
I've read dozens os similiar topics such as How to stop BackgroundWorker correctly, How to wait correctly until BackgroundWorker completes? but I'm not reaching anywhere.
What's happening is that I have a C# application that uses a PHP WebService to send info to a MySQL database. If the user, for some reason, clicks (in the form) on the "back" or "stop" button, this is the code that is fired:
BgWorkDocuments.CancelAsync();
BgWorkArticles.CancelAsync();
I do understand that the request is Asynchronous, therefore the cancelation might take 1 or 2 seconds, but it should stop..and that doesn't happen at all. Even after clicked "back" (the current form is closed and a new one is opened) the backgroundworker continues to work, because I keep seeing data being inserted into MySQL.
foreach (string[] conn in lines)
{
string connectionString = conn[0];
FbConnection fbConn = new FbConnection(connectionString);
fbConn.Open();
getDocuments(fbConn);
// Checks if one of the backgrounds is currently busy
// If it is, then keep pushing the events until stop.
// Only after everything is completed is when it's allowed to close the connection.
//
// OBS: Might the problem be here?
while (BgWorkDocuments.IsBusy == true || BgWorkArticles.IsBusy == true)
{
Application.DoEvents();
}
fbConn.Close();
}
The above code is needed because I might have multiple databases, that's why I have the loop.
private void getDocuments(FbConnection fbConn)
{
BgWorkDocuments.RunWorkerAsync();
BgWorkDocuments.DoWork += (object _sender, DoWorkEventArgs args) =>
{
DataTable dt = getNewDocuments(fbConn);
for (int i = 0; i <= dt.Rows.Count - 1; i++)
{
// Checks if the user has stopped the background worker
if (BgWorkDocuments.CancellationPending == false)
{
// Continue doing what has to do..
sendDocumentsToMySQL((int)dt.Rows[i]["ID"]);
}
}
// After the previous loop is completed,
// start the new backgroundworker
getArticles(fbConn);
};
}
private void getArticles(FbConnection fbConn)
{
BgWorkArticles.RunWorkerAsync();
BgWorkArticles.DoWork += (object _sender, DoWorkEventArgs args) =>
{
DataTable dt = getNewArticles(fbConn);
for (int i = 0; i <= dt.Rows.Count - 1; i++)
{
// Checks if the user has stopped the background worker
if (BgWorkArticles.CancellationPending == false)
{
// Continue doing what has to do..
sendArticlesToMySQL((int)dt.Rows[i]["ID"]);
}
}
};
}
I agree with the comments expressing surprise that the code even works, due to the apparent ordering problem of the call to RunWorkerAsync() vs when you actually subscribe to the DoWork event. Additionally, your use of DoEvents() is unwarranted and should be removed (as is the case with any use of DoEvents()).
I also note that your workers don't actually exit when you try to cancel them. You just skip the processing, but continue to loop on the rows. Without seeing the rest of the code, it's impossible to know what's going on, but it's possible that after you cancel, the CancellationPending property gets reset to false, allowing the loops to start doing things again.
The lack of a complete code example is a real impediment to understanding the full detail of what's going on.
That said, IMHO this does not seem to be a case where you actually need BackgroundWorker at all, not with the new async/await feature in C#. Given that network I/O is involved, my guess is that each call to sendDocumentsToMySQL() and sendArticlesToMySQL() can be executed individually in the thread pool without too much overhead (or may even be able to be written as async I/O methods…again, lacking detail as to their specific implementation prevents any specific advise in that respect). Given that, your code could probably be rewritten so that it looks more like this:
private CancellationTokenSource _cancelSource;
private void stopButton_Click(object sender, EventArgs e)
{
if (_cancelSource != null)
{
_cancelSource.Cancel();
}
}
private async void startButton_Click(object sender, EventArgs e)
{
using (CancellationTokenSource cancelSource = new CancellationTokenSource)
{
_cancelSource = cancelSource;
try
{
foreach (string[] conn in lines)
{
string connectionString = conn[0];
FbConnection fbConn = new FbConnection(connectionString);
fbConn.Open();
try
{
await getDocuments(fbConn, cancelSource.Token);
await getArticles(fbConn, cancelSource.Token);
}
catch (OperationCanceledException)
{
return;
}
finally
{
fbConn.Close();
}
}
}
finally
{
_cancelSource = null;
}
}
}
private async Task getDocuments(FbConnection fbConn, CancellationToken cancelToken)
{
DataTable dt = await Task.Run(() => getNewDocuments(fbConn));
for (int i = 0; i <= dt.Rows.Count - 1; i++)
{
cancelToken.ThrowIfCancellationRequested();
await Task.Run(() => sendDocumentsToMySQL((int)dt.Rows[i]["ID"]));
}
}
private async Task getArticles(FbConnection fbConn, CancellationToken cancelToken)
{
DataTable dt = await Task.Run(() => getNewArticles(fbConn));
for (int i = 0; i <= dt.Rows.Count - 1; i++)
{
cancelToken.ThrowIfCancellationRequested();
await Task.Run(() => sendArticlesToMySQL((int)dt.Rows[i]["ID"]));
}
}

Class variable set by BackgroundWorker not persisting.

I am brand new to the idea of BackgroundWorkers, so this has me a little perplexed.
So I created a new WPF Application and created a BackgroundWorker and List class variable:
public partial class MainWindow : Window
{
private BackgroundWorker bw = new BackgroundWorker();
private List<int> tempList = new List<int>();
...
Then I use the BackgroundWorker to populate that list: (In the same class btw)
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
Random r = new Random();
for (int j = 1; j <= 100; j++)
{
for (int i = 0; i < 1000; i++)
{
tempList.Add(r.Next(100));
}
...
}
}
Now here is the part that's getting me...
The code that populates that list seems to be working fine. When I step through its execution, *it behaves as I have intended until the exact moment the code exits the bw_DoWork method.* After that point, it reverts back to an empty list. I changed the list to static at one point but nothing changed.
So why is this List not persisting throughout the programs execution?
I am(was) almost certain that this is some issue with the list being allocated in different memory regions for each thread, but I simply know far too little about about BackgroundWorker and MultiThreading in general to diagnose it myself.
Any help would be appreciated.
Before you start using more expensive options such as locking or thread safe collections. Try out Threading Tasks. If they work then you have some kind of a problem with your BackgroundWorker, if they don't then your code touches the list somewhere and you'll have to trace that.. (I just think Tasks are so much easier to work with)
private void bw_DoWork()
{
Task.Factory.StartNew(
() =>
{
var r = new Random();
for (int j = 1; j <= 100; j++)
{
for (int i = 0; i < 1000; i++)
{
tempList.Add(r.Next(100));
}
//the rest of whaterver you're doing...
}
});
}
#Stephen Marsh like #Douglas said you need to wait until work is finish.
See this:
// this execute the DoWork asynchronously.
bw.RunWorkerAsync();
// asynchronously means the next line may be executed
// before the DoWork fill the list. In fact can be executed
// before the DoWork begin.
MessageBox.Show("Without wait: " + tempList.Count.ToString());
To correct you can add this line before call RunWorkerAsync:
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
and put this in any place of the MainWindows class.
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Completed: " + tempList.Count.ToString());
}
In my tests always the result was:
"Without wait: 0"
"Completed: 100000"

C# Threading using invoke, freezing the form

I'm trying to use threads and prevent the program from freezing while the thread is busy. It should show the progress (writing of 0's / 1's) and not just show the result after its done, freezing the form in the meanwhile.
In the current program I'm trying to write to a textbox, and actually see constant progress, and the form can't be affected by the tasks of the other thread.
What I have now is I can write to a textbox with a thread using invoke, but It only shows the result (Form freezes while thread is busy), and the form freezes.
Form image:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace MultiThreading
{
public partial class MultiThreading : Form
{
public MultiThreading()
{
InitializeComponent();
}
Thread writeOne, writeTwo;
private void writeText(TextBox textBox, string text)
{
if (textBox.InvokeRequired)
{
textBox.BeginInvoke((MethodInvoker)delegate()
{
for (int i = 0; i < 500; i++)
{
textBox.Text += text;
}
});
}
else
{
for (int i = 0; i < 500; i++)
{
textBox.Text += text;
}
}
}
private void btnWrite1_Click(object sender, EventArgs e)
{
writeOne = new Thread(() => writeText(txtOutput1, "0"));
writeOne.Start();
}
private void btnWrite2_Click(object sender, EventArgs e)
{
writeTwo = new Thread(() => writeText(txtOutput2, "1"));
writeTwo.Start();
}
private void btnClear1_Click(object sender, EventArgs e)
{
txtOutput1.Clear();
}
private void btnClear2_Click(object sender, EventArgs e)
{
txtOutput2.Clear();
}
private void btnWriteBoth_Click(object sender, EventArgs e)
{
writeOne = new Thread(() => writeText(txtOutput1, "0"));
writeTwo = new Thread(() => writeText(txtOutput2, "1"));
writeOne.Start();
writeTwo.Start();
}
private void btnClearBoth_Click(object sender, EventArgs e)
{
txtOutput1.Clear();
txtOutput2.Clear();
}
}
}
EDIT:
Btw for anyone wondering, I'm new to multithreading and I'm just trying to write a small program to understand the best way to do this.
I understand that my previous invoke didn't realy help because I still wasn't giving the form a chance to update, so its getting there.
Ok so running 1 thread like this works, but still running multiple threads together, won't update the form till after the thread is done.
I've added a thread.sleep() so I can try and clear while writing, to see if I can still use the form.
When writing to 1 textbox I can still clear the screen while writing.
But once I use 2 threads, I can't use the form anymore till the thread completes, and gives the output.
private void writeText(TextBox textBox, string text)
{
for (int i = 0; i < 500; i++)
{
Invoke(new MethodInvoker(() =>
{
textBox.Text += text;
Thread.Sleep(2);
}));
}
}
(If I'm totally wrong on this I don't mind having to read through some examples/threads, I'm still trying to see what is the best way to do this, besides a backgroundworker)
EDIT 2:
I've reduced the number of invokes by reducing the amount to write, but to increase delay giving the same effect of constant writing, just reducing the load.
private void writeText(TextBox textBox, string text)
{
for (int i = 0; i < 500; i++)
{
Invoke(new MethodInvoker(() =>
{
textBox.Text += text;
Thread.Sleep(2);
}));
}
}
EDIT 3:
Sumeet's example works using
Application.DoEvents();
(notice the s, .DoEvent doesn't work, typo probably :P), writing multiple strings simultaneously & having them show the progress and not just the result.
So Code update again :)
*Using a new button to create 5 threads that write a random number to both textboxes
private void writeText(TextBox textBox, string text)
{
for (int i = 0; i < 57; i++)
{
Invoke(new MethodInvoker(() =>
{
textBox.Text += text;
Thread.Sleep(5);
Application.DoEvents();
}));
}
}
private void btnNewThread_Click(object sender, EventArgs e)
{
Random random = new Random();
int[] randomNumber = new int[5];
for (int i = 0; i < 5; i++)
{
randomNumber[i] = random.Next(2, 9);
new Thread(() => writeText(txtOutput1, randomNumber[i-1].ToString())).Start();
new Thread(() => writeText(txtOutput2, randomNumber[i-1].ToString())).Start();
}
}
This solution works ! Have checked it.
The problem is you keep telling the UI thread to change the Text, but never letting it have time to show you the updated text.
To make your UI show the changed text, add the Application.DoEvents line like this :
textBox.Text += text;
Application.DoEvents();
p.s. : Remove the else block of your If / Else loop, it is redundant, and also as pointed by others there is not any use of creating those 2 Threads as all they are doing is post the message on the UI Thread itself.
You're still performing a single-threaded task, just re-launching it on the UI thread if needed.
for (int i = 0; i < 500; i++){
string text = ""+i;
textBox.BeginInvoke((MethodInvoker)delegate()
{
textBox.Text += text;
});
}
The problem is that you're starting a new thread, and then that new thread is doing nothing except adding one new task for the UI thread to process that does a lot of work. To keep your form responsive you need to have time where the UI thread is doing nothing, or at least not spending a significant amount of time doing any one task.
To keep the form responsive we need to have lots of little BeginInvoke (or Invoke) calls.
private void writeText(TextBox textBox, string text)
{
for (int i = 0; i < 500; i++)
{
Invoke(new MethodInvoker(() =>
{
textBox.Text += text;
}));
}
}
By having lots of little invoke calls it allows things like paint events, mouse move/click events, etc. to be handled in the middle of your operations. Also note that I removed the InvokeRequired call. We know that this method will be called from a non-UI thread, so there's no need for it.
You're defeating the purpose of using threads.
All your thread does is tell the UI thread to execute some code using BeginInvoke().
All of the actual work happens on the UI thread.
Either you're doing data processing or you're just trying to animate the UI.
For data processing you should do all the heavy lifting on a background thread and only update the UI occasionally. In your example a TextBox is particularly troublesome in this regard, as you're adding data to the underlying data model several hundred times and the UI element (a TextBox) takes longer to render each time. You must be careful about how often to update the UI so that processing for UI updates does not overwhelm data model updates. TextBoxes are nasty like that.
In the example below, a flag set during the paint event ensures that additional UI updates aren't queued until the TextBox has finished painting the last update:
string str = string.Empty;
public void DoStuff()
{
System.Threading.ThreadPool.QueueUserWorkItem(WorkerThread);
}
void WorkerThread(object unused)
{
for (int i = 0; i < 1000; i++)
{
str += "0";
if (updatedUI)
{
updatedUI = false;
BeginInvoke(new Action<string>(UpdateUI), str);
}
}
BeginInvoke(new Action<string>(UpdateUI), str);
}
private volatile bool updatedUI = true;
void textbox1_Paint(object sender, PaintEventArgs e) // event hooked up in Form constructor
{
updatedUI = true;
}
void UpdateUI(string str)
{
textBox1.Text = str;
}
On the other hand if UI animation is your goal then you probably ought to be using something other than a TextBox. It's just not designed to handle updates so frequently. There might be some optimizations to text rendering you could make for your specific use case.
You must never use a string in high volume applications. UI or not. Multi-threading or not.
You should use StringBuilder to accumulate the string. and then assign
tb.Text = sb.ToString();

Categories