I'm new to C# and i have a problem ... when i run my windows form application and press start button, during runtime i can't do anything with the form (close, minimize, move, ...) but when it's done i can close the form.
What should i do to solve it?
using System;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication10
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void c1Button1_Click(object sender, EventArgs e)
{
for ( int i = 0; i < 100; i++)
{
c1RadialGauge1.Value = i;
textBox1.Text = i.ToString();
textBox1.Update();
c1Gauge1.Refresh();
Thread.Sleep(100);
}
}
}
}
Since your button handler is running in the UI thread until it is finished, your UI blocks and you can't do anything else with that.
One solution to that would be to make the handler async and use await a Task.Delay() instead of using Thread.Sleep():
private async void c1Button1_Click(object sender, EventArgs e)
{
c1Button1.Enabled = false; // avoid multiple clicks
for ( int i = 0; i < 100; i++)
{
c1RadialGauge1.Value = i;
textBox1.Text = i.ToString();
textBox1.Update();
c1Gauge1.Refresh();
await Task.Delay(100);
}
c1Button1.Enabled = true; // allow further clicks
}
The compiler translates this code into a state machine. At the await keyword, the control flow is returned to the caller (the UI) and resumed when Task.Delay() has finished. So while Task.Delay() runs, the UI thread has time to react on other events (e.g. user interaction).
I added two lines to disable the button while the whole thing is running, so that the user can't start this several times.
But this is only possible using .NET 4.5 or newer. Read this for more information about async/await.
If you have to use an older .NET framework, you may use a BackgroundWorker instead.
Related
Im trying to make a simple application to learn some things in c# (Visual Studio).
For now i am building a music player, and one of the actions is fading out the music at the button click event.
I've got no problem with building a fade-out part, i made a while loop and put the volume down with 1% eacht time the loop is running. Also i update a label with the fade value.
Only problem is, for slowing down the fading i'm using the Thread.Sleep event, and that part is freezing my application, and also is blocking any updates to my text label with the fade value.
The fading is working fine, so the only part I have to work on is another option to build some delay in. On some topics over here i did read about the timer, and i added a timer component in Visual Studio. Only problem, I am new to c# and don't know how to use it correctly in this while loop.
Can anybody give me some help?
The current code is:
private void BtnPodiumtune1Fadeout_Click(object sender, EventArgs e)
{
PlayerPodiumtune1.settings.volume = 100;
fade1 = 100;
while (fade1 != -1)
{
PlayerPodiumtune1.settings.volume = fade1;
Fadelevel1.Text = fade1.ToString();
System.Threading.Thread.Sleep(30);
fade1 = fade1 - 1;
}
PlayerPodiumtune1.Ctlcontrols.stop();
}
You could use a pattern like this instead of a timer. A timer is a fine way to go, just throwing this option out there:
private async void button_Click(object sender, EventArgs e)
{
if (Monitor.TryEnter(sender))
{
int fade1 = 1000;
while (fade1 != -1)
{
await Task.Delay(30);
fade1--;
}
}
}
So sender is the button, and Monitor.TryEnter prevents the function from being run again until the function is done. async tells the framework that this function can be executed asynchronously and is necessary for await. await returns control of the thread to the UI until the task is done.
PS--You're going to need something like Monitor.TryEnter to prevent re-entrancy in a timer-based solution as well, by the way.
This is a Console Application in C#:
using System;
namespace WaitAsync
{
class Program
{
static void Main(string[] args)
{
bool ok = false;
Console.Write("EnterTime (Seconds): ");
int time = Convert.ToInt32(Console.ReadLine()) * 1000;
while (ok != true)
{
System.Threading.Thread.Sleep(time);
ok = true;
Console.WriteLine("Waiting Time Just Finished");
}
Console.ReadLine();
}
}
}
I'm using GTK# to build a GUI. Some data is processed in the background and I'd like to see some info about the progress on the user interface. Here is some code demonstrating the way I am trying to do this:
using System;
using Gtk;
using System.Threading.Tasks;
using System.Threading;
public partial class MainWindow: Gtk.Window
{
//a button and a textfield
private VBox VB = new VBox();
private Button B = new Button("Push dis");
private Label L = new Label("0");
public MainWindow () : base (Gtk.WindowType.Toplevel)
{
B.Clicked += OnClickEvent;
////////////////////
VB.PackStart (B);
VB.PackStart (L);
Add (VB);
ShowAll ();
Build ();
}
protected void OnDeleteEvent (object sender, DeleteEventArgs a)
{
Application.Quit ();
a.RetVal = true;
}
//async method incrementing variable, simulating some work and sending its progress
protected async Task CounterGUIUpdateAsync(IProgress<string> progress)
{
await Task.Run (() => {
for (int i = 0; i <= 10000; i++) {
Thread.Sleep (100);
if(progress != null)
{
var stri = Convert.ToString(i);
progress.Report(stri);
}
}
});
}
//event handler for the button
protected async void OnClickEvent(object sender, EventArgs e)
{
var ProgressIndicator = new Progress<string> (ReportProgress);
await CounterGUIUpdateAsync (ProgressIndicator);
}
//action connected to the progress instance
protected void ReportProgress(string value)
{
L.Text = value;
}
}
Running the code will start off as expected, but at some point it is likely that the displayed counter gets stuck. The GUI won't update anymore, turning black if it has been minimized. It is still functional though.
Help is appreciated very much.
I think your problem is that you're not using the main thread (the gui thread) when using the Gtk+ API. You need to use Gtk.Application.Invoke() passing a delegate that manipulates the UI, so the action is performed in the correct thread.
You can read more about this here.
We are learning multi-threadding today in class and we came across a very curious error. When doing a for loop in our new thread the upper bound of the for loop keeps getting passed. The thread is being killed but then another value will appear and end another thread.
For the purpose of debugging the error I changed the upper bound to 90 to avoid the OutOfRange Exception on the progressbar.
While outputting the counter to the progressing bar and updating the progress bar I got this in my output window.
If i commented out the updating on the progress bar (pbLoad.Value = i;) I got this in my output window
I have tried changing the loop to i<101 and also tried moving where the i++ was but it made no difference
EDIT: This is coming from the BeginInvoke. When i switched it to Invoke it worked but then I will get a deadlock when trying to use the cancel button.
Here is the code:
public partial class Form1 : Form
{
Thread backgroundThread;
bool stopExecution = false;
public Form1()
{
InitializeComponent();
}
private void btnStart_Click(object sender, EventArgs e)
{
stopExecution = false;
btnStart.Enabled = false;
backgroundThread = new Thread(DoDomethingThatTakesAWhile);
backgroundThread.Start();
}
private void DoDomethingThatTakesAWhile()
{
for (int i = 0; i <= 100; i++)
{
if (!stopExecution)
{
Thread.Sleep(100);
if (pbLoad.InvokeRequired)
{
MethodInvoker myMethod
= new MethodInvoker(
delegate
{
if (!stopExecution)
{
pbLoad.Value = i;
Debug.WriteLine(i); //i to output window
}
});
pbLoad.BeginInvoke(myMethod);
}
else
{
pbLoad.Value = i;
}
}
else
{
break;
}
}
}
private void btnCancel_Click(object sender, EventArgs e)
{
//backgroundThread.Abort();
stopExecution = true;
backgroundThread.Join();
pbLoad.Value = 0;
btnStart.Enabled = true;
}
}
When you call MethodInvoke it will not occurs at that moment, but some time later.
In your scenario you have a chance of following to occurs:
invoked code is finally executed;
the loop is already finished (and i become 101)
you are accessing i directly and you read 101.
And to fix it you can make a copy of i (by passing it as a parameter to invoked method):
pbLoad.BeginInvoke(new Action<int>(a =>
{
if (!stopExecution)
{
pbLoad.Value = a;
Debug.WriteLine(a); //a to output window
}
}), new object[] { i });
P.S: you don't need to check for InvokeRequired, unless you plan to call DoDomethingThatTakesAWhile method directly, which I assume is not the case.
You're using BeginInvoke which explicitly opens the possibility for races. I recommend synchronous invoking.
Furthermore, you are capturing i, not its value. This is racy and only works by accident because you're sleeping.
Either of the changes will fix the problem. Do both of them.
If you can, abolish this low-level use of synchronization and use async/await.
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();
I have a windows form application that needs to load a bunch of things before loading the Main window. I thought this would justify a ProgressBar, so I thought I display another form that contains the ProgressBar Control using the constructor of my main form.
It all works fine but if I try to put the text in a Label on the intro form its content won't show until the main form is loaded. Is here a way to avoid this other than loading the intro window first?
Warning: this post contains elements of self promotion ;o)
I would probably use a splash form in this case. I wrote a blog post a while ago (triggered by this SO Q&A) about a thread-safe splash form that could be used together will long-running main form initializations.
In short the approach is to using ShowDialog, but to create and display the form on a separate thread so it doesn't block the main thread. The form contains a status message label (could of course be extended with a progressbar as well). Then there is a static class that provides thread-safe methods for displaying, updating and closing the splash form.
Condensed code samples (for commented code samples, check the blog post):
using System;
using System.Windows.Forms;
public interface ISplashForm
{
IAsyncResult BeginInvoke(Delegate method);
DialogResult ShowDialog();
void Close();
void SetStatusText(string text);
}
using System.Windows.Forms;
public partial class SplashForm : Form, ISplashForm
{
public SplashForm()
{
InitializeComponent();
}
public void SetStatusText(string text)
{
_statusText.Text = text;
}
}
using System;
using System.Windows.Forms;
using System.Threading;
public static class SplashUtility<T> where T : ISplashForm
{
private static T _splash = default(T);
public static void Show()
{
ThreadPool.QueueUserWorkItem((WaitCallback)delegate
{
_splash = Activator.CreateInstance<T>();
_splash.ShowDialog();
});
}
public static void Close()
{
if (_splash != null)
{
_splash.BeginInvoke((MethodInvoker)delegate { _splash.Close(); });
}
}
public static void SetStatusText(string text)
{
if (_splash != null)
{
_splash.BeginInvoke((MethodInvoker)delegate { _splash.SetStatusText(text); });
}
}
}
Example of usage:
SplashUtility<SplashForm>.Show();
SplashUtility<SplashForm>.SetStatusText("Working really hard...");
SplashUtility<SplashForm>.Close();
There sure is. It's called a BackgroundWorker.
Here is a code snippet from Figo Fei with slight modification for explanation purposes:
private void button1_Click(object sender, EventArgs e)
{
progressBar1.Maximum = 100;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// This would be the load process, where you put your Load methods into.
// You would report progress as something loads.
for (int i = 0; i < 100; i++)
{
Thread.Sleep(100);
backgroundWorker1.ReportProgress(i); //run in back thread
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) //call back method
{
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) //call back method
{
progressBar1.Value = progressBar1.Maximum;
}
Hope this helps you.
You can show your SplashForm from either the main program or the MainForm constructor, that doesn't really matter. What you are seeing is that as long as your Loading process isn't completed, no messages are processed and hence no Screen updates are happening. The ProgressBar is an exception, it runs it's own thread for precisely this reason.
The short solution is to do a SplashForm.Update() after changing the Label. A little more involved would be to start a separate Thread with a MessagePump (Application.Run). Here is a SO question with some more leads.
The problem is most likely because there is not a running message loop at the time you are attempting to display the progress bar form. There should be a line of code that looks something like the following in the entry point of your application.
Application.Run(new Form1());
The call to Application.Run will start the message loop, but do you see how the Form1 constructor is executed before the message loop is running? And since your progress bar logic is in that constructor then there is no mechanism running that can dispatch the form's painting messages.
I think the best approach is to load a splash screen first and kick off a worker thread (you could use BackgroundWorker for that) that does the time consuming work. The progress bar will live on the splash screen form and you will update that periodically. Once the work is complete then you can close the splash screen and load the main form.