I am using a WPF user control. I should enable the button when a response comes from a socket, as I am mostly using layoutupdate when the response comes. The thread is changing and the layout is not being updated. I tried to use focus but it gives an error:
class.button.isEnabled = true;
Error:
The calling thread cannot access this object because a different thread owns it.
I tried to use a dispatcher, but I cannot achieve success:
userControl.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => {
class.button.IsEnabled = true;
}));
Error:
The calling thread cannot access this object because a different thread owns it.
Related
I am in a similar situation as this poster (What's the best way to create a new UI thread and call back methods on the original thread?)
I have an API object which I would like to perform lengthy calculations on, however any properties or methods of this object must be accessed on the current thread (which is the UI thread) or else I get "Accessing disposed TPS.NET DataObject" exceptions
Is there an elegant way of accomplishing this using F# async workflows or will I be stuck managing thread dispatchers as in his solution.
For reference, here is his solution to the issue:
public class Plugin
{
public void Run(Context context)
{
// Get the application's UI thread dispatcher
var dispatcher = Dispatcher.CurrentDispatcher;
// Create a dispatcher frame to push later
var frame = new DispatcherFrame();
// Create a new UI thread (using an StaTaskScheduler)
Task.Factory.StartNew(async () =>
{
var window = new MyWindow();
// The Click event handler now uses the original
// thread's dispatcher to run the slow method
window.MyButton.Click += async (o, e) =>
await dispatcher.InvokeAsync(() => context.SlowMethod());
window.ShowDialog();
// When the window is closed, end the dispatcher frame
frame.Continue = false;
}, CancellationToken.None, TaskCreationOptions.None, new StaTaskScheduler(1));
// Prevent exiting this Run method until the frame is done
Dispatcher.PushFrame(frame);
}
}
Without know the exact details I would suggest having the Click handler on the main thread and do the following:
Copy any data needed off the UI into an F# record and passes this into an async workflow
Return immediately after putting the UI into a 'loading' state
The following code is untested but should put you on the right track:
//Get the context of the UI
let context = System.Threading.SynchronizationContext.Current
//Gather any needed data from the UI into immutable F# records
//Put the UI into a 'loading' state
async {
// Do work on a different thread
do! Async.Sleep 1000
let x = 1
// Switching back to the UI
do! Async.SwitchToContext context
//Update UI
return ()
}
|> Async.Start
This link should also provide some useful information http://tomasp.net/blog/async-non-blocking-gui.aspx/
EDIT:
If you need to go back and forth between the UI thread and a background thread to gather additional information in the async workflow you can make alternating calls between do! Async.SwitchToThreadPool() and do! Async.SwitchToContext context
I have the following:
public ICollectionView Children
{
get
{
// Determining if the object has children may be time-consuming because of network timeouts.
// Put that in a separate thread and only show the expander (+ sign) if and when children were found
ThreadPool.QueueUserWorkItem(delegate
{
if (_objectBase.HasChildren)
{
// We cannot add to a bound variable in a non-UI thread. Queue the add operation up in the UI dispatcher.
// Only add if count is (still!) zero.
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
if (_children.Count == 0)
{
_children.Add(DummyChild);
HasDummyChild = true;
}
}),
System.Windows.Threading.DispatcherPriority.DataBind);
}
});
return _childrenView;
}
}
It works great: HasChildren is run in a background thread which uses the dispatcher to insert its result into the variable used for the binding to the UI.
Note: _childrenView is set to this:
_childrenView = (ListCollectionView) CollectionViewSource.GetDefaultView(_children);
Problem:
If I call the Children property from another ThreadPool thread, I get a NotSupportedException in the line
_children.Add(DummyChild);
Exception text: "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread."
Why? I have verified that that code is executed from the Dispatcher thread.
We've run into this problem before ourselves. The issue is twofold:
1- Make sure that any changes to the SourceCollection are on the main thread (you've done that).
2- Make sure that the creation of the CollectionView was also on the main thread (if it were created on a different thread, say in response to an event handler, this will not usually be the case). The CollectionView expects modifications to be on "its" thread, AND that "its" thread is the "UI" thread.
i have a problem whenever i Refresh the prograss bar i get the error The calling thread cannot access this object because a different thread owns it
how can i remove it
shashank
backgroundWorker12 = new BackgroundWorker();
timer1.Enabled = true;
//cancel any async processes running for the background worker
//backgroundWorker1.CancelAsync();
backgroundWorker12.DoWork += (s, args) =>
{
BackgroundWorker worker2 = s as BackgroundWorker;
worker2.WorkerReportsProgress = true;
float percentageDone = 20f;
//check if the user status and update the password in xml
CheckUseridPwd();
//call the function to sync the wall chart data
//call the function to sync event relate data
percentageDone = 100f;
ValidateLogin2(txtUserID.Text.Trim(), txtPassword.Password.Trim(), -1);
worker2.ReportProgress((int)percentageDone);
};`
This bit looks like it's using UI controls from the wrong thread:
ValidateLogin2(txtUserID.Text.Trim(), txtPassword.Password.Trim(), -1);
I suggest you capture the user and password in local string variables above the code which adds the event handler - you can use those captured variables within your delegate. That way everything should be on the right thread:
backgroundWorker12 = new BackgroundWorker();
timer1.Enabled = true;
string user = txtUserID.Text.Trim();
string password = txtPassword.Password.Trim();
backgroundWorker12.DoWork += (s, args) =>
{
// ... same code as before up to here
ValidateLogin2(user, password, -1);
worker2.ReportProgress((int)percentageDone);
};
See if you can use the RunWorkerCompleted event of the BackgroundWorker, since you're accessing the UI only after progress is 100% i.e. done..
Then you wouldn't have to worry about thread-affinity of WPF UI controls - since the event handler is invoked again on the right/ UI Thread.
The other option (if you need to access the UI controls before the work is complete) is to cache the object
returned by Dispatcher.CurrentDispatcher on the UI Thread before the work starts and then use object.Invoke to marshal to the right thread from the thread pool thread that is executing your DoWork handler. See some code here.
Have you tried invoking ValidateLogin2
you can either do it directly from your code shown, or in ValidateLogin2 check if the method itself requires invoking. If not, go ahead and validate, but if it does, then have it invoke itself
void ValidateLogin2(...)
{
if (this.InvokeRequired)
{
//Invokes itself if required
BeginInvoke(new MethodInvoker(delegate(){ValidateLogin2(...);}));
}
else
{
//validate login here
}
}
I am trying to reuse some .NET code that performs some calls to a data-access-layer type service. I have managed to package up both the input to the method and the output from the method, but unfortunately the service is called from inside code that I really don't want to rewrite in order to be asynchronous.
Unfortunately, the webservice code generated in Silverlight only produces asynchronous methods, so I was wondering if anyone had working code that managed to work around this?
Note: I don't need to execute the main code path here on the UI thread, but the code in question will expect that calls it makes to the data access layers are synchronous in nature, but the entire job can be mainly executing on a background thread.
I tried the recipe found here: The Easy Way To Synchronously Call WCF Services In Silverlight, but unfortunately it times out and never completes the call.
Or rather, what seems to happen is that the completed event handler is called, but only after the method returns. I am suspecting that the event handler is called from a dispatcher or similar, and since I'm blocking the main thread here, it never completes until the code is actually back into the GUI loop.
Or something like that.
Here's my own version that I wrote before I found the above recipe, but it suffers from the same problem:
public static object ExecuteRequestOnServer(Type dalInterfaceType, string methodName, object[] arguments)
{
string securityToken = "DUMMYTOKEN";
string input = "DUMMYINPUT";
object result = null;
Exception resultException = null;
object evtLock = new object();
var evt = new System.Threading.ManualResetEvent(false);
try
{
var client = new MinGatServices.DataAccessLayerServiceSoapClient();
client.ExecuteRequestCompleted += (s, e) =>
{
resultException = e.Error;
result = e.Result;
lock (evtLock)
{
if (evt != null)
evt.Set();
}
};
client.ExecuteRequestAsync(securityToken, input);
try
{
var didComplete = evt.WaitOne(10000);
if (!didComplete)
throw new TimeoutException("A data access layer web service request timed out (" + dalInterfaceType.Name + "." + methodName + ")");
}
finally
{
client.CloseAsync();
}
}
finally
{
lock (evtLock)
{
evt.Close();
evt = null;
}
}
if (resultException != null)
throw resultException;
else
return result;
}
Basically, both recipes does this:
Set up a ManualResetEvent
Hook into the Completed event
The event handler grabs the result from the service call, and signals the event
The main thread now starts the web service call asynchronously
It then waits for the event to become signalled
However, the event handler is not called until the method above has returned, hence my code that checks for evt != null and such, to avoid TargetInvocationException from killing my program after the method has timed out.
Does anyone know:
... if it is possible at all in Silverlight 3
... what I have done wrong above?
I suspect that the MinGatServices thingy is trying to be helpful by ensuring the ExecuteRequestCompleted is dispatched on the main UI thread.
I also suspect that your code is already executing on the main UI thread which you have blocked. Never block the UI thread in Silverlight, if you need to block the UI use something like the BusyIndicator control.
The knee-jerk answer is "code asynchronously" but that doesn't satisfy your question's requirement.
One possible solution that may be less troublesome is to start the whole chunk of code from whatever user action invokes it on a different thread, using say the BackgroundWorker.
Of course the MinGatServices might be ensuring the callback occurs on the same thread that executed ExecuteRequestAsync in which case you'll need to get that to run on a different thread (jumping back to the UI thread would be acceptable):-
Deployment.Current.Dispatcher.BeginInvoke(() => client.ExecuteRequestAsync(securityToken, input));
I get the following error
Cross-thread operation not valid:
Control accessed from a thread other
than the thread it was created on.
This is a callback from a wcf.
I have a textBox and I need to update the value and appendtext to it.
This value is coming back from another thread and updates the UI.
public CarStatus CarState
{
get
{
return _carState;
}
set
{
_carState;= value;
CarStatus tmpCarState;=null;
if (txtResult.InvokeRequired)
{
txtResult.Invoke(new MethodInvoker(() => { tmpCarState;=null;= _carState;}));
}
txtResult.AppendText(string.Format("Car status is: {0}{1}", tmpCarState, Environment.NewLine));
}
the following crashes!!
You forgot the else, as you're updating the text of the control via AppendText always, not just on non-invoke required.
And, well, I think you've got something wrong here: You're setting member variables through the invoker, but changing the actual WinForm component on any thread? You probably just want to but the whole block on Invoke.