You can NOT so much as touch ANY part of a control from a thread other than the one that created it, the UI (or startup) thread. This iswhere your entire problem comes from.
My confusion was mostly that I was not trying to run a Method at all - I was just changing a Property (or what I thought was DATA). Upon more consideration, I realized that a Property is just syntactic sugar over a procedure call, anyway, so the "you can't run code from a different thread" rule applied even changing a Property.
However, I am mystified as to WHY this rule "no touchie from different thread" even exists. Sure, if I'm trying to update a TextBox from other threads then there *could* be confusion and a random mix of characters added from the various threads due to the asynchronous timing of the code in the different threads. Yes, there *could* be... it is *possible* - just like I could really mess up a data structure if I update it from many threads without synchronization.
But that is exactly what multithreaded coding is all about - synchronizing the use of common data structures (at least it is a major issue, if not the biggest issue).
But why would the compiler assume that I am needing multithreading synchronization when updating a Textbox? The truth is, I am doing something at timed intervals - I am using a timer. It just so *happens* that C# chooses internally to implement a timer via a separate new thread. That is not *my* problem. I'm not trying to append text to the textbox from 17 different timers or asynchronous threads. I'm simply trying to update a textbox at constant intervals - in this case the system chooses to do this via ONE (other) thread.
Suddenly now I have to become fully knowledgeable about multithreading and delegates and "invoking", etc. BAH! Unnecessary, unneeded, unwanted complexification.
Aside from all that, I especially did not appreciate that the code simply failed to do anything - I got no compiler warning/error nor any runtime problem. My dialogbox simply didn't update. Then I had the fire up The Google and go searching for why it didn't work. My first real clue was that if I caused the update to happen from the very same function, via a click of a button on the UI, then it worked. Whaaa? My code definitely worked, but only if executed via a pushbutton.
I've coded for years, and I'm teaching myself C# now, and "yeah" this is a noobie kinda problem, but geeeez what an unnecessary timewaster.
Okay, 'nuff whining. Thx to all who replied. I found some code example for doing a "invoke" via a deleagate, etc., etc., and got it working.
Wow! You have such a myopic view of what's going on.
The reason you need Invoke is because controls are NOT thread safe. What you see in the toolbox are .NET wrappers around standard window variants you find in Win32. They are not thread safe because of limitations in the Windows user interface. The golden rule is that any operation on a control is required to be done by the thread that created the window handle. It's possible to wrap all this code in synchronzation code, but that would have been a ton of code and a rather large performance hit.
But, but, but "I'm just changing a property!" Yeah, I know. A property is nothing but a method that gets/sets a value, but that method can also do other things like validation, modifying other values inside the control, updating the controls internal state machine, kicking off events, and whatever else the controls need.
What you're forgetting is that Windows is a shared system. Not only can your code cause code in the contorl to run, but Windows can too, AT ANY TIME. It can send your control a WM_PAINT message to get your control to repaint itself, but, what if you were changing the Text or Forecolor properties, from your thread, at the exact same time the control was getting these values to use in the paint code??
Invoke is there because .NET, like any other application, has to follow the "UI thread rule" like any other application.
A little tidbit: The only methods on controls that ARE thread safe are Invoke, BeginInvoke, EndInvoke and CreateGraphics.
Wow! You have such a myopic view of what's going on.
I don't think so. Let's revisit this one more time.
So I'm supposed to put all code that modifies a control into wrapper code that calls invoke and this wrapper code ends up being a conditional test to see if invoke is required and then recursively calling the same function thru a special delegate that is created just for that invoke call. I've repeated my example code below:
All this is required in order that the code gets executed on the GUI thread. Now, I'm simply updating a textbox with a sample (number). Nothing else.
I guess I could have stated my point better: yes, it is an asynchronous system and it is *possible* that a WM_PAINT would get processed while the textbox text is changing. I am aware of this. My complaint was that .NET designers used this sledgehammer solution to prevent any presentation rendering mishap (ie. my number is rendered while it is changing) by constraining all code to execute on the GUI thread. So this means that for every single interaction with any control - and there's a LOT of them, I'm supposed to write wrapper code around everything, and this wrapper code involves defining and creating delegates and rewriting all my code to include a conditional statement which tests for this invokability.
AND YOU'RE FINE WITH THIS.
Me, I think this is a horrible and terribly primitive solution and design. It would be better to leave me with using SendMessage() to inject my action into the GUI's Message-Loop.
And that's not to mention that the compiler gave no warnings, there was no runtime error, the code SILENTLY failed, and I had no idea that the timer implemented its actions with a (hidden) separate thread.
Me, I think this is a horrible and terribly primitive solution and design.
Microsoft had the same idea; that's why the threaded version of the timer isn't in the WinForms designer-toolbox. The Timer from the toolbox simply raises events on the GUI-thread. No syncing required.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
Hmmmmm... I am using a System.Windows.Forms.Timer. Isn't that the one you are referring to that doesn't use a separate thread? Okay, now I'm confused. My code works if i execute it from a button. It works if I invoke it thru a delegate. Other than that nothing happens.
Perhaps I misunderstood your problem, I thought that it was that the ListBox was not automatically reflecting changes to the underlying DataSource List. The BindingList raises events that would cause the ListBox to update automatically when the List changes.
Here is a simple example. New WinForm project with two buttons and two ListBoxes. Clicking Button1 adds items to the underlying lists, but only the ListBox with the BindingList as the DataSource is update. Clicking Button2 tells ListBox1 to Refresh it's data.
publicpartialclass Form1 : Form
List<int> L1 = new List<int>();
BindingList<int> L2 = new BindingList<int>();
privatevoid Form1_Load(object sender, EventArgs e)
listBox1.DataSource = L1;
listBox2.DataSource = L2;
this.button1.Click += new System.EventHandler(this.button1_Click);
this.button2.Click += new System.EventHandler(this.button2_Click);
privatevoid button1_Click(object sender, EventArgs e)
L1.Add(3); // adds a value to L1, but listBox1 does not display it until the binding is refresshed
L2.Add(33); // adds a value to L2 and listBox2 is automatically refreshed to display it
privatevoid button2_Click(object sender, EventArgs e)
// tell listBox1 to refresh the data
Kuel. I like that you can use a List<> of Key/Values for a ListBox. Did not know that. Then can use the DataMember, ValueMember flexibility. Thx. However...
You are executing the update from a button_click Method - thus the code is executed on the UI thread - thus it will work. My problem is that I am executing from a timer handler - because I need the update at regular intervals (taking samples) - thus the handler is on a different thread and thus, when executed, silently does absolutely nothing.
Thx for the code. Educational. (and 'yes' I got it working)
I have two objects, PriceRule and WeekDay, and PriceRule has a DayId column I have bound to a ComboBox column in a DataGridView. When I load the grid, if DayId is null, the combo is blank, but as soon as I drop the combo down, I have to select a day. No problem, I can inject an extra empty string day into the combo's data source, but the Id property of WeekDay is not nullable, so I need to do something as clumsy and stupid as insert a false day with Id of -1.
How do I catch this -1 and make it null before saving, and vice versa, catch a null DayId and make it -1 when loading?
Is there no other way to do what is a bloody common task that MS clearly isn't capable of handling themselves?