I've this situation:
Form "Menu" where you can click "New" and an insert form (with showdialog) will appear. If you insert data, I make an INSERT query on the database and I close this form.
After showDialog() statement, I have a lot of methods who performs a lot of operations in a sequential way (MUCH IMPORTANT) and sometimes update a dataGridView.
After all this computation, I simulate press of "new button" in order to allow user to insert a new item.
This is an example:
private void buttonNew_Click(object sender, EventArgs e)
{
DialogResult dr = new DialogResult();
InsertForm form = new InsertForm();
dr = form.ShowDialog();
if (dr == System.Windows.Forms.DialogResult.OK)
{
Method1();
Method2();
if (dataGrid2.RowCount > 0)
{
Method3();
Method4();
Method5();
Method6();
Method7();
Method8();
}
bNew.PerformClick();
}
}
The problem is that the "New" form (bNew.PerfomClick()) appear after a 2-3 seconds and I can't wait so much time. So I tried to create a method who include Method1 to Method8, run it in a new Thread and execut bNew.PerfomClick(), but this doesn't works because a lot of my methods update a datagridView.
Is there a way to solve this problems?
Sorry for my bad english.
------------UPDATE------------------
Now I'm trying this code:
delegate string OperazioniDelegate();
private string Operazioni()
{
if (!InvokeRequired)
{
Method1();
Method2();
............
}
else
Invoke(new OperazioniDelegate(Operazioni));
return "";
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Operazioni();
}
In this way, new insert form is showed instantly but his UI is blocked until the backgroundWorker End work..
Assuming you are running your long running operation in a seperate thread, which updates the UI elements (datagridview in your case), it is bound to experience cross thread exception, because the context in which background thread runs is different from that of UI thread. Either use InvokeRequired as given below:
delegate void valueDelegate(string value);
private void SetValue(string value)
{
if (someControl.InvokeRequired)
{
someControl.Invoke(new valueDelegate(SetValue),value);
}
else
{
someControl.Text = value;
}
}
or use BackGroundWorker class, which will do automatic marshalling of calls from background thread to Ui thread. Use of Background worker is given in this link http://www.albahari.com/threading/part3.aspx#_BackgroundWorker
Related
Well , I have a form1 which has buttons and if you click one of its button
It would load the UserControl into panel in form1
That usercontrol1 contains a lot of data like Database,charts and picture boxes too. So it would definitely make the User Interface unresponsive while loading.
So I read some article and I found out that I need to run it through another thread so I tried it and it just increase the performance by a little bit.
The usercontrol1 still make the GUI unresponsive for about 3-5 sec and what if my data become larger.
I want to make it responsive and show to user that still loading by running the animated picturebox and stop if its finish loading
here is my code:
private void click_dashb_Click(object sender, EventArgs e)
{
ParameterizedThreadStart pts = new ParameterizedThreadStart(load_UserControl);
Thread t = new Thread(pts);
t.Start();
//Animated Picturebox to show user that UI is loading
pictureBox1.Enabled = true;
hover.Location = new Point(42, 130);
}
private void load_UserControl(object state)
{
Invoke(new Action(() =>
{
//load user control through another thread
while (panel1.Controls.Count > 0)
panel1.Controls[0].Dispose();
Home frm = new Home();
frm.AutoScroll = true;
panel1.Controls.Add(frm);
frm.Show();
}));
//Stop the animated GIF means the load is finish!
pictureBox1.Enabled = false;
}
If you help me about this problem. I might apply it to all of my works. because most of it contains large data.
Thanks stackoverflow community :)
EDIT:
After reading the comments suggesting to use Background worker . I tried to use it. but still getting a little bit unresponsiveness
Here's the new code:
private void click_dashb_Click(object sender, EventArgs e)
{
bgw.RunWorkerAsync();
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
try
{
BeginInvoke((MethodInvoker)delegate
{
while (panel1.Controls.Count > 0)
panel1.Controls[0].Dispose();
Home frm = new Home();
frm.AutoScroll = true;
panel1.Controls.Add(frm);
frm.Show();
});
}
catch (Exception x)
{
MessageBox.Show("An error occured while performing operation" + x);
}
}
private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
}
private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
MessageBox.Show("Operation Cancelled");
}
else
{
MessageBox.Show("Operation Completed");
}
}
It's a little bit better but i still got a little unresponsiveness. Can you check out my code and what's wrong with this.?
thanks again
The problem with your code is that although you run the load_UserControl code in a new thread, that code calls Invoke which effectively makes al the code run on the UI thread again. I can image you did that because accessing Forms and PictureBoxes requires running on the UI thread.
The solution (in general) is to do non-UI work on a seperate thread and then switch back to the UI thread to update the visual controls.
To do this, you can conveniently use the BackgroundWorker class. In the DoWork event handler you do the heavy computation, in the RunWorkerCompleted event handler you update the controls. If you want, you can even update some controls (like a progressbar) during the work by means of the ProgressChanged event handler.
Well, just starting a new thread doesn't make the UI responsive by definition. You need to make the thread so that it actually does stuff in parallel.
Your thread does not, as it basically executes all code in this.Invoke.
That being said: Your code needs to be executed in this.Invoke, as almost everything you do needs to be done on the UI thread.
So in your case, there's really no point in parallelizing stuff, as there's no way to do what you want to do without blocking the UI thread and no technique I know of (Threads, Tasks, BackgroundWorker, etc.) will solve this problem.
How to use BackgroundWorker in C#?
Actually i'm performing an operation of filling a PDF-Form from method called fill(). It takes more time to show up the result into pdfviewer, so I decided to show up a 'processing image' using a backgroundworker, and tried using it but failing to achieve it
here is my code snippet :
private void bgwLoadFile_DoWork(object sender, DoWorkEventArgs e)
{
this.Invoke((MethodInvoker)delegate()
{
????
});
}
private void bgwLoadFile_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == true)
{
}
else if (e.Error != null)
{
}
else
{
picLoading.SendToBack();
}
}
Fill method is called when button FILL is been clicked
private void btnFill_Click(object sender, EventArgs e)
{
if (btnFill.Text == "Fill")
{
bgwLoadFile.RunWorkerAsync();
picloading.BringToFront();
Fill();
}
wat statement should i need to add in DoWork method , if i tried to add FILL() fill is been called twice ...
can any one help me out
Thanks
Add Fill(); to your bgwLoadFile_DoWork and remove it from btnFill_Click
Just a side-note you'll probably want to call your picLoading.SendToBack(); outside of that 'else' as if you error or cancel it will stay there.
So let's try to find some answers:
The method worker_DoWork() will be executed within another thread. By calling within that method this.Invoke() you're going to pass the call back to the gui thread, which makes the usage of the background worker useless. Instead within the worker method you have to call the method that needs some time and doesn't interact with the gui. If this called method produces any result (e.g. has a return value) you should write this information into the variable e.Result.
The method worker_RunWorkerCompleted() will be called within the gui thread again. Allowing you to take the result and let it somehow interact with the gui. Due to the fact, that this method will be executed on the gui thread it should be quite simple (or fast) in its doing otherwise your gui is going to freeze again.
So given these informations lets clean up your code:
private void btnFill_Click(object sender, EventArgs e)
{
if (btnFill.Text == "Fill")
{
// Update the gui for the user
// and start our long running task
// (disable buttons etc, cause the
// user is still able to click them!).
picloading.BringToFront();
bgwLoadFile.RunWorkerAsync();
}
}
private void bgwLoadFile_DoWork(object sender, DoWorkEventArgs e)
{
// Let's call the long running task
// and wait for it's finish.
Fill();
}
private void bgwLoadFile_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// We're back in gui thread.
// So let us show some results to the user.
if (e.Cancelled)
{
// To support cancellation, the long running
// method has to check some kind of cancel
// flag (boolean field) to allow fast exit of it.
labelMessage.Text = "Operation was cancelled.";
}
else if (e.Error != null)
{
labelMessage.Text = e.Error.Message;
}
// Hide the picture to allow the user
// to access the gui again.
// (re-enable buttons again, etc.)
picLoading.SendToBack();
}
I am currently writing my first program on C# and I am extremely new to the language (used to only work with C so far). I have done a lot of research, but all answers were too general and I simply couldn't get it t work.
So here my (very common) problem:
I have a WPF application which takes inputs from a few textboxes filled by the user and then uses that to do a lot of calculations with them. They should take around 2-3 minutes, so I would like to update a progress bar and a textblock telling me what the current status is.
Also I need to store the UI inputs from the user and give them to the thread, so I have a third class, which I use to create an object and would like to pass this object to the background thread.
Obviously I would run the calculations in another thread, so the UI doesn't freeze, but I don't know how to update the UI, since all the calculation methods are part of another class.
After a lot of reasearch I think the best method to go with would be using dispatchers and TPL and not a backgroundworker, but honestly I am not sure how they work and after around 20 hours of trial and error with other answers, I decided to ask a question myself.
Here a very simple structure of my program:
public partial class MainWindow : Window
{
public MainWindow()
{
Initialize Component();
}
private void startCalc(object sender, RoutedEventArgs e)
{
inputValues input = new inputValues();
calcClass calculations = new calcClass();
try
{
input.pota = Convert.ToDouble(aVar.Text);
input.potb = Convert.ToDouble(bVar.Text);
input.potc = Convert.ToDouble(cVar.Text);
input.potd = Convert.ToDouble(dVar.Text);
input.potf = Convert.ToDouble(fVar.Text);
input.potA = Convert.ToDouble(AVar.Text);
input.potB = Convert.ToDouble(BVar.Text);
input.initStart = Convert.ToDouble(initStart.Text);
input.initEnd = Convert.ToDouble(initEnd.Text);
input.inita = Convert.ToDouble(inita.Text);
input.initb = Convert.ToDouble(initb.Text);
input.initc = Convert.ToDouble(initb.Text);
}
catch
{
MessageBox.Show("Some input values are not of the expected Type.", "Wrong Input", MessageBoxButton.OK, MessageBoxImage.Error);
}
Thread calcthread = new Thread(new ParameterizedThreadStart(calculations.testMethod);
calcthread.Start(input);
}
public class inputValues
{
public double pota, potb, potc, potd, potf, potA, potB;
public double initStart, initEnd, inita, initb, initc;
}
public class calcClass
{
public void testmethod(inputValues input)
{
Thread.CurrentThread.Priority = ThreadPriority.Lowest;
int i;
//the input object will be used somehow, but that doesn't matter for my problem
for (i = 0; i < 1000; i++)
{
Thread.Sleep(10);
}
}
}
I would be very grateful if someone had a simple explanation how to update the UI from inside the testmethod. Since I am new to C# and object oriented programming, too complicated answers I will very likely not understand, I'll do my best though.
Also if someone has a better idea in general (maybe using backgroundworker or anything else) I am open to see it.
First you need to use Dispatcher.Invoke to change the UI from another thread and to do that from another class, you can use events.
Then you can register to that event(s) in the main class and Dispatch the changes to the UI and in the calculation class you throw the event when you want to notify the UI:
class MainWindow : Window
{
private void startCalc()
{
//your code
CalcClass calc = new CalcClass();
calc.ProgressUpdate += (s, e) => {
Dispatcher.Invoke((Action)delegate() { /* update UI */ });
};
Thread calcthread = new Thread(new ParameterizedThreadStart(calc.testMethod));
calcthread.Start(input);
}
}
class CalcClass
{
public event EventHandler ProgressUpdate;
public void testMethod(object input)
{
//part 1
if(ProgressUpdate != null)
ProgressUpdate(this, new YourEventArgs(status));
//part 2
}
}
UPDATE:
As it seems this is still an often visited question and answer I want to update this answer with how I would do it now (with .NET 4.5) - this is a little longer as I will show some different possibilities:
class MainWindow : Window
{
Task calcTask = null;
void buttonStartCalc_Clicked(object sender, EventArgs e) { StartCalc(); } // #1
async void buttonDoCalc_Clicked(object sender, EventArgs e) // #2
{
await CalcAsync(); // #2
}
void StartCalc()
{
var calc = PrepareCalc();
calcTask = Task.Run(() => calc.TestMethod(input)); // #3
}
Task CalcAsync()
{
var calc = PrepareCalc();
return Task.Run(() => calc.TestMethod(input)); // #4
}
CalcClass PrepareCalc()
{
//your code
var calc = new CalcClass();
calc.ProgressUpdate += (s, e) => Dispatcher.Invoke((Action)delegate()
{
// update UI
});
return calc;
}
}
class CalcClass
{
public event EventHandler<EventArgs<YourStatus>> ProgressUpdate; // #5
public TestMethod(InputValues input)
{
//part 1
ProgressUpdate.Raise(this, status); // #6 - status is of type YourStatus
// alternative version to the extension for C# 6+:
ProgressUpdate?.Invoke(this, new EventArgs<YourStatus>(status));
//part 2
}
}
static class EventExtensions
{
public static void Raise<T>(this EventHandler<EventArgs<T>> theEvent,
object sender, T args)
{
if (theEvent != null)
theEvent(sender, new EventArgs<T>(args));
}
}
#1) How to start the "synchronous" calculations and run them in the background
#2) How to start it "asynchronous" and "await it": Here the calculation is executed and completed before the method returns, but because of the async/await the UI is not blocked (BTW: such event handlers are the only valid usages of async void as the event handler must return void - use async Task in all other cases)
#3) Instead of a new Thread we now use a Task. To later be able to check its (successfull) completion we save it in the global calcTask member. In the background this also starts a new thread and runs the action there, but it is much easier to handle and has some other benefits.
#4) Here we also start the action, but this time we return the task, so the "async event handler" can "await it". We could also create async Task CalcAsync() and then await Task.Run(() => calc.TestMethod(input)).ConfigureAwait(false); (FYI: the ConfigureAwait(false) is to avoid deadlocks, you should read up on this if you use async/await as it would be to much to explain here) which would result in the same workflow, but as the Task.Run is the only "awaitable operation" and is the last one we can simply return the task and save one context switch, which saves some execution time.
#5) Here I now use a "strongly typed generic event" so we can pass and receive our "status object" easily
#6) Here I use the extension defined below, which (aside from ease of use) solve the possible race condition in the old example. There it could have happened that the event got null after the if-check, but before the call if the event handler was removed in another thread at just that moment. This can't happen here, as the extensions gets a "copy" of the event delegate and in the same situation the handler is still registered inside the Raise method.
I am going to throw you a curve ball here. If I have said it once I have said it a hundred times. Marshaling operations like Invoke or BeginInvoke are not always the best methods for updating the UI with worker thread progress.
In this case it usually works better to have the worker thread publish its progress information to a shared data structure that the UI thread then polls at regular intervals. This has several advantages.
It breaks the tight coupling between the UI and worker thread that Invoke imposes.
The UI thread gets to dictate when the UI controls get updated...the way it should be anyway when you really think about it.
There is no risk of overrunning the UI message queue as would be the case if BeginInvoke were used from the worker thread.
The worker thread does not have to wait for a response from the UI thread as would be the case with Invoke.
You get more throughput on both the UI and worker threads.
Invoke and BeginInvoke are expensive operations.
So in your calcClass create a data structure that will hold the progress information.
public class calcClass
{
private double percentComplete = 0;
public double PercentComplete
{
get
{
// Do a thread-safe read here.
return Interlocked.CompareExchange(ref percentComplete, 0, 0);
}
}
public testMethod(object input)
{
int count = 1000;
for (int i = 0; i < count; i++)
{
Thread.Sleep(10);
double newvalue = ((double)i + 1) / (double)count;
Interlocked.Exchange(ref percentComplete, newvalue);
}
}
}
Then in your MainWindow class use a DispatcherTimer to periodically poll the progress information. Configure the DispatcherTimer to raise the Tick event on whatever interval is most appropriate for your situation.
public partial class MainWindow : Window
{
public void YourDispatcherTimer_Tick(object sender, EventArgs args)
{
YourProgressBar.Value = calculation.PercentComplete;
}
}
You're right that you should use the Dispatcher to update controls on the UI thread, and also right that long-running processes should not run on the UI thread. Even if you run the long-running process asynchronously on the UI thread, it can still cause performance issues.
It should be noted that Dispatcher.CurrentDispatcher will return the dispatcher for the current thread, not necessarily the UI thread. I think you can use Application.Current.Dispatcher to get a reference to the UI thread's dispatcher if that's available to you, but if not you'll have to pass the UI dispatcher in to your background thread.
Typically I use the Task Parallel Library for threading operations instead of a BackgroundWorker. I just find it easier to use.
For example,
Task.Factory.StartNew(() =>
SomeObject.RunLongProcess(someDataObject));
where
void RunLongProcess(SomeViewModel someDataObject)
{
for (int i = 0; i <= 1000; i++)
{
Thread.Sleep(10);
// Update every 10 executions
if (i % 10 == 0)
{
// Send message to UI thread
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(Action)(() => someDataObject.ProgressValue = (i / 1000)));
}
}
}
Everything that interacts with the UI must be called in the UI thread (unless it is a frozen object). To do that, you can use the dispatcher.
var disp = /* Get the UI dispatcher, each WPF object has a dispatcher which you can query*/
disp.BeginInvoke(DispatcherPriority.Normal,
(Action)(() => /*Do your UI Stuff here*/));
I use BeginInvoke here, usually a backgroundworker doesn't need to wait that the UI updates. If you want to wait, you can use Invoke. But you should be careful not to call BeginInvoke to fast to often, this can get really nasty.
By the way, The BackgroundWorker class helps with this kind of taks. It allows Reporting changes, like a percentage and dispatches this automatically from the Background thread into the ui thread. For the most thread <> update ui tasks the BackgroundWorker is a great tool.
If this is a long calculation then I would go background worker. It has progress support. It also has support for cancel.
http://msdn.microsoft.com/en-us/library/cc221403(v=VS.95).aspx
Here I have a TextBox bound to contents.
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Debug.Write("backgroundWorker_RunWorkerCompleted");
if (e.Cancelled)
{
contents = "Cancelled get contents.";
NotifyPropertyChanged("Contents");
}
else if (e.Error != null)
{
contents = "An Error Occured in get contents";
NotifyPropertyChanged("Contents");
}
else
{
contents = (string)e.Result;
if (contentTabSelectd) NotifyPropertyChanged("Contents");
}
}
You are going to have to come back to your main thread (also called UI thread) in order to update the UI.
Any other thread trying to update your UI will just cause exceptions to be thrown all over the place.
So because you are in WPF, you can use the Dispatcher and more specifically a beginInvoke on this dispatcher. This will allow you to execute what needs done (typically Update the UI) in the UI thread.
You migh also want to "register" the UI in your business, by maintaining a reference to a control/form, so you can use its dispatcher.
Thank God, Microsoft got that figured out in WPF :)
Every Control, like a progress bar, button, form, etc. has a Dispatcher on it. You can give the Dispatcher an Action that needs to be performed, and it will automatically call it on the correct thread (an Action is like a function delegate).
You can find an example here.
Of course, you'll have to have the control accessible from other classes, e.g. by making it public and handing a reference to the Window to your other class, or maybe by passing a reference only to the progress bar.
Felt the need to add this better answer, as nothing except BackgroundWorker seemed to help me, and the answer dealing with that thus far was woefully incomplete. This is how you would update a XAML page called MainWindow that has an Image tag like this:
<Image Name="imgNtwkInd" Source="Images/network_on.jpg" Width="50" />
with a BackgroundWorker process to show if you are connected to the network or not:
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
public partial class MainWindow : Window
{
private BackgroundWorker bw = new BackgroundWorker();
public MainWindow()
{
InitializeComponent();
// Set up background worker to allow progress reporting and cancellation
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
// This is your main work process that records progress
bw.DoWork += new DoWorkEventHandler(SomeClass.DoWork);
// This will update your page based on that progress
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
// This starts your background worker and "DoWork()"
bw.RunWorkerAsync();
// When this page closes, this will run and cancel your background worker
this.Closing += new CancelEventHandler(Page_Unload);
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
BitmapImage bImg = new BitmapImage();
bool connected = false;
string response = e.ProgressPercentage.ToString(); // will either be 1 or 0 for true/false -- this is the result recorded in DoWork()
if (response == "1")
connected = true;
// Do something with the result we got
if (!connected)
{
bImg.BeginInit();
bImg.UriSource = new Uri("Images/network_off.jpg", UriKind.Relative);
bImg.EndInit();
imgNtwkInd.Source = bImg;
}
else
{
bImg.BeginInit();
bImg.UriSource = new Uri("Images/network_on.jpg", UriKind.Relative);
bImg.EndInit();
imgNtwkInd.Source = bImg;
}
}
private void Page_Unload(object sender, CancelEventArgs e)
{
bw.CancelAsync(); // stops the background worker when unloading the page
}
}
public class SomeClass
{
public static bool connected = false;
public void DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
int i = 0;
do
{
connected = CheckConn(); // do some task and get the result
if (bw.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
Thread.Sleep(1000);
// Record your result here
if (connected)
bw.ReportProgress(1);
else
bw.ReportProgress(0);
}
}
while (i == 0);
}
private static bool CheckConn()
{
bool conn = false;
Ping png = new Ping();
string host = "SomeComputerNameHere";
try
{
PingReply pngReply = png.Send(host);
if (pngReply.Status == IPStatus.Success)
conn = true;
}
catch (PingException ex)
{
// write exception to log
}
return conn;
}
}
For more information: https://msdn.microsoft.com/en-us/library/cc221403(v=VS.95).aspx
I am new to C#, I hope my description of the problem is readable.
Here's my problem, I am developing a app for a win6.5 mobile. The App should have some memu items, one is 'scan', when clicked, it scans repeatedly the wifi access points nearby, and displays them on a listview. So i create a thread with a while loop for scanning every 10 seconds, i also use listview.invoke to make the listview accessible in the thread. Things looks fine when 'scan' is clicked, however, other menu items cannot be clicked due to the running of the while loop thread. I stuck here for several days, many thanks for u guys help~
private void menuItemScan_Click(object sender, EventArgs e)
{
...
Thread t = new Thread(new ThreadStart(ScanThread));
t.Start();
}
private void ScanThread()
{
listView1.Invoke(new APScanCallback(APScan));
}
public void APScan()
{
while (true)
{
listView1.Items.Clear();
foreach (AccessPoint ap in wzcInterface.NearbyAccessPoints)
{
ListViewItem item = new ListViewItem(ap.Name);
item.SubItems.Add(ap.PhysicalAddress.ToString());
item.SubItems.Add(ap.SignalStrength.Decibels.ToString());
item.SubItems.Add(ap.AuthenticationMode.ToString());
listView1.Items.Add(item);
}
listView1.Refresh();
Thread.Sleep(10000);
}
}
Control.Invoke "enqueue" the method execution to the thread handling UI (in order to serialize those routine call to the other UI routine calls).
Even if you start a thread which calls Control.Invoke, the routine APSScan is executed in thread which has called Application.Run... and what I see is that APSScan never returns, causing to freeze the UI thread.
The solution is to call Control.Invoke multiple times, looping in ScanThread routine.
Using your code:
private void ScanThread()
{
while (true) {
listView1.Invoke(new APScanCallback(APScan));
Thread.Sleep(10000);
}
}
public void APScan()
{
listView1.Items.Clear();
foreach (AccessPoint ap in wzcInterface.NearbyAccessPoints)
{
ListViewItem item = new ListViewItem(ap.Name);
item.SubItems.Add(ap.PhysicalAddress.ToString());
item.SubItems.Add(ap.SignalStrength.Decibels.ToString());
item.SubItems.Add(ap.AuthenticationMode.ToString());
listView1.Items.Add(item);
}
listView1.Refresh();
}
Your code is actually running in the main thread.
listView1.Invoke(new APScanCallback(APScan));
This code submits execution of APScan in the main application thread. Just use the timer insteaf of worker thread.
I'm just trying to run a new thread each time a button click even occurs which should create a new form. I tried this in the button click event in the MainForm:
private void button1_Click(object sender, EventArgs e)
{
worker1 = new Thread(new ThreadStart(thread1));
worker2 = new Thread(new ThreadStart(thread2));
worker1.Start();
worker2.Start();
}
private void thread1()
{
SubForm s = new SubForm();
s.Show();
}
private void thread2()
{
SubForm s = new SubForm();
s.Show();
}
The code in the Subform button click event goes like this:
private void button1_Click(object sender, EventArgs e)
{
int max;
try
{
max = Convert.ToInt32(textBox1.Text);
}
catch
{
MessageBox.Show("Enter numbers", "ERROR");
return;
}
progressBar1.Maximum = max;
for ( long i = 0; i < max; i++)
{
progressBar1.Value = Convert.ToInt32(i);
}
}
Is this the right way? Because I'm trying to open two independent forms, operations in one thread should not affect the other thread.
Or is BackGroundworker the solution to implement this? If yes, can anyone please help me with that?
You do not need to run forms in separate threads. You can just call s.Show() on multiple forms normally. They will not block each other.
Of course, if you’re doing something else, like some sort of calculation or other task that takes a long while, then you should run that in a separate thread, but not the form.
Here is a bit of code that will let you create a progress bar that shows progress for a long process. Notice that every time to access the form from inside the thread, you have to use .Invoke(), which actually schedules that invocation to run on the GUI thread when it’s ready.
public void StartLongProcess()
{
// Create and show the form with the progress bar
var progressForm = new Subform();
progressForm.Show();
bool interrupt = false;
// Run the calculation in a separate thread
var thread = new Thread(() =>
{
// Do some calculation, presumably in some sort of loop...
while ( ... )
{
// Every time you want to update the progress bar:
progressForm.Invoke(new Action(
() => { progressForm.ProgressBar.Value = ...; }));
// If you’re ready to cancel the calculation:
if (interrupt)
break;
}
// The calculation is finished — close the progress form
progressForm.Invoke(new Action(() => { progressForm.Close(); }));
});
thread.Start();
// Allow the user to cancel the calculation with a Cancel button
progressForm.CancelButton.Click += (s, e) => { interrupt = true; };
}
Although I'm not 100% aware of anything that says running completely seperate forms doing completely isolated operations in their own threads is dangerous in any way, running all UI operations on a single thread is generally regarded as good practice.
You can support this simply by having your Subform class use BackgroundWorker. When the form is shown, kick off the BackgroundWorker so that it processes whatever you need it to.
Then you can simply create new instances of your Subform on your GUI thread and show them. The form will show and start its operation on another thread.
This way the UI will be running on the GUI thread, but the operations the forms are running will be running on ThreadPool threads.
Update
Here's an example of what your background worker handlers might look like - note that (as usual) this is just off the top of my head, but I think you can get your head around the basic principles.
Add a BackgroundWorker to your form named worker. Hook it up to the following event handlers:
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Executed on GUI thread.
if (e.Error != null)
{
// Background thread errored - report it in a messagebox.
MessageBox.Show(e.Error.ToString());
return;
}
// Worker succeeded.
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Executed on GUI thread.
progressBar1.Value = e.ProgressPercentage;
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
// Executed on ThreadPool thread.
int max = (int)e.Argument;
for (long i = 0; i < max; i++)
{
worker.ReportProgress(Convert.ToInt32(i));
}
}
Your click handler would look something like:
void button1_Click(object sender, EventArgs e)
{
int max;
try
{
// This is what you have in your click handler,
// Int32.TryParse is a much better alternative.
max = Convert.ToInt32(textBox1.Text);
}
catch
{
MessageBox.Show("Enter numbers", "ERROR");
return;
}
progressBar1.Maximum = max;
worker.RunWorkerAsync(max);
}
I hope that helps.
Try this. It runs the new Form on its own thread with its own message queues and what not.
Run this code:
new Thread(new ThreadStart(delegate
{
Application.Run(new Form());
})).Start();
Use Thread.CurrentThread.GetHashCode() to test that is runs on different thread.
It's possible to run different forms on different threads. There are two caveats I'm aware of:
Neither form may be an MDI client of the other. Attempting to make a form an MDI client of another when the forms have different threads will fail.
If an object will be sending events to multiple forms and all forms use the same thread, it's possible to synchronize the events to the main thread before raising it. Otherwise, the event must be raised asynchronously and each form must perform its own synchronization mechanism for incoming events.
Obviously it's desirable not to have any window's UI thread get blocked, but using separate threads for separate windows may be a nice alternative.