|
Luc
If I take your canonical pattern and shove it into a static utilities class (possibly extending it by checking the typeof control) I presume this would act as a global thread safe control updater (only called when using a second thread naturally).
|
|
|
|
|
Yes you could do that; I never used it that way, most often I don't even pass the Control, I tend to create one or a few dedicated methods for each control (as there aren't that many) within the Form class, but there probably isn't a good reason for doing so, except for providing a more functional name to the method.
Note some actions may need more parameters, say setting the n-th item in a listbox.
And then there is the performance issue when you need to update a lot, it is wise to pass all data and cross the thread barrier only once.
Luc Pattyn [Forum Guidelines] [My Articles]
I only read code that is properly indented, and rendered in a non-proportional font; hint: use PRE tags in forum messages
|
|
|
|
|
I have 2 common operations that I want to perform from a thread, update a textbox or label (typically with the name of a completed process) or increment a progress bar so testing to the progress bar and an else would meet 90% of my needs.
Where I need to update a listview, which I often need to, I would use a specific delegated method.
|
|
|
|
|
If I understand you correctly, I would create a couple of global methods then, one for SetText (taking a Control, as all Controls have a Text property; and a string), one for SetProgress (taking a ProgressBar, not a Control, and a number), etc.
That's a bit more code (statically), however it does not perform any unnecessary type checking (with as and is).
BTW: especially for progress bars, make sure you don't update progress at a ridiculous precision; if you're not careful each iteration would cause a thread switch and possibly just confirm the previous version (assuming you are using a small range of values, say percentages). So it may be worthwhile to "cache" the latest value and short-circuit when there isn't a real change.
(I should add this to my article)
FWIW: this holds true as well when using BackgroundWorker.ReportProgress(), however I haven't seen this kind of warnings in MSDN yet.
Luc Pattyn [Forum Guidelines] [My Articles]
I only read code that is properly indented, and rendered in a non-proportional font; hint: use PRE tags in forum messages
|
|
|
|
|
Thinking about it I had already come to the 2 method solution and thinking more I will probably create a generic listview updater as well. I always do the same thing when processing a list of stored procs, uncheck the item, add the record count and time to sub items.
Luc Pattyn wrote: don't update progress at a ridiculous precision
Been there, I regularly process 100k+ result sets, you very quickly learn to lighten the load when using a progress bar . I think it should be sooooo obvious the warning would be a little redundant.
|
|
|
|
|
Mycroft Holmes wrote: the warning would be a little redundant
Now I disagree. Too often I've seen code (not mine!) that would run say 10 times faster by commenting out the progress bar, sometimes making it so fast a progress bar doesn't make much sense any more. Remember, with the Invoke in place, a thread switch could easily dominate the bulk of the loop code.
Seems some people lack a feeling for normal and abnormal speed.
[EDIT]The article has been updated.[/EDIT]
Luc Pattyn [Forum Guidelines] [My Articles]
I only read code that is properly indented, and rendered in a non-proportional font; hint: use PRE tags in forum messages
|
|
|
|
|
Totally agree with you. You don't have to update the progress bar unnecessarily.
BTW, I used to use BeginInvoke than Invoke when I need to update the controls. This executes as an asynchronous call and won't block the worker thread until progress bar is updated.
Best wishes,
Navaneeth
|
|
|
|
|
yes, you have told me that before; I did not react on it then, other than start thinking about it. So I did, and I decided not to change my ways.
Of course I do agree you can use BeginInvoke, and it has the advantage of not blocking the worker.
However it does not really fit my "canonical form", where a single method can be called from the GUI thread as well as any other thread. If I were to use BeginInvoke there, it would be a synchronous method when called from the GUI thread, and asynchronous when called from another thread, which would probably be very confusing.
The same point, in other words, is: I prefer asynchronous operations to be obvious from their name, so "Begin" or "Async" should be part of the method name, which then wouldn't be possible in my canonical form.
Another, minor, issue might be: I do not like the idea of a lot of GUI work piling up somewhere, as this (1) would consume memory nobody is aware of, and (2) will consume a lot of CPU cycles even after the worker has finished.
Of course, if the net result would be fewer thread switches (I should perform an experiment), then it would have its merits.
And finally, I just added to the article a remark about avoiding frequent progress bar updates; having them synchronous should make it easier for the programmer to notice something is going wrong in that area.
If and when I do some experiments on this, I'll let you know the results.
Luc Pattyn [Forum Guidelines] [My Articles]
I only read code that is properly indented, and rendered in a non-proportional font; hint: use PRE tags in forum messages
|
|
|
|
|
Luc Pattyn wrote: Of course, if the net result would be fewer thread switches (I should perform an experiment), then it would have its merits.
I don't think BeginInvoke has this advantage over Invoke as both are doing the same API call internally. All it does extra is to run the method in a separate thread to get asynchronous nature.
Luc Pattyn wrote: If and when I do some experiments on this, I'll let you know the results.
Thanks. I will be very much interested to hear from you.
Best wishes,
Navaneeth
|
|
|
|
|
Luc is correct, the right way to do this is to do the operation in another thread and make that thread report back the progress to the GUI thread so it can update progress bars or whatever. This is exactly what the BackgroundWorker component was designed for.
Dave
BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn) Why are you using VB6? Do you hate yourself? (Christian Graus)
|
|
|
|
|
The easiest way is to write Application.DoEvents() inside the for loop. This will allow the application to process its message queue and update the Progress bar, but the downside is that your loop can become slower by upto 10 times (or even more).
Using a BackgroundWorker is the most appropriate choice for background activities. It runs your code in a separate thread which allows your main thread to process other messages and update the GUI.
|
|
|
|
|
calling Application.DoEvents() is dangerous; for one it risks a StackOverflowException. I do not recommend it unless I am sure the programmer knows the internals of the Windows messaging system (in which case he probably does not need any advice and wouldn't have asked the question at all).
Luc Pattyn [Forum Guidelines] [My Articles]
I only read code that is properly indented, and rendered in a non-proportional font; hint: use PRE tags in forum messages
|
|
|
|
|
Yes, I know that. I just wanted to give an heads up on why his GUI remained unchanged when his main thread was processing a loop.
|
|
|
|
|
I would like to add that when DoEvents is called, your GUI becomes "alive" in the middle of the process and your user can click buttons and create unstable UI states.
It also seems like calling DoEvents inside a loop may decrease performance to the ground depending on your loop code.
When using threading and UI make sure to use Invoke and BeginInvoke properly to avoid dead locks or racing/overflowing
|
|
|
|
|
Wrong target, I know these things and am reluctant to use DoEvents().
Luc Pattyn [Forum Guidelines] [My Articles]
I only read code that is properly indented, and rendered in a non-proportional font; hint: use PRE tags in forum messages
|
|
|
|
|
Hi!
I understand now, that I was a bit naive about my question.
After a bit of thought, I guess using the BackgroundWorker is indeed the best solution.
In future, I will definitely give more thought to the general division of GUI and program from the start,
not only in the sense of separating the code (what I already did in not using GUI elements to store data etc.) but also in the sense of separating code execution to avoid such deadlocks. Although that means having some kind of state-machine in the GUI to handle the asynchronous working (in most simple cases just 2 states, busy and idle).
A shame that C#/Visual Studion/Runtime lib do not clearly enforce such a separation.
Or is there a framework to enforce(!), not just enable, this ?
Thanks for the discussion, Rolf
modified 2-Aug-18 21:02pm.
|
|
|
|
|
I tried my best to Normalize the tables but someone suggested me there is something wrong.
Could anyone please help me out.
COURSE[CourseCode, CourseName]
INSTRUCTOR[InstructorNumber, InstructorName]
CLASS[CourseCode, ClassCode, InstrNo, InstrName, CourseStartDate]
ENROLLEMENT[CourseCode, ClassCode, StudentNumber, StudentName,Grade]
Relationships: The relationship I have defined are as below.
Instructor and Class tables: 1 to many.
Class and Course: 1 to many
Thanks
|
|
|
|
|
Take it to the database forum... at least.
|
|
|
|
|
|
Hi , have a good day
I hope I can Help
Try this SQL Query
SELECT COUNT(Student) FROM table1 WHERE year = 2009
I know nothing , I know nothing ...
|
|
|
|
|
Hello. I'm having some problems about comparing two pictureBox . I want the other one to be invisible when they have the same image but when I tried it didn't have any changes. How can I do that? Me and my groupmates are creating a game similar to mystery case files: prime suspect and I'm the one who is creating the two player mode. The purpose of making the visible property of the other pictureBox is to let the players know that they already found the image we're asking for them to find but until now I can't still do that. Please help me.
Here's the code I tried to use, this is just one of the pictureBox i want to compare to the other pictureBox:
private void picBoxRandom1_Click(object sender, EventArgs e)
{
if (picBoxPlayerOne1st.Image.Equals(picBoxRandom1.Image))
{
picBoxRandom1.Visible = false;
PlayerTwo();
}
if (picBoxPlayerTwo1st.Image.Equals(picBoxRandom1.Image))
{
picBoxRandom1.Visible = false;
PlayerOne();
}
}
picBoxPlayerOne1st is the pictureBox where the asked image is being shown while the picBoxRandom1 is the pictureBox where the right image is shown. Both pictureBox have random images.
|
|
|
|
|
Image.Equals does a reference comparison and it will always return false since they may be pointing to two different Image objects in memory, even if you loaded them from the same file.
You can achieve this behavior by setting a flag during the loading of these images into the picture boxes by checking if they are loaded from the same file.
|
|
|
|
|
Oh, i forgot to say that the images that the two pictureBox get came from an imagelist.
|
|
|
|
|
The problem is that the PictureBox.Equals method[^] is inherited from object, and is a reference test (as PictureBox is a reference object). What that means in English is that Equals compares the references (in C or C++ that would be a pointer) rather than comparing them bit - by - bit. If the pointers are to different ememory, then the Equals test will return false even if the two images are bitwise identical. You will have to do a bitwise comparison to check they are realy different.
No trees were harmed in the sending of this message; however, a significant number of electrons were slightly inconvenienced.
This message is made of fully recyclable Zeros and Ones
"Rumour has it that if you play Microsoft CDs backwards you will hear Satanic messages.Worse still, is that if you play them forwards they will install Windows"
|
|
|
|
|
How can i do the bitwise comparison if the images i get came from an imageList?
|
|
|
|