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.
Related
why I can't close this from it gave me an error message (Cross-thread operation not valid: Control 'Form4' accessed from a thread other than the thread it was created on)
my form code;
System.Timers.Timer t = new System.Timers.Timer();
private void Form4_Load(object sender, EventArgs e)
{ myFunction2();}
private void myFunction2()
{
t.Interval = int.Parse(textBox1.Text);
t.Elapsed += T_Elapsed;
t.Start();
t.AutoReset = false;
}
private void T_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
myFunction();
t.Stop();
t.Enabled = false;
this.Close();
}
private void myFunction()
{
var form6 = new Form6();
//form6.Closed += (s, args) => this.Close();
form6.ShowDialog();}
Edit
I get help from a friend to change this in my code but still, the from4 open and form6 open much time.
private System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
private void myFunction2()
{
t.Interval = int.Parse(textBox1.Text);
t.Tick += T_Elapsed;
t.Start();
}
private void T_Elapsed(object sender, EventArgs e)
{
myFunction();
this.Invoke((new Action(() =>
this.Close();
})));
}
private void myFunction()
{
Form6 form6 = new Form6();
form6.ShowDialog();}
Winforms has an "owning-thread" model.
What does that mean?
This model prevents you from accessing an UI component from another thread, not the one which created it.
Why?
Because GUI components are not thread-safe. and should not be, since they'll be much slower. So, WinForms throws an exception like that at you when you try to access a GUI component from another thread - not the owned thread.
But why does this happen toyou?
Because System.Timers.Timer executes its callback in its own thread, which isn't the thread that created the GUI (the main thread of the app). So, you can't access from its callback to any GUI component.
What's the solution?
You can access an GUI component from another thread by a tool called a Dispatcher. But if all you want is a simple timer, you have nicer solution.
Simply use System.Windows.Forms.Timer instead of System.Timers.Timer. This timer is specific to WinForms, and handles all the black work with the dispatcher for you. (Note: WPF has System.Windows.Threading.DispatcherTimer for the same purpose).
But, there's one pitfall: this timer has not AutoReset property. So, you should remove the event by hand after one run, like:
private void T_Elapsed(object sender, EventArgs e)
{
myFunction();
t.Stop();
this.Close();
}
Since you're closing the window, this is not really needed, but for safety...
Also, note that you don't need both Stop() and Enabled = false together, they are identical (I personally prefer Stop(), I think it's more readable).
In your example (with AutoReset) you didn't need Stop() at all - AutoReset = false run the callback only one time.
Edit:
Although it isn't needed in your case, I append an explanation about "how to use the dispatcher".
Each WinForms' form has a Dispatcher, and some methods related to it. The most important are Invoke() and BeginInvoke() (two overloaded versions, I'm talking about the first which takes System.Delegate).
These methods enable you two access GUI components from not-owning thread, only from the method passed as parameter (in most cases, you must cast it to System.Delegate first).
The difference is, that Invoke() returns only after the method called, while BeginInvoke() is asynchronous; it returns immediately.
So, you can rewrite you code as follows:
private System.Timers.Timer t = new System.Timers.Timer();
public Form1()
{
InitializeComponent();
t.Elapsed += T_Elapsed;
t.Interval = int.Parse(textBox1.Text);
t.AutoReset = false;
t.Start();
}
private void T_Elapsed(object sender, EventArgs e)
{
this.Invoke((Action)(() => // You can use `BeginInvoke()` as well
{
this.Close();
}));
// Or
// this.Invoke(new Action(() => // You can use `BeginInvoke()` as well
// {
// this.Close();
// }));
}
Note: Never put long-running tasks inside Invoke() or BeginInvoke()! since they're executed in the owning thread - not in the called thread, they'll freeze the GUI - it's much easier to not use threads at all... Put the calculations in the thread, and call these methods only to update the GUI!
Edit 2:
After I saw what you did with my answer, I was shocked... It seems you even didn't read it! You chose both the solutions: the winforms timer (the good), and the dispatcher (the bas, in this case)! simplify you Tick event so:
private void T_Elapsed(object sender, EventArgs e)
{
myFunction();
Close();
}
Also, in your myFunction(), you show your second form in modal form. That say, that the method won't return after the second form is closed. See What's the difference between Show(), ShowDialog() and Application.Run() functions? for more details. I think you want to show your second form modeless.
private System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
private void T_Elapsed(object sender, EventArgs e)
{
if (true)
{
myFunction();
t.Enabled = false;
t.Stop();
}
}
private void myFunction2()
{
t.Interval = int.Parse(textBox1.Text);
t.Tick += T_Elapsed;
t.Start();
}
private void myFunction()
{
t.Enabled = false;
t.Stop();
this.Hide();
Form6 form6 = new Form6();
form6.ShowDialog();}
In my c# form application i use BackgroundWorker but couldnt manage to change label1 field. Can anyone help me on that?
Thanks.
private void goButton_Click(object sender, EventArgs e)
{
_worker = new BackgroundWorker();
_worker.WorkerSupportsCancellation = true;
_worker.DoWork += new DoWorkEventHandler((state, args) =>
{
do
{
if (_worker.CancellationPending)
break;
setlabel();
} while (true);
});
_worker.RunWorkerAsync();
goButton.Enabled = false;
stopButton.Enabled = true;
}
private void setlabel()
{
label1.Text = "test";
}
You cannot manipulate controls that were created on the UI thread from a background thread. To accomplish that you can use the BeginInvoke method:
if (_worker.CancellationPending)
break;
this.BeginInvoke(new Action(setlabel));
The method:
Executes the specified delegate asynchronously on the thread that the control's underlying handle was created on.
You can't update UI elements not from UI thread (in your case within DoWork function).
You can use BackgroundWorker's ProgressChangedEventHandler to modify UI state within it's callback.
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.
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";
}
}
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);
}
}