I need to detect a procedure from a click event has finished without delaying the main wpf process..
What I don't want
public void click_event(object sender,routedeventargs)
{
<launch a bunch of threads>
while(<threads.are alive>);
<code for what i want to do after threads are done>
}
public void threadtask()
{}
what i just did
public void click_event()
{
foreach(<thread>)
<give thread task and start() each>
}
}
but this will not detect when the threads are done.. need help here. Thanks.
You are asking for two different things. You want the main thread to not be blocked, but you want to do something when the other threads are done (which you have to wait for). Consider starting the threads from a new thread, then let that other thread do the work. Something like this:
public void click_event()
{
<start new thread>
<foreach(thread in threads)>
<do work>
<join threads>
<do your work here>
}
So all of the work is on a different thread, even the work you want to do afterward. Given that, do you need more than one worker thread anyway?
Check out this article
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.ClickMe.Click += new RoutedEventHandler(ClickMe_Click);
}
void ClickMe_Click(object sender, RoutedEventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (workSender, workE) =>
{
string argument = (string)workE.Argument;
// argument == "Some data"
System.Threading.Thread.Sleep(2000);
};
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync("Some data");
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.ResultsTextBlock.Text = "I'm done";
}
}
Related
I'm using the ThreadPool to manage my threads. Separately from the UI thread I have a thread that does data retrieval and general work operations and I have a 3rd thread that updates the UI to reflect the status of requested operations.
See code below:
// ui thread
private void btnLoadClients_Click(object sender, EventArgs e)
{
// start thread 1
ThreadPool.QueueUserWorkItem(new Form1().LoadClientList);
}
// thread 1
private void LoadClientList(object state)
{
ThreadBusy = true;
ThreadAction = "Loading Clients...";
// start thread 2
ThreadPool.QueueUserWorkItem(new Form1().ShowProgress);
// get data
ClientController c = new ClientController();
List<Client> clients = c.GetClient();
foreach (Client item in clients)
{
cmbClientList.Items.Add(item.Name);
}
cmbClientList.Items.Insert(0, "Please select a client");
ThreadBusy = false;
}
// thread 2
private void ShowProgress(object state)
{
while (ThreadBusy)
{
foreach (string action in lstAction.Items)
{
// write the action that's being taken to the listbox
if (String.Compare(action, ThreadAction) != 0)
lstAction.Items.Add(ThreadAction);
}
}
}
Problem is that although ShowProgress is being hit when I set a breakpoint on it, execution isn't entering it really. The while (ThreadBusy) line isn't getting hit ever.
Have I got anything wrong here?
ThreadPool.QueueUserWorkItem(new Form1().LoadClientList);
ThreadPool.QueueUserWorkItem(new Form1().ShowProgress);
You're creating new Form1 instances every time you start a background thread, every action you take in these methods will happen to these new, "unbounded" instances, not on the one interacting with the user.
If you want to perform background work in WinForms you can leverage the BackgroundWorker class.
A really simple example:
public static class Program
{
public static void Main()
{
var backgroundWorker = new BackgroundWorker();
backgroundWorker.WorkerReportsProgress = true
backgroundWorker.Disposed += BackgroundWorker_Disposed;
backgroundWorker.DoWork += BackgroundWorker_DoWork;
backgroundWorker.ProgressChanged += BackgroundWorker_ProgressChanged;
backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
backgroundWorker.RunWorkerAsync();
}
private static void BackgroundWorker_Disposed(object sender, EventArgs e)
{
// Cleanup after yourself.
}
private static void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Do your things in background.
}
private static void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Notify progress.
}
private static void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// The background task is complete ("successfully" is NOT implied).
}
}
ThreadBusy property that you set to true belongs to a different Form1 object. because the thread that runs ShowProgress executes on a new instance of Form1 and its ThreadBusy property is false always. Thats why it is not entering into the while loop.
Can you try this
ThreadPool.QueueUserWorkItem(this.ShowProgress);
instead of
ThreadPool.QueueUserWorkItem(new Form1().ShowProgress);
I'm using a BackgroundWorker to periodically check a hardware switch. Due to it is connected via a slow RS485 network, I have to delay the next status update.
On switch Status change I want to update an OK/nOK Picture Box. This is realized as a green OK pictureBox over a nOK pictureBox. No real work is done here.
For expandability I decided to use the Backgroundworker. Finally I want to have a hidden worker, which
provides globally the Status of three switches and
updates on StatusChange the PictureBoxes.
Problem description
Once the BackgroundWorker is started, it works as expected. However the GUI freezes.
What did I try?
The MSDN BackgroundWorker Class Note 1
says, that GUI should be updated via ProgressChanged. I tried to raise this Event by Worker_Switch.ReportProgress(fakeProgress++) and failed. The PictureBox wasn't updated anymore.
Snippet from designer
this.Worker_Switch = new System.ComponentModel.BackgroundWorker();
//
// Worker_Switch
//
this.Worker_Switch.WorkerSupportsCancellation = true;
this.Worker_Switch.DoWork += new System.ComponentModel.DoWorkEventHandler(this.Worker_Switch_DoWork);
Snippet from Main Form
delegate void SetEventCallback(object sender, DoWorkEventArgs e); // Threadsafe calls for DoWork
private void btnBackgroundworker_Click(object sender, EventArgs e)
{
if (!Worker_Switch.IsBusy)
{
Worker_Switch.RunWorkerAsync();
}
}
private void Worker_Switch_DoWork(object sender, DoWorkEventArgs e)
{
// Worker Thread has no permission to change PictureBox "pictureBoxSwitchrightOK"
// Therefore this method calls itsself in the MainThread, if necessary.
while (!Worker_Switch.CancellationPending)
{
if (this.pictureBoxSwitchrightOK.InvokeRequired) // Worker Thread
{
System.Threading.Thread.Sleep(400);
SetEventCallback myCall = new SetEventCallback(Worker_Switch_DoWork);
this.Invoke(myCall, new object[] { sender, e });
}
else // Main Thread
{
// Turns OK Picture Box invisible, if nOk State (Switch pushed)
pictureBoxSwitchrightOK.Visible = SwitchOK("right"); // true: OK (green)
this.Refresh();
}
}
private bool SwitchOK(string rightOrLeft) // select one of the switches
{ (...)} // gets hardware switch status
Edit: Special Thanks to laszlokiss88 (3 possibilities) and JMK (for simplicity with System.Windows.Forms Timer from toolbox)
This alternative from Toolbox also worked:
this.timer_Switch.Enabled = true;
this.timer_Switch.Interval = 400;
this.timer_Switch.Tick += new System.EventHandler(this.timer_Switch_Tick);
private void timer_Switch_Tick(object sender, EventArgs e)
{
motorSwitchControl.Init(); // globally available Switch status
SwitchRight = SwitchOK("right");
SwitchRightOK.Visible = SwitchRight;
SwitchLeft = SwitchOK("left"); // globally available Switch status
SwitchLeftOK.Visible = SwitchLeft;
SwitchAllOK = SwitchRight & SwitchLeft;
this.Refresh();
}
a) Is it correct, that the Sleep() actually happens in the Worker Thread?
- no Main Thread
b) What is going wrong, if I manipulate user interface objects in DoWork? (Contrary to MSDN Note)
- works in Main Thread?
c) What is the correct way to periodically update a PictureBox? DoWork, ProgressChanged, RunWorkerCompleted...?
- Three possibilities from laszlokiss88 answer.
You can update the UI from the DoWork event via the Dispatcher, or Control.Begininvoke(winforms), or you can do it via the ProgressChanged event of the BackgroundWorker:
public MainWindow()
{
InitializeComponent();
var bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerAsync();
}
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// You are in the main thread
// Update the UI here
string data = (string)e.UserState;
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
// You are in a worker thread
(sender as BackgroundWorker).ReportProgress(0, "right");
}
For a start you should almost never have a need to put an active background thead to sleep. I am also not sure why you are constructing/defining the delegate this way, try some thing like
public delegate void UpdatePictureBox();
myDelegate = new UpdatePictureBox(UpdatePictureboxMethod);
then you have a method UpdatePictureBoxMethod
private void UpdatePictureBoxMethod()
{
this.pictureBox1.Image = Properties.Resources.SomeImage;
}
or something simalar, where you pass in the image to update to.
Alternatively you could use the (bgWorker as BackgroundWorker).ReportProgress(progress, object); method. So from the background thread you call
(bgWorker as BackgroundWorker).ReportProgress(progressBarValue, infoBall);
where here class IfoBall will hold all your important information
class InfoBall
{
public int nProgressBar { get; set; }
public int nMaxProgressBar { get; set; }
public Image image { get; set; }
}
then you can pass this object back to the UI thread and do your updates
void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// On UI thread.
InfoBall someBall = (InfoBall)e.UserState;
this.pictureBox1.Image = someBall.image;
// etc...
}
I hope this helps.
I have a MainFrame window with imageViewer control on it. Also there is my dll which calculates changes for the image all was working fine before I decided to add ProgressDialog.(( The Idea was - firstly I am loading the image via dll to main frame (this still OK). Then if user clicks button then show ProgressDialog and in worker.DoWork create new image via the same dllwrapper class (I am using "new")
All seems to be ok but when i am trying to set my currentImage property of imageviewer control (this is nothing more then setter for Image)it show me this error!
This is the code of my userButtonClickHandler from where I am launching ProgressDialog:
void OnThumbnailClick(object sender, RoutedEventArgs e)
{
pd = new ProgressDlg();
pd.Cancel += CancelProcess;
int max = 1000;
System.Windows.Threading.Dispatcher pdDispatcher = pd.Dispatcher;
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
LibWrap lwrap = new LibWrap();//!NEW instance for dll wrapper!
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
imageViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));//ERROR IS HERE!!!//The calling thread cannot access this object because a different thread owns it.
//what process??
};
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
pd.Close();
};
worker.RunWorkerAsync();
pd.ShowDialog();
}
There is function from the same MainFrame class for canceling (There is OK too)
void CancelProcess(object sender, EventArgs e)
{
worker.CancelAsync();
}
This is class for ProgressDlg (it has nothing more then progress bar and cancel button):
public partial class ProgressDlg : Window
{
public ProgressDlg()
{
InitializeComponent();
}
public string ProgressText
{
set
{
this.lblProgress.Content = value;
}
}
public int ProgressValue
{
set
{
this.progress.Value = value;
}
}
public event EventHandler Cancel = delegate { };
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
Cancel(sender, e);
}
}
}
I am working with this problem for (almost) two days and still couldn't find the solution. Help me please if you have an idea.
1 UPDATE
It seems to me that you was right about this threads - when I am trying to load previously loaded(initial) image (from the main thread) -it loads OK but if I am trying libWrap it fails due to processes conflict!
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
imageViewer.Width = 1000;//work!
imageViewer.CurrentImage = MyPrj.App.draggedImage;//Work!
imageViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));//Fail =(!
}
2 UPDATE
I have tried this construction OnThumbnailClick
Application.Current.MainWindow.Dispatcher.BeginInvoke(new Action(() =>
{
imaeViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(FXPhotoStudio.App.draggedImage));
}
This caused same error/ Perhaps it will be correct to pass this value in MainThread (UI)? But I have no idea how.( I couldnot use serializers - becouse it is rapidly calling operation and this images are temporary/
WPF cannot alter items that were created on another thread.
So if you create an ImageViewer on one thread, you cannot alter it's properties on another thread.
Instead, use the Dispatcher, which is WPF's internal message queue for the main UI thread, to update your objects.
Or, use Henk's Answer to do your work on another thread, but return the result to the main thread so it can update your ImageViewer's properties
You need at least these changes:
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
args.Result = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));
};
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
if (args.Error != null)
{ ... } // handle error
else if (args.Cancelled)
{ ... } // handle Cancel
else
{
imageViewer.CurrentImage = args.Result;
}
pd.Close();
}
I'm not sure if it's enough but try again.
The imageViewer was created on the main thread of the application (which is appropriate because it is a UI control). UI controls can ONLY be accessed by the thread which created it, and that thread must have its own dispatcher (by which I mean message loop).
Remove the threading code, and it will work.
If you want this to popup the window and then show the image when the conversion completes, you will have to store the returned image in a variable until you return to the main thread, and then make the assignment to the imageViewer.
In my application, I create a new UI-Thread with the fallowing code:
Thread thread = new Thread(() =>
{
MyWindow windowInAnotherThread = new MyWindow();
windowInAnotherThread.Show();
System.Windows.Threading.Dispatcher.Run();
}) { IsBackground = true };
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
This give me the fallowing problem:
In the constructor of the MyWindow class, a BackgroundWorker is executed. In the RunWorkerCompleted there should a Control be updated with some data, which the BackgroundWorker is calculating.
I have build a small sample, which is illustrating this:
public partial class MyWindow : Window {
public MyWindow() {
InitializeComponent();
var bw = new BackgroundWorker();
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.RunWorkerAsync();
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
this.Title = "Calculated title";
}
void bw_DoWork(object sender, DoWorkEventArgs e) {
Thread.Sleep(3000);
}
}
In bw_RunWorkerCompleted() I get an InvalidOperationException (The calling thread cannot access this object because a different thread owns it.). It looks like, that the BackgroundWorker is not returning to the correct UI-Thread from which it was started from.
Can someone help me, what I can do to solve this problem? I can't change the Code which is executing the BackgroundWorker, because it is in a framework, which I use. But I can do something else in the RunWorkerCompleted-Event. But I have no idea, how to solve this problem.
The problem is that the window is getting created too soon. The thread doesn't have a synchronization context yet. You can see this is the debugger by setting a breakpoint on BGW constructor call and look at Thread.CurrentThread.ExecutionContext.SynchronizationContext. It's null. Which is what BGW uses to decide how to marshal the RunWorkerCompleted event. Which no synchronization context, the event runs on a threadpool thread and that invokes wrath.
You need to get the dispatcher initialized sooner. Not 100% this is the correct way but it did seem to work:
Thread thread = new Thread(() => {
System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => {
MyWindow windowInAnotherThread = new MyWindow();
windowInAnotherThread.Show();
}));
System.Windows.Threading.Dispatcher.Run();
}) { IsBackground = true };
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
You also have to explicitly force the thread to shutdown. Add this method to MyWindow:
protected override void OnClosed(EventArgs e) {
Dispatcher.BeginInvokeShutdown(System.Windows.Threading.DispatcherPriority.Background);
}
Ran into similar issue. Based on note 1 and 2 below I created UIBackgroundWorker. May be it can help other developers who encounter this issue.
If it works then please let me know or update the design for benefit of other developers.
public class UIBackgroundWorker : BackgroundWorker
{
private System.Windows.Threading.Dispatcher uiDispatcher;
public SafeUIBackgroundWorker(System.Windows.Threading.Dispatcher uiDispatcher)
: base()
{
if (uiDispatcher == null)
throw new Exception("System.Windows.Threading.Dispatcher instance required while creating UIBackgroundWorker");
else
this.uiDispatcher = uiDispatcher;
}
protected override void OnProgressChanged(ProgressChangedEventArgs e)
{
if (uiDispatcher.CheckAccess())
base.OnProgressChanged(e);
else
uiDispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => base.OnProgressChanged(e)));
}
protected override void OnRunWorkerCompleted(RunWorkerCompletedEventArgs e)
{
if (uiDispatcher.CheckAccess())
base.OnRunWorkerCompleted(e);
else
uiDispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => base.OnRunWorkerCompleted(e)));
}
}
The problem is that you need to setup the SynchronizationContext. This is normally not an issue, as Dispatcher.Invoke will set it up for you, but since you're using BackgroundWorker in the constructor (which is fired prior to Dispatcher.Run), no context is setup.
Change your thread creation to:
Thread thread = new Thread(() =>
{
// Create the current dispatcher (done via CurrentDispatcher)
var dispatcher = Dispatcher.CurrentDispatcher;
// Set the context
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(dispatcher));
MyWindow windowInAnotherThread = new MyWindow();
windowInAnotherThread.Show();
Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
This will cause it to run correctly, as the SynchronizationContext will be in place prior to the construction of the Window.
Try providing a getter and setter for your BackgroundWorker inside MyWindow. And pass BackgroundWorker object via setter method to Mywindow. That should solve the problem, i guess.
You need to use a delegate method and an invoke in the calling function. There's a good example here: http://msdn.microsoft.com/en-us/library/aa288459(v=vs.71).aspx
Using your code,
public partial class MyWindow : Window {
delegate void TitleSetter(string title);
public MyWindow() {
InitializeComponent();
var bw = new BackgroundWorker();
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.RunWorkerAsync();
}
void SetTitle(string T)
{
this.Title = T;
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
try
{
TitleSetter T = new TitleSetter(SetTitle);
invoke(T, new object[]{"Whatever the title should be"}); //This can fail horribly, need the try/catch logic.
}catch (Exception){}
}
void bw_DoWork(object sender, DoWorkEventArgs e) {
Thread.Sleep(3000);
}
}
I think simply moving your background worker thread setup code into the "Load" event instead of the constructor should be just fine.
I have a program that makes some hefty calls to the database and then updates the UI. This is causing problems because for most of the time it means that the UI in not responsive. I therefore decided that I wanted to put the function calls that access the database and update the UI in a separate thread, so now I have something like this:
private delegate void CallAsyncDelegate();
private void CallGetDBValues()
{
// Call GetDatabaseValues in new thread
CallAsyncDelegate callGetDatabaseValues = new
CallAsyncDelegate(GetDatabaseValues);
BeginInvoke(callGetDatabaseValues);
}
private void GetDatabaseValues()
{
// Get lots of data here
// Update UI here
}
...
However, it seems to make no difference whatsoever to the UI. I read somewhere that if the code to be run in a separate thread needed to update the UI then this was how the call should be made - is this correct? Am I doing something wrong?
You may be better served using the BackgroundWorker that is built-in to the .NET framework.
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.WorkerReportsProgress = true;
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// update UI with status
label1.Text = (string)e.UserState
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Check for cancel
if(e.Cancelled)
{
//Handle the cancellation.
{
//Check for error
if(e.Error)
{
//Handle the error.
}
// Update UI that data retrieval is complete
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
// Get data
//foreach to process data
//Report progress
bw.ReportProgress(n, message);
}
Here's a link to the MSDN article on how to use the BackgroundWorker for additional details. Thanks to Henk Holterman for the suggestion to include this:
http://msdn.microsoft.com/en-us/library/cc221403%28VS.95%29.aspx
In the "// Update UI here", make sure to use Control.Invoke to actually do the work -- it's imperative that the UI only be "touched" by the UI-thread, and this only happens when you use Control.Invoke.
BeginInvoke and Invoke means to run the code on the UI thread. In this case if you are calling CallGetDBValues() from the UI thread you are not going to gain anything.
Usually you will create a BackgroundWorker or background thread that will do the heavy lifting and it will Invoke back to the UI thread the values that need to be updated.
A BackgroundWorker will probably be the better solution (see Robaticus's answer), but here is a background thread version.
private delegate void CallAsyncDelegate();
private void button_Click( object sender, EventArgs e )
{
Thread thread = new Thread( GetDBValues );
thread.IsBackground = true;
thread.Start();
}
private void GetDBValues()
{
foreach( ... )
{
Invoke( new CallAsyncDelegate( UpdateUI ) );
}
}
private void UpdateUI()
{
/* Update the user interface */
}
I'm not sure of the syntax.. but the sytax I'm more familiar with is something like:
public delegate object myDelegate(object myParam);
Public class MyClass
{
public static void Main()
{
myDelegate d = new myDelegate(myMethod);
d.BeginInvoke ( new object() );
}
static void myMethod(object myParam)
{
// do some work!!
return new object);
}
}