I'm working on a app, where I'm adding items to a listview from a RunWorkerCompleted method using BackgroundWorker. From the RunWorkerCompleted method I'm adding ListViewItems, where I'm setting .Content and .Background.
However when setting the .Background property and calling the .UpdateLayout() method of the ListView class, I'm getting an exception: "Cannot use a DependencyObject that belongs to a different thread than its parent Freezable.".
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
try
{
if (e.Cancelled == true)
{
System.Windows.MessageBox.Show("Something..cancelled");
}
else if (e.Error != null)
{
System.Windows.MessageBox.Show("Something..error " + e.Error.Message);
}
else
{
workLoad_listView.Items.Clear();
workLoad_listView.Height = 23;
foreach (Workload.workload element in Workload.Get())
{
System.Windows.Controls.ListViewItem item = new System.Windows.Controls.ListViewItem();
item.Content = (string)element.name;
item.Background = element.brush; // if I outcomment this line, no exception is thrown!
workLoad_listView.Items.Add(item);
}
workLoad_listView.UpdateLayout(); //exception is thrown here!
while (FindVisualChild<ScrollViewer>(workLoad_listView).ComputedVerticalScrollBarVisibility == Visibility.Visible)
{
workLoad_listView.Height += 1;
workLoad_listView.UpdateLayout();
}
}
}
catch (Exception ex)
{
System.Windows.MessageBox.Show(string.Format("An exception was thrown!\n{0}", ex), "Exception caught", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
This is the Workload class:
public static class Workload
{
public struct workload
{
public string name;
public System.Windows.Media.SolidColorBrush brush;
}
private static List<workload> workload_list = new List<workload>();
public static void Add(string name, int colorNumber)
{
workload tmp_workload = new workload();
tmp_workload.name = name;
System.Drawing.Color color = System.Drawing.ColorTranslator.FromWin32(colorNumber);
tmp_workload.brush = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromArgb(255, (byte)color.R, (byte)color.G, (byte)color.B));
workload_list.Add(tmp_workload);
}
public static void Clear()
{
workload_list.Clear();
}
public static List<workload> Get()
{
return workload_list;
}
}
Any suggestions would be appreciated :)
Best regards.
The System.Windows.Media.SolidColorBrush type is derived from DependencyObject. The DependencyObject type has thread-affinity; that is, it may be used only in the thread where it was created.
You haven't shown a complete code example; in particular, it's not clear from the example where your Workload.Add() method is being called. But given the error message, it seems extremely likely you are calling this from the BackgroundWorker's DoWork event handler. Of course, this handler is operating in a different thread from the UI thread where the RunWorkerCompleted event handler is executed, and so the brush object is being created in a thread different from the one where you want to actually use it.
Basically: this is prohibited by the framework.
Again, without a complete code example, it's hard to know for sure what the best solution would be. However, based on the code shown here, it looks to me as though you should store the System.Windows.Media.Color value for the brush in your workload struct instead of a Brush value, and then create the Brush itself in the RunWorkerCompleted event handler.
I would also suggest changing the type names so that you don't have both a Workload class and a workload struct. Having two type names which are different only in the case of the first letter is potentially very confusing to others who may want to read the code (or to yourself, months later after you've forgotten how you implemented it).
Related
I am working on a Youtube search by keyword programmatically and using Youtube API to do this. I want to fire an event when a search progress is completed and return the result in YoutubeSearchCompletedEventArgs sent by YoutubeSearchCompleted.
But the code inYoutubeSearchCompleted in Form.cs throws cross thread illegal operation exception. Normally, using AsyncOperation.Post method it must not throw InvalidOperationException. Because I used the same method in a download manager project before and it worked well. So I can't understand why this happens.
Youtube search class
class YouTubeManager
{
public delegate void YoutubeSearchCompletedEventHandler(object sender, YoutubeSearchCompletedEventArgs e);
public event YoutubeSearchCompletedEventHandler YoutubeSearchCompleted;
AsyncOperation aop = AsyncOperationManager.CreateOperation(null);
List<YoutubeVideo> SearchByKeyword(string keyword)
{
List<YoutubeVideo> videos = new List<YoutubeVideo>();
//.......
//...Youtube data api search codes....
//.......
return videos;
}
public void Search(string keyword)
{
Task.Run(() =>
{
List<YoutubeVideo> list = SearchByKeyword(keyword);
aop.Post(new System.Threading.SendOrPostCallback(delegate
{
if (YoutubeSearchCompleted != null)
YoutubeSearchCompleted(this,
new YoutubeSearchCompletedEventArgs(keyword, list);
}), null);
});
}
}
Form.cs
public partial class Form1 : Form
{
YouTubeManager yam = new YouTubeManager();
public Form1()
{
InitializeComponent();
this.Load += Form1_Load;
}
void Form1_Load(object sender, EventArgs e)
{
yam.YoutubeSearchCompleted += yam_YoutubeSearchCompleted;
yam.Search("Blues");
}
void yam_YoutubeSearchCompleted(object sender, YoutubeSearchCompletedEventArgs e)
{
if (e.Videos.Count < 1) return;
textBox1.Text = e.Videos[0].Title();
}
}
In this code the textBox1.Text = e.Videos[0].Title(); line throws InvalidOperationException. How can I fix this problem?
Note: I don't want Invoke method, just AsyncOperation.
Most likely the issue is caused by AsyncOperation created too early. You can check that with the following:
if (!(aop.SynchronizationContext is WindowsFormsSynchronizationContext))
{
// Oops - we have an issue
}
Why is that? The AsyncOperation stores the SynchronizationContext.Current at the construction time, and normally all Control derived classes (including the Form) install WindowsFormsSynchronizationContext from inside the Control class constructor.
But imagine the Forgm1 is your startup form (e.g. the typical Application.Run(new Form1()); call from Main). Since Any instance variable initializers in the derived class are executed before the base class constructor, at the time aop variable is initialized (through yam field initializer), the Control class constructor has not been ran yet, hence the WindowsFormsSynchronizationContext is not installed, so the AsynOperation is initialized with the default SynchronozationContext, which implements Post by simply executing it on separate thread.
The fix is simple - don't use initializer, just define the field
YouTubeManager yam;
and move the initialization
yam = new YouTubeManager();
inside the form constructor or load event.
As you can see, I have two classes. RfidReaderHardware generates event in thread "th", but Form running at another thread. As you can see, in form if use Invoke method of ListViewControl. So, question is how to change RfidReaderHardware to resolve encapsulation problem.
public class RfidReaderHardware : IDisposable
{
public event EventHandler<RfidReaderEventArgs> OnNewPackage;
Thread th;
//This method will be called from thread "th"
private void FireNewPackageEvent(UHFPackage package)
{
... code ...
}
... some code ...
}
and we have example code, where this event is using
public partial class PassageForm : Form
{
RfidReaderHardware RfidReader = new RfidReaderHardware(...);
private void Form1_Load(object sender, EventArgs e)
{
RfidReader.OnNewPackage += NewRfidPackage;
}
//not sure, but i think it's running in thread "th"
private void NewRfidPackage(Object o, RfidReaderEventArgs e)
{
ListViewItem item = new ListViewItem();
//from point of encapsulation view it's wrong as you know
CPackageList.Invoke(new Action(() => {CPackageList.Items.Add(item); }));
}
}
question is how to change RfidReaderHardware to resolve encapsulation problem
In fact there is no encapsulation problem. By definition, the relation between event source and subscriber is one to many, hence the source cannot "encapsulate" a logic for a specific subscriber. It's the subscriber choice how to handle the notification. One can ignore it, or handle it immediately, or like in your case handle it on the UI thread either synchronously (using Control.Invoke) or asynchronously (using Control.BeginInvoke).
Not so sure there's any real need to fix this, having the UI object itself deal with the fact that event is fired on the "wrong" thread is not a flaw. As long as you know it is in fact fired on the wrong thread, a documentation requirement.
.NET however has a general mechanism to solve this, it is used in several places inside the .NET Framework code. Your RfidReaderHardware class constructor can copy the value of SynchronizationContext.Current and store it in a field. With the implicit assumption that the object is created by code that runs on the UI thread. When you are ready to fire the event, and the copied object isn't null, you can then use its Post() or Send() method. Which automagically makes the code resume on the UI thread. Regardless of the specific UI class library that was used, works just as well in a WPF or Universal app for example.
Some sample code, it doesn't take much:
public class RfidReaderHardware {
public event EventHandler Received;
public RfidReaderHardware() {
syncContext = System.Threading.SynchronizationContext.Current;
}
protected void OnReceived(EventArgs e) {
if (syncContext == null) FireReceived(e);
else syncContext.Send((_) => FireReceived(e), null);
}
protected void FireReceived(EventArgs e) {
var handler = Received;
if (handler != null) Received(this, e);
}
private System.Threading.SynchronizationContext syncContext;
}
I just answered a question about whether a Task can update the UI. As I played with my code, I realized I'm not clear myself on a few things.
If I have a windows form with one control txtHello on it, I'm able to update the UI from a Task, it seems, if I immediately do it on Task.Run:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Task.Run(() =>
{
txtHello.Text = "Hello";
});
}
}
However if I Thread.Sleep for even 5 milliseconds, the expected CrossThread error is thrown:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Task.Run(() =>
{
Thread.Sleep(5);
txtHello.Text = "Hello"; //kaboom
});
}
}
I'm not sure why that happens. Is there some sort of optimization for an extremely short running Task?
You didn't post the exception stack trace, but I expect that it looked something like this:
System.InvalidOperationException: Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on.
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.Control.set_WindowText(String value)
at System.Windows.Forms.TextBoxBase.set_WindowText(String value)
at System.Windows.Forms.Control.set_Text(String value)
at System.Windows.Forms.TextBoxBase.set_Text(String value)
at System.Windows.Forms.TextBox.set_Text(String value)
at WindowsFormsApplicationcSharp2015.Form1.<.ctor>b__0_0() in D:\test\WindowsFormsApplicationcSharp2015\Form1.cs:line 27
We can see that the exception is thrown from the Control.Handle getter property. And in fact, if we look at the source code for that property, there it is, as expected:
public IntPtr Handle {
get {
if (checkForIllegalCrossThreadCalls &&
!inCrossThreadSafeCall &&
InvokeRequired) {
throw new InvalidOperationException(SR.GetString(SR.IllegalCrossThreadCall,
Name));
}
if (!IsHandleCreated)
{
CreateHandle();
}
return HandleInternal;
}
}
The interesting part is when we look at the code that calls Control.Handle. In this case, that's the Control.WindowText setter property:
set {
if (value == null) value = "";
if (!WindowText.Equals(value)) {
if (IsHandleCreated) {
UnsafeNativeMethods.SetWindowText(new HandleRef(window, Handle), value);
}
else {
if (value.Length == 0) {
text = null;
}
else {
text = value;
}
}
}
}
Notice that the Handle property is only invoked if IsHandleCreated is true.
And for completeness, if we look at the code for IsHandleCreated we see the following:
public bool IsHandleCreated {
get { return window.Handle != IntPtr.Zero; }
}
So, the reason you don't get the exception, is because by the time the Task executes, the window handle hasn't been created yet, which is to be expected since the Task starts in the form's constructor, that is, before the form is even displayed.
Before the window handle is created, modifying a property doesn't yet require any work from the UI thread. So during this small time window at the start of your program, it would seem that it is possible to invoke the methods on control instances from a non-UI thread without getting the "cross thread" exception. But clearly, the existence of this special small time window doesn't change the fact that we should always make sure to invoke control methods from the UI thread to be safe.
To prove the point that the timing of the window handle creation is the determining factor in getting (or not) the "cross thread" exception, try modifying your example to force the creation of the window handle before you start the task, and notice how you will now consistently get the expected exception, even without a sleep:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Force creation of window handle
var dummy = txtHello.Handle;
Task.Run(() =>
{
txtHello.Text = "Hello"; // kaboom
});
}
}
Relevant documentation: Control.Handle
If the handle has not yet been created, referencing this property will force the handle to be created.
I'm writing a simple Windows forms application to get me into the swing of things with Threads. So far what I have is working, but what I would like to do is contain it all in a seperate class rather than directly in my forms code.
I have a background thread that starts and retrieves data from a database. I then display that data in to a listbox.
private delegate void UpdateListValues(List<ListBoxItem> itemList);
private void form_main_Shown(object sender, EventArgs e)
{
// Set the loading text.
list_selection.Items.Add(ListHelpers.LoadingItem());
// Start the data access on a seperate thread.
Thread worker = new Thread(GetInvoicingData);
worker.IsBackground = true;
worker.Start();
}
private void GetInvoicingData()
{
// Query database
List<ListBoxItem> values = DAC.GetInvoicingAccounts();
// Display results
BeginInvoke(new UpdateListValues(DisplayList), new object[] { values });
}
private void DisplayList(List<ListBoxItem> itemList)
{
// Display each result
list_selection.Items.Clear();
for (int i = 0; i < itemList.Count; i++)
{
list_selection.Items.Add(itemList[i]);
}
}
The problem is that in the DisplayList method, I won't be able to access the list box (list_selection) because it's part of the form class. Does anyone have any suggestions on how I can do this.
Also, I'm new to threading so feel free to tell me I'm doing it absolutely wrong. I just used the example from http://www.codeproject.com/Articles/23517/How-to-Properly-Handle-Cross-thread-Events-and-Upd to get me to where I am now.
Thanks
How about something like this:
// Added the form's class declaration to highlight separation of thread code into a separate class, but may not be exactly the same as yours depending on naming
public class Form1 : Form
{
private readonly DataRetriever _dataRetriever;
private void form_main_Shown(object sender, EventArgs e)
{
// Set the loading text.
list_selection.Items.Add(ListHelpers.LoadingItem());
// Create the DataRetriever, and provide it with a delegate to DisplayList for returning data
_dataRetriever = new DataRetriever(DisplayList);
// Start retrieving data on a separate thread...
_dataRetriever.GetData();
}
private void DisplayList(List<ListBoxItem> itemList)
{
if (InvokeRequired)
{
// Ensure the update occurs on the UI thread
Invoke((Action)(() => DisplayList(itemList)));
return;
}
// Display each result
list_selection.Items.Clear();
foreach (var item in itemList)
{
list_selection.Items.Add(item);
}
}
}
// Separate class to hold thread code
public class DataRetriever
{
public delegate void UpdateCallbackDelegate(List<ListBoxItem> itemList);
private readonly UpdateCallbackDelegate _updateCallback;
public DataRetriever(UpdateCallbackDelegate updateCallback)
{
_updateCallback = updateCallback;
}
public void GetData()
{
var thread = new Thread(GetInvoicingData)
{
IsBackground = true
};
thread.Start();
}
private void GetInvoicingData()
{
// Not sure whether "DAC" is a static class, if it needs to be constructed
// in the DataRetriever's constructor, or passed to it as a parameter
_updateCallback(DAC.GetInvoicingAccounts());
}
}
As you can see, all the thread code is now in a separate class DataRetriever, and a delegate provided when constructing it to enable the retrieved data to be passed back to the form once the retrieval is complete. The method that handles the callback ensures that the call is marshalled to the UI thread to prevent cross-thread exceptions.
I would like to point out that this is not presented as the "best" way to do this, but merely as an answer to the question (how to separating threading code into a separate class). As others have mentioned, there are already mechanisms in place to do this sort of thing (e.g. BackgroundWorker). Some complexity has been omitted for clarity. For example, in the implementation presented here, if you were to call GetData() multiple times (with each call occurring before the previous ones have returned their data), you would have multiple queries occurring simultaneously, and as they are running asynchronously, may return their data in an arbitrary order. This may or may not be an issue in your case.
Presently I'm working with WinForms(in C#) and I have to run the application in the background. For this purpose I'm using asynchronous. When I run the application it's showing an exception like
"Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on."
How can I solve this error?
When making method calls to a control, if the caller is on a different thread than the one the control was created on, you need to call using Control.Invoke. Here is a code sample:
// you can define a delegate with the signature you want
public delegate void UpdateControlsDelegate();
public void SomeMethod()
{
//this method is executed by the background worker
InvokeUpdateControls();
}
public void InvokeUpdateControls()
{
if (this.InvokeRequired)
{
this.Invoke(new UpdateControlsDelegate(UpdateControls));
}
else
{
UpdateControls();
}
}
private void UpdateControls()
{
// update your controls here
}
Hope it helps.
Most often, the best way to do this sort of thing with WinForms is to use BackgroundWorker, which will run your work on a background thread, but provide you with a nice clean way to report status back to the UI.
In a lot of everyday .NET programming, explicitly creating threads or calling .Invoke is a sign that you're not using the framework to its full advantage (of course, there are lots of legitimate reasons to do low-level stuff too, it's just that they're less common that people sometimes realise).
You need to check if Invoke is required for the control you're trying to update. Something like this:
Action<Control, string> setterCallback = (toSet, text) => toSet.Text = text;
void SetControlText(Control toSet, string text) {
if (this.InvokeRequired) {
this.Invoke(setterCallback, toSet, text);
}
else {
setterCallback(toSet, text);
}
}
Updated from Invoke to begin Invoke
// you can define a delegate with the signature you want
public delegate void UpdateControlsDelegate();
public void SomeMethod()
{
//this method is executed by the background worker
InvokeUpdateControls();
}
public void InvokeUpdateControls()
{
if (this.InvokeRequired)
{
this.BeginInvoke(new UpdateControlsDelegate(UpdateControls));
}
else
{
UpdateControls();
}
}
private void UpdateControls()
{
// update your controls here
}
A pattern you might find useful is to do a check at the top of functions that interact with the GUI to see whether you are running on the correct thread or not and have the function invoke itself if required. Like this:
public delegate void InvocationDelegate();
public void DoGuiStuff(){
if (someControl.InvokeRequired){
someControl.Invoke(InvocationDelegate(DoGuiStuff));
return;
}
//GUI manipulation here
}
Using this pattern - if you are on the correct thread when the method is called it doesn't invoke itself, but if you are on a different thread it will invoke itself and then return (so the GUI manipulation logic is only ever called once either way).
The UI changes can be done with Control.Invoke() methods, this cross thread exception can be solved using below code snippet.
void UpdateWorker()
{
//Here ddUser is the user control
//Action to be performed should be called within { } as like below code
if (this.ddUser.InvokeRequired)
ddUser.Invoke(new MethodInvoker(() => { ddUser.Size = new Size(100, 100); }));
}
I knew the topic is 10 years old, but I would like to improve the solution for generic through lambda selector instead of defining of each type of setter
private void SetControlSafety<C, V>(C control, Expression<Func<C, V>> selector, V value)
{
if (this.InvokeRequired)
this.Invoke(MyUtils.GetSetter(selector), control, value);
else
DataCrawlerUtils.GetSetter(selector)(control, value);
}
Or static
public static void SetControlSafety<C, V>(C control, Expression<Func<C, V>> selector, V value) where C : Control
{
if (control.InvokeRequired)
control.Invoke(DataCrawlerUtils.GetSetter(selector), control, value);
else
DataCrawlerUtils.GetSetter(selector)(control, value);
}
GetSetter method from here to assign value to a property has been selected through lambda
public static Action<T, TProperty> GetSetter<T, TProperty>(
Expression<Func<T, TProperty>> pExpression
)
{
var parameter1 = Expression.Parameter(typeof(T));
var parameter2 = Expression.Parameter(typeof(TProperty));
// turning an expression body into a PropertyInfo is common enough
// that it's a good idea to extract this to a reusable method
var member = (MemberExpression)pExpression.Body;
var propertyInfo = (PropertyInfo)member.Member;
// use the PropertyInfo to make a property expression
// for the first parameter (the object)
var property = Expression.Property(parameter1, propertyInfo);
// assignment expression that assigns the second parameter (value) to the property
var assignment = Expression.Assign(property, parameter2);
// then just build the lambda, which takes 2 parameters, and has the assignment
// expression for its body
var setter = Expression.Lambda<Action<T, TProperty>>(
assignment,
parameter1,
parameter2
);
return setter.Compile();
}
Then the using is pretty simple
SetControlSafety(txtStatus, x => x.Text, "Loading resources...");
BeginInvoke
It is a good way to prevent a cross-thread exception. I read it in a book "The C# Programmer’s Study Guide (MCSD"
You can use BeginInvoke
BeginInvoke method is used to change values of UI control from other threads. It does it in a thread-safe way. It requires a delegate; it tells which UI control needs to change its value.
private async void button1_Click(object sender, EventArgs e)
{
Task task = Task.Run(() =>
{
this.BeginInvoke(new Action(() =>
{
label1.Text = "Hello";
}));
});
await task;
}
The value of label1.Text shall be changed to “Hello” and no exception will arise because it’s a threadsafe operation.