BackgroundWorker and Clipboard - c#

I'm going crazy with a simple code in which I use a BackgroundWorker to automate the basic operations. Should I add a content to the clipboard.
After executing this code in the method of the BackgroundWorker:
Clipboard.SetText (splitpermutation [i]);
I get an error that explains the thread must be STA, but I do not understand how to do.
Here more code: (not all)
private readonly BackgroundWorker worker = new BackgroundWorker();
private void btnAvvia_Click(object sender, RoutedEventArgs e)
{
count = lstview.Items.Count;
startY = Convert.ToInt32(txtY.Text);
startX = Convert.ToInt32(txtX.Text);
finalY = Convert.ToInt32(txtFinalPositionY.Text);
finalX = Convert.ToInt32(txtFinalPositionX.Text);
incremento = Convert.ToInt32(txtIncremento.Text);
pausa = Convert.ToInt32(txtPausa.Text);
worker.WorkerSupportsCancellation = true;
worker.RunWorkerAsync();
[...]
}
private void WorkFunction(object sender, DoWorkEventArgs e)
{
[...]
if (worker.CancellationPending)
{
e.Cancel = true;
break;
}
else
{
[...]
Clipboard.SetText(splitpermutation[i]);
[...]
}
}

You could marshal this to the UI thread to make it work:
else
{
[...]
this.Dispatcher.BeginInvoke(new Action(() => Clipboard.SetText(splitpermutation[i])));
[...]
}

The BackgroundWorker runs on the .NET thread pool. Thread pool threads run in the COM multi-threaded apartment. To use the clipboard, you must be running in a single-threaded apartment. You could create your own thread and set it to run in an STA, but it would probably be best to use Control.Invoke (or BeginInvoke) to get back onto a user-interface thread (which must be an STA thread).

The exception you're getting is because you're trying to do something on the UI thread from outside the UI thread (a BackgroundWorker, as the name implies, does something in the background, and for that it needs to create a separate thread).
While the answer posted by Reed (that is, by using Dispatcher.BeginInvoke) is one way to do avoid this exception, I'm wondering WHY you are trying to send something to the clipboard from the main work method in the first place...
The BackgroundWorker exposes events like ProgressChanged (which you can call periodically from your work method) or RunWorkerCompleted (which will fire when the main work method finishes).
Using Clipboard.SetText in either of these events should not cause the exception you're seeing, and this seems to be the preferable way of doing things on the UI thread when working with the BackgroundWorker.

Related

Display "Busy Indicator" using background thread

I am doing some heavy computations on the main thread and these computations cannot run on a separate thread.
I want to display a "Busy Indicator" (i.e., spinning widget) on the application UI when these computations are running. As such, I cannot show the busy indicator on main thread as the UI is locked while these computations are running.
To work around this issue, I tried to move the busy indicator to separate thread. With the help of this post I am able to place the busy indicator on separate thread. However, I am not able to communicate with this thread to start or stop the busy indicator.
private HostVisual CreateBusyIndicatorOnWorkerThread()
{
// Create the HostVisual that will "contain" the VisualTarget
// on the worker thread.
HostVisual hostVisual = new HostVisual();
Thread thread = new Thread(new ParameterizedThreadStart(BusyIndicatorWorkerThread));
thread.ApartmentState = ApartmentState.STA;
thread.IsBackground = true;
thread.Start(hostVisual);
// Wait for the worker thread to spin up and create the VisualTarget.
s_event.WaitOne();
return hostVisual;
}
private static AutoResetEvent s_event = new AutoResetEvent(false);
private void BusyIndicatorWorkerThread(object arg)
{
// Create the VisualTargetPresentationSource and then signal the
// calling thread, so that it can continue without waiting for us.
HostVisual hostVisual = (HostVisual)arg;
VisualTargetPresentationSource visualTargetPS = new VisualTargetPresentationSource(hostVisual);
s_event.Set();
// Create a MediaElement and use it as the root visual for the
// VisualTarget.
visualTargetPS.RootVisual = CreateBusyIndicator();
// Run a dispatcher for this worker thread. This is the central
// processing loop for WPF.
System.Windows.Threading.Dispatcher.Run();
}
private FrameworkElement CreateBusyIndicator()
{
var busyIndicator = new MyBusyIndicator();
//busyIndicator.DataContext = this.
Binding myBinding = new Binding("IsBusy");
myBinding.Source = this;
busyIndicator.SetBinding(MyBusyIndicator.IsBusyProperty, myBinding);
}
I always gets an exception "The calling thread cannot access this object because a different thread owns it". This is because I am trying to update the busy indicator from the main thread while the busy indicator is owned by a different thread.
I have also tried an approach given in this article,
private void CreateAndShowContent()
{
Dispatcher = Dispatcher.CurrentDispatcher;
VisualTargetPresentationSource source =
new VisualTargetPresentationSource(_hostVisual);
_sync.Set();
source.RootVisual = _createContent();
DesiredSize = source.DesiredSize;
_invalidateMeasure();
Dispatcher.Run();
source.Dispose();
}
But with this approach Dispatcher.Run() nothing happens until after the completion of the computations and then the busy indicator is displayed.
I want to communicate from main thread to the thread having busy indicator. Does anyone have an approach?
There is no reason to run "heavy computations" in UI thread. Even more - this is a bad practice. Instead use BackgroundWorker which will do work, meantime alive UI-thread will show Loading/Calculating:
var worker = new BackgroundWorker();
worker.DoWork += (s, e) => {
// This part will last at a separate thread without blocking UI.
// Excellent place for heavy computations.
}
worker.RunWorkerCompleted += (s, e) => {
// Here we're back to UI thread - so you can change states and stop animations.
}
// And finally start async computation
worker.RunWorkerAsync();
UI should contain BusyIndicator control which will be activated/stopped when you'll start/finish worker.
Please stop what you are doing... it is totally incorrect. #Anatolii Gabuza was correct... you shouldn't do any long running process using the UI thread as this will block it, making the application unusable at these times. Unless your long running process is rendering UI objects, then there really is no reason to do it using the UI thread... let us know what it is and we can help you to run it on a background thread correctly.
So you discovered that you can't display your busy indicator on the UI thread because it is busy with your long running process... at this point, most developers would realise their error, but unfortunately, not you. Instead of accepting that the long running process should be run on a background thread, you do the exact opposite and now want to display some UI element in a background thread, while blocking the UI thread with a long running process???
This is utter madness, if you want to avoid some horrendous problems, please stop. If you continue, then you'd better get used to seeing that exception:
The calling thread cannot access this object because a different thread owns it.
You need to invoke it busyContainer dispatcher. use as below
this.busyContainer.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() =>
{
//update busy Container
}));
I got this from SO but cannot find it on SO
Run this on the UI thread and put in your task where very long task is
public class WaitCursor : IDisposable
{
private Cursor _previousCursor;
public WaitCursor()
{
_previousCursor = Mouse.OverrideCursor;
Mouse.OverrideCursor = Cursors.Wait;
}
#region IDisposable Members
public void Dispose()
{
Mouse.OverrideCursor = _previousCursor;
}
#endregion
}
using (new WaitCursor())
{
// very long task
}

Cross thread exception

I am having a problem for a while
this line:
txtPastes.Text = (string)e.UserState;
throws a cross thread exception and I didn't find any solution
txtPastes - textbox
GuiUpdate - BackgroundWorker
lstAllPastes - list of string
private void GuiUpdate_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
while (lstAllPastes.Count == 0) ;
for (int i = 0; i < lstAllPastes[0].Length; i++)
{
GuiUpdate.ReportProgress(0, lstAllPastes[0].Substring(0, i));
Thread.Sleep(1);
}
lstAllPastes.RemoveAt(0);
}
}
private void GuiUpdate_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
txtPastes.Text = (string)e.UserState;
}
private void GuiUpdate_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
You cannot update a UI control from any thread other than the UI thread. Typically, the BackgroundWorker would take care of raising its ProgressChanged and RunWorkerCompleted events correctly on the UI thread. Since that doesn’t appear to be the case here, you could marshal your UI-updating logic to the UI thread yourself by using the Invoke method:
txtPastes.Invoke(new Action(() =>
{
// This code is executed on the UI thread.
txtPastes.Text = (string)e.UserState;
}));
If you’re on WPF, you would need to call Invoke on the control’s dispatcher:
txtPastes.Dispatcher.Invoke(new Action(() =>
{
txtPastes.Text = (string)e.UserState;
}));
Update: As Thomas Levesque and Hans Passant have mentioned, you should investigate the reason why your ProgressChanged event is not being raised on the UI thread. My suspicion is that you’re starting the BackgroundWorker too early in the application initialization lifecycle, which could lead to race conditions and possibly a NullReferenceException if the first ProgressChanged event is raised before your txtPastes textbox has been initialized.
Well, this is supposed to work of course. The cause of this exception is not visible in your snippet. What matters is exactly where and when the BackgroundWorker is started. Its RunWorkerAsync() method uses the SynchronizationContext.Current property to figure out what thread needs to execute the ProgressChanged event handler.
This can go wrong when:
You started the BGW too early, before the Application.Run() call. Winforms or WPF won't yet have had a chance to install its own synchronization provider.
You called the BGW's RunWorkerAsync() method in a worker thread. Only marshaling to the UI thread is supported, the message loop is the crucial ingredient to make running code on another thread work.
The form that has txtPastes control was created on another thread. With the BGW started on the UI thread that's still a thread mismatch
The form's Show() method was called on another thread. Which creates the native Windows window on the wrong thread.
Make sure you start the BackgroundWorker from the UI thread; if you do that, the ProgressChanged event will be raised on that thread, and the exception won't happen.
If you want to update yout GUI for example TextBox, you should read this article:
Update GUI from another thread

How to use BeginInvoke method correctly?

I have got this code. It works but it freezes the UI.
What I want to know is how to use WPF BeginInvok method corectly?
private void ValidateAuthURL_Click(object sender, RoutedEventArgs e)
{
((Button)sender).Dispatcher.BeginInvoke(DispatcherPriority.Input,
new ThreadStart(() =>
{
bool result = false;
try
{
Your delegate is going to be executed in the UI thread. That's what Dispatcher.BeginInvoke is there for. I assume you really want to execute that delegate in a background thread... then you should use Dispatcher.BeginInvoke to get back to the UI thread in order to update the UI later.
In terms of getting to a background thread, you could:
Use the thread pool directly (ThreadPool.QueueUserWorkItem)
Use BackgroundWorker
Start a new thread
Use Task.Factory.StartNew (if you're using .NET 4)

unable to update progress bar with threading in C#

private void button1_Click(object sender, EventArgs e)
{
PROGRESS_BAR.Minimum = 0;
PROGRESS_BAR.Maximum = 100;
PROGRESS_BAR.Value = 0;
for (int i = 0; i < 100; i++)
{
Thread t = new Thread(new ThreadStart(updateProgressBar));
t.IsBackground = true;
t.Start();
}
}
private void updateProgressBar()
{
PROGRESS_BAR.PerformStep();
Thread.Sleep(4000);
}
I always get this error:
Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.
I tried to search in google for solutions and unfortunately all of them didn't work for me. does any one know how to solve this?
thanks in advance..
You cannot interact with UI elements from non-UI thread.
You need to use code like
this.BeginInvoke((Action)(() => PROGRESS_BAR.PerformStep()));
You should use the BackgroundWorker component and its ProgressChanged event.
You can call the ReportProgress method inside the DoWork handler (which runs on the background thread), then update the progress bar in the ProgressChanged handler (which runs on the UI thread).
If you really want to do it yourself (without a BackgroundWorker), you can call BeginInvoke
All of your UI interaction, including callbacks, property sets, and method calls, must be on the same thread.
One of those callbacks can start another thread (or many threads) but they cannot directly update the UI. The way to handle the updates are through data properties. My processing thread would update a progress status property. This is throne read by the UI thread which has a timer for regular (100ms) updates of the progress bar.
If you do this, you will need a lock on any objects which are used to communicate e status updates (eg. Strings).

c# progressbar not updating

I have a ProgressBarWindow which has a progressbar and a cancel button on it which I use to report progress on file I/O. However, the UI Thread of the ProgressBarWindow and my main window both hang despite all the work being done in a backgroundworker. The progressbar is rendered, as is my main window, but does not update whilst the backgroundworker does its thing.
The following code is called at the very end of the constructor of the main window:
iCountLogLinesProgressBar = new ProgressBarWindow();
iCountLogLinesProgressBar.cancelButton.Click += EventCountLogLinesProgressBarCancelButtonClicked;
iCountLogLinesProgressBar.Show();
iCountLogRecords = new BackgroundWorker();
iCountLogRecords.DoWork += EventCountLogLinesDoWork;
iCountLogRecords.ProgressChanged += EventCountLogLinesProgressChanged;
iCountLogRecords.RunWorkerCompleted += EventCountLogLinesRunWorkerCompleted;
iCountLogRecords.WorkerReportsProgress = true;
iCountLogRecords.WorkerSupportsCancellation = true;
iCountLogRecords.RunWorkerAsync(new BinaryReader(File.Open(iMainLogFilename, FileMode.Open, FileAccess.Read)));
EventCountLogLinesProgressChanged() looks like this:
private void EventCountLogLinesProgressChanged(object sender, ProgressChangedEventArgs e)
{
iCountLogLinesProgressBar.Value = e.ProgressPercentage;
}
Here is a shortened version of ProgressBarWindow(the rest is just a couple of setters):
public partial class ProgressBarWindow : Window
{
public ProgressBarWindow()
{
InitializeComponent();
this.progressBar.Value = this.progressBar.Minimum = 0;
this.progressBar.Maximum = 100;
}
public double Value
{
get
{
return progressBar.Value;
}
set
{
this.progressBar.Value = value;
}
}
}
I've tried wrapping the value setter line in a dispatcher.invoke delegate but that gives me a stack overflow(I shouldn't have to have a dispatcher.invoke line anyway as backgroundworker calls ProgressChanged in the UI Thread, right?). I have checked msdn and googled but I can't seem to find anyone else with this problem.
EDIT Apologies, I did not realise my simplified code blocked the UI Thread, I get exactly the same behaviour despite using a backgroundworker so I erroneously assumed they were equivalent. I should have mentioned I was using a backgroundworker :P
You're blocking the UI thread, so it won't re-render the UI until your loop has finished.
Move the background processing into a separate thread, and use appropriate Dispatcher calls (or BackgroundWorker) to marshal UI update calls back to the UI thread.
If your progress bar is really just meant to be a timer, you could use one of the Timer classes to update it.
EDIT: Okay, now you've changed the code, it looks okay to me. It should be thread-safe as you're only changing the UI on the UI thread. Is your background worker definitely reporting progress periodically?
Listen to Jon Skeet,
or you could sometimes call Application.DoEvents() to make all UI updates in the same thread.

Categories

Resources