MethodInfo mi = typeof(NotifyIcon).GetMethod("ShowContextMenu", BindingFlags.Instance | BindingFlags.NonPublic);
mi.Invoke(notify, null);
This throws the following exception:
{"Exception has been thrown by the target of an invocation."}
With the following inner exception:
"Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on."
If I comment out a line of code that sets the images for the context menu entries then it stops throwing the exception.
Any ideas?
You may be confusing the Invoke method of MethodInfo, which just invokes the delegate on the current thread, with Control.Invoke which invokes a delegate on the UI thread.
You get this exception if you try to access a UI element from a thread other than the correct UI thread.
Basically you need to execute this code on the UI thread instead.
Is there any reason why you're trying to invoke ShowContextMenu via reflection instead of directly? You may just need something like (assuming C# 3):
MethodInvoker action = () => notify.ShowContextMenu();
someControl.Invoke(action);
You are updating UI controls on a thread other than the thread that created them. That is not allowed.
In regular code you can use the Control.InvokeRequired property.
Assuming that you have a form with two buttons, and two labels here is how to do updates from another thread:
private void button1_Click(object sender, EventArgs e)
{
//force execution on another thread
new Thread(updateLabelThreaded).Start();
}
private void button2_Click(object sender, EventArgs e)
{
//force execution on another thread
new Thread(updateLabelReflect).Start();
}
private void updateLabelThreaded()
{
if (!label1.InvokeRequired)
{
//if we are on the correct thread, do a trivial update
label1.Text = "something";
}
else
{
//else invoke the same method, on the UI thread
Invoke(new Action(updateLabelThreaded), null);
}
}
private void updateLabelReflect()
{
Control ctrl = label2;
PropertyInfo pi = typeof (Label).GetProperty("InvokeRequired");
bool shouldInvoke = (bool) pi.GetValue(ctrl, null);
if (!shouldInvoke)
{
//if we are on the correct thread, reflect whatever is neccesary - business as usual
PropertyInfo txtProp = typeof (Label).GetProperty("Text");
txtProp.SetValue(ctrl, "Something 2", null);
}
else
{
//else invoke the same method, on the UI thread
Invoke(new Action(updateLabelReflect), null);
}
}
You can't call UI methods from a non-UI thread. I recommend using TaskScheduler.FromCurrentSynchronizationContext to marshal the call to the UI thread.
Related
I have an application which fetches data form the server and gives the result to the user .
The data being fetched is quite large which blocks the UI for sometime. I am using dispatcher to make it asynchronous.
Here is the code snippet :-
private void GetData(object sender, RoutedEventArgs e)
{
List<result> data=new List<result>;
DispatcherObject obj= Dispatcher.BeginInvoke(DispatcherPriority.Background,(ThreadStart)delegate
{
data =Data.fetch_data(id, name, url);
});
if(obj.Completed){
MessageBox.Show("Done!");
}
}
But the code gives an error saying
"Cannot implicitly convert type
'System.Windows.Threading.DispatcherOperation' to
'System.Windows.Threading.DispatcherObject' ".
Is there anyway where user can notified when the background task gets completed?
Edit :-
Here is the async/await code
private async void GetData(object sender, RoutedEventArgs e)
{
Task<List<result>> T =Task<List<resultsummary>>.Factory.StartNew(() => data==Data.fetch_data(id, name, url));
await T;
}
But it gives an error "" The calling thread cannot access this object because a different thread owns it."
Calling Dispatcher.BeginInvoke does not enable you to perform asynchronous actions on a background thread unless you initialised the Dispatcher object on a background thread. So, instead of using a Dispatcher to do what you want, in order to fetch data on a background thread and then pass that data from the background thread to the UI thread, you can use the Task class.
Task.Factory.StartNew((Func<YourDataType>)delegate()
{
// Fetch data on background thread here (return YourDataType, whatever that is)
return DataAccessClass.GetData();
}).ContinueWith((Task<YourDataType> task) =>
{
// Update controls with result on UI thread here
YourUiProperty = task.Result;
}, TaskScheduler.FromCurrentSynchronizationContext());
Obviously, you'll need to replace the YourDataType type with whatever your data type is.
With .Net 4.5 and VS2013 i would recommend the Usage of async and await.
By executing the fetch_data operation on the Dispatcher you are explicitly blocking the UI Thread with your fetch Operation.
private async void GetData(object sender, RoutedEventArgs e)
{
// fetch the data in a Background Task and release the UI Thread.
List<result> data = await Task.Run<List<result>>(() => Data.fetch_data(id, name, url));
// when completed return to the UI Thread.
this.MyListBox.ItemsSource = data;
}
Even better would be to build an async Version of your fetch_data Operation that Returns a Task<IEnumerable<result>>
i use background worker to call method but in return give me an Exception here is the code
private void Button_Click(object sender, RoutedEventArgs e)
{
BGW.RunWorkerAsync();
}
void BGW_DoWork(object sender, DoWorkEventArgs e)
{
e.Result = new Drug_Class().Search(Filter);
}
class Drug_Class
{
public List<WorkSpaceVariable.Drug_List> Search(Expression<Func<db.Drug_Catalog, bool>> Filter)
{
using (db.PVDBDataContext PVDB=new PVDBDataContext ())
{
try
{
var DQuary = from D in PVDB.Drug_Catalogs.Where(Filter)
select new WorkSpaceVariable.Drug_List
{
Drugs_ID = D.Drugs_ID
}
return DQuary.ToList() ;//Exception The calling thread cannot access this object because a different thread owns it.
}
catch (Exception E)
{
return null;
}
}
}
i don't understand why i got this Exception may any one tell me whats wrong with my code ?
The Exception that you got really explains your problem... all you need to do is to read it:
The calling thread cannot access this object because a different thread owns it.
You should hopefully know that a BackgroundWorker works on a background thread, so you should be able to work out that the two threads mentioned would be this background thread and the main UI thread.
What it means is that you cannot manipulate UI objects that were declared on (and therefore owned by) the main UI thread on any other thread.
The solution is to not manipulate UI objects from the UI thread in your background thread. Instead, just manipulate the data in the background thread and when the BackgroundWorker has finished, then update the UI element by updating the data bound collection.
I searched and got that Dispatcher CheckAccess can be used in place of InvokeRequired in wpf.
This is the code want to convert in wpf
private void ChangeTextBox(string txt)
{
if (msg_log.InvokeRequired)
{
Invoke(new UpdateText(ChangeTextBox), new object[] { txt });
}
else
{
msg_log.Text += txt + "\r\n";
}
}
I tried out this ---->
private void ChangeTextBox(string txt)
{
if (msg_log.Dispatcher.CheckAccess())
{
Dispatcher.Invoke(new UpdateText(ChangeTextBox), new object[] { txt });
}
else
{
msg_log.Text += txt + "\r\n";
}
}
But while running i am getting Error [InvalidOperationException] "The calling thread cannot access this object because a different thread owns it."
What i am doing wrong ? Please Help ?
Your problem is not because of the CheckAccess method... it is fine to use that to check whether the call to Invoke is required or not. When you call the Dispatcher, it is important to ensure that you are calling the correct instance of the Dispatcher class. From the Dispatcher Class page on MSDN:
In WPF, a DispatcherObject can only be accessed by the Dispatcher it is associated with. For example, a background thread cannot update the contents of a Button that is associated with the Dispatcher on the UI thread. In order for the background thread to access the Content property of the Button, the background thread must delegate the work to the Dispatcher associated with the UI thread. This is accomplished by using either Invoke or BeginInvoke. Invoke is synchronous and BeginInvoke is asynchronous.
So in your case, if you can access the correct Dispatcher instance using the following:
msg_log.Dispatcher.CheckAccess()
Then as #SriramSakthivel mentioned in the comments, you should access the same instance when calling Invoke:
msg_log.Dispatcher.Invoke(new UpdateText(ChangeTextBox), new object[] { txt });
OP problem solved but just for record a useful helper for dispatcher check is:
public void DispatchIfNecessary(Action action) {
if (!Dispatcher.CheckAccess())
Dispatcher.Invoke(action);
else
action.Invoke();
}
which can then be called as:
DispatchIfNecessary(() => { myUIcontrol.Update(...); });
How can I cancel a backgroundworker and pass back an error message. I know that you can use DoWorkEventArgs e.Results to pass back results to main thread but e.Results gets overwritten when I cancel the child thread. Example:
private MyProgram_DoWork(object sender, DoWorkEventArgs e)
{
e.Cancel = true;
e.Result = "my error message";
return;
}
private void MyProgram_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
string ErrorMsg = (string)e.Result; //exception happens here
....
}
else
{
// success code
}
}
Is there another way to stop my child thread and send a string back to the main thread?
If your long-running process was canceled, it wouldn't really have a "result", as the process didn't completely finish.
According to the documentation:
Your RunWorkerCompleted event handler should always check the Error and Cancelled properties before accessing the Result property. If an exception was raised or if the operation was canceled, accessing the Result property raises an exception.
I took a peek inside BackgroundWorker. Here's the contents of the Result property:
public object Result
{
get
{
this.RaiseExceptionIfNecessary();
return this.result;
}
}
And the contents of RaiseExceptionIfNecessary():
protected void RaiseExceptionIfNecessary()
{
if (this.Error != null)
throw new TargetInvocationException(SR.GetString("Async_ExceptionOccurred"), this.Error);
if (this.Cancelled)
throw new InvalidOperationException(SR.GetString("Async_OperationCancelled"));
}
So if you cancel the thread, referencing Result will throw an InvalidOperationException. That's just the way it's designed.
I don't know what the "best" way is to pass a string back. I'd say you could define a variable in the same method you run the BackgroundWorker from, and assign a value to it from the DoWork event.
You just have to be very careful that nothing on the UI thread is somehow bound to the variable or you could run into problems. A string should be safe, but don't start adding to a list that's bound to a ComboBox or something.
I have a worker thread that needs to add items to a BindingList. However, the BindingList is databound to a DataGridView. So, when I try to add to the list, I get an InvalidOperationException (Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on.)
Normally for this exception you would do:
if(winformControl.InvokeRequired) {
winformControl.Invoke(MethodDelegate);
}
However, the databinding confuses things, as there is no Winform control in sight. All I have is the following line, which throws the exception:
ClassInstance.MyBindingList.Add(myObject);
If you have a solution specifically for this scenario, great.
If not, how can I get the worker thread to tell my main thread to perform a particular method (with several parameters supplied by the worker thread)? This may be a preferable option, since my worker thread is actually doing a bunch of stuff at the moment (like writing to the database), and I'm not sure if everything is thread-safe. I'm a student, and new to multithreading, and it really is not my forte yet.
One option here is to tell BindingList<T> to use the sync-context, like this - however, this is arguably not the best approach. I wonder if you could expose your data via an event or similar (rather than adding to the list directly) - then have your UI handle the event by sending to the right thread and adding to the UI model.
In your worker class constructor, try this:
private System.Threading.SynchronizationContext mContext = null;
/// <summary>
/// Constructor for MyBackgroundWorkerClass
/// </summary>
public MyBackgroundWorkerClass(System.Threading.SynchronizationContext context)
{
mContext = context;
}
Then, when you need to invoke something on the UI thread:
private void CallOnTheUiThread(object dataToPassToUiThread)
{
// Make sure the code is run on the provided thread context.
// Make the calling thread wait for completion by calling Send, not Post.
mContext.Send(state =>
{
// Change your UI here using dataToPassToUiThread.
// Since this class is not on a form, you probably would
// raise an event with the data.
}
), null);
}
When creating your worker class from a form on the UI thread, this is what you would pass as the synchronization context.
private void Form1_Load(object sender, EventArgs e)
{
var worker = new MyBackgroundWorkerClass(SynchronizationContext.Current);
}
You can fire an event to the main, UI, thread and there have:
if (this.InvokeRequired)
{
this.Invoke(...);
}
so you are testing on the main Window itself.
BackgroundWorkers are easy to implement if you are able to given the requirements.
Define a DoWork method that runs on a background thread such as saves to the database. The RunWorkerCompleted method is called when DoWork finishes. RunWorkerCompleted runs on the UI thread, and you can update the view's list with no problems.
// on the UI thread
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += DoWork;
worker.RunWorkerCompleted += RunWorkerCompleted;
worker.RunWorkerAsync("argument");
Events:
static void DoWork(object sender, DoWorkEventArgs e)
{
e.Result = "4";
}
static void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
string a = (string)e.Result;
Console.WriteLine(a);
}
else
{
Console.WriteLine(e.Error.Message);
}
}