|
I think you misunderstood - I didn't want to imply that you shouldn't use tasks (or worker threads in general). For keeping the UI responsive/updating a progress bar while a lengthy operation is in progress it's the only option you have. Just when you do use tasks/threads you have to make sure you don't directly call control's methods but invoke that on the UI thread (which is likely the cause of the TargetInvocationException) - take a look the the link in my previous reply.
And I would recommend to separate the tasks from your custom control classes, but that's a design issue and not the immediate cause of your problem here.
Can you show the stack trace of the exception and the code where it occurs?
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
If I was starting out on "multi-threading", I would get familiar with the BackgroundWorker (BGW) and DispatcherTimer classes first; they are more intuitive than "Tasks" (IMO).
(BGW can update the UI in the ReportProgess callback; while DispatcherTimer runs on the UI thread, so even simpler).
I created generic "adapters" of the above classes that will accept an object with a interface for "DoWork", "ReportProgress" and "Completed"; and "run with it".
Once you have your "pattern" down, adding new "threads" is trivial.
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|
|
As I said/implied in my reply to Sacha, the implementation isn't as important as making sure that the user has been made aware that the program hasn't stopped responding by giving feedback. That is one of the really annoying things about .NET. In my experience, the UI doesn't update when it is supposed to while a CPU intensive process is running on the UI thread; even if that process consistently performs changes to the UI. So as long as the UI updates properly, and the code is logically organized (and easy to maintain), I don't care which convention is used.
As far as "tasks", I simply mean calling the method that performs a specific "step" in the process being reported to the user. My Task class combines these together into one, easy to maintain, logical entity.
I will look into your suggestions.
|
|
|
|
|
The point was, BGW and DispatcherTimer were designed with "UI updating" in mind.
Obviously I wasn't clear enough on that point; but, "task" away.
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|
|
Thanks. This tip really panned out. I was even able to use the DoWorkEventHandler delegate as a parameter to set up the handler callback as part of of my custom Task class. Very convenient.
|
|
|
|
|
Yes .. you got it!
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|
|
I am getting another problem. As far as I understand it, the background worker "finishes" when the DoWork callback has completed. I have a method that is called when the UI detects that the task is completed which is supposed to make sure the background worker doesn't get messed with when it is done. It is supposed to prevent cross-thread issues (the RunWorkerCompleted handler sets BackgroundWorker to null to try and prevent the exception below) while still making sure that the UI is updated and the worker "knows" it is supposed to be done. That method is below:
public void End()
{
if (BackgroundWorker != null)
{
if (BackgroundWorker.IsBusy)
BackgroundWorker.ReportProgress(100);
BackgroundWorker = null;
}
}
However, the line with the call to ReportProgress crashes with this inner exception:
"This operation had OperationCompleted called on it and further calls are illegal"
First, if the operation has been completed, how come the handler for RunWorkerCompleted hasn't been called yet (I put a break in the handler's first line so I know this for certain)? Second, if the operation is completed, how can it be "busy"?
|
|
|
|
|
The "completed" event / handler is able to access the UI; it doesn't call "Report progress". It's the equivalent of a "final" report progress; with a (optional) "state object" in the event arg.
BGW is an IDisposable.
You maintain a reference to it somewhere, and use that to: Start and optionally stop; and later dispose of the worker.
An "adapter" (wrapper) make it easy to interface with the BGW and create a diverse collection of worker references that can be managed.
On the same way you used a delegate for "work" and "progress", also use a delegate for "completed" (or 3 interface access points).
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|
|
Ok. But I have already done all that. The UI class that manages the worker (the "adapter" as you say) is the same custom control class that manages the tasks (represented by the "Task" class) and displays progress. It has a method "AddTask" that sets up the background worker like this:
public void AddTask(Task task)
{
tasks.Add(task);
counterText = counterTemplate.Replace("#2", tasks.Count.ToString());
task.BackgroundWorker.WorkerReportsProgress = true;
task.BackgroundWorker.ProgressChanged += OnTaskProgressChanged;
task.BackgroundWorker.RunWorkerCompleted += OnTaskCompleted;
}
(The DoWork event handler is set up in the Task class constructor so it has already been assigned by the time the above method is called). Assuming by "completed" you are referring to the RunWorkerCompleted event, as you can see, I have a handler (shown below). That handler makes it so that the BackgroundWorker method for the task is set to null (by calling the task's "InvalidateWorker" method also shown below). This handler is NOT being called before the Task::End() code I posted above.
The OnTaskCompleted method (part of the custom control with the AddTask method above):
void OnTaskCompleted(object sender, RunWorkerCompletedEventArgs e)
{
tasks[currentIndex].InvalidateWorker();
NextTask();
}
The InvalidateWorker() method in the Task class (I suppose I could dispose the BackgroundWorker here if necessary):
public void InvalidateWorker()
{
BackgroundWorker = null;
}
I have a break on the line where the OnTaskCompleted method calls the InvalidateWorker method that is not reached before the End method I posted previously is called (from the OnTaskProgressChanged method if the UI detects that the progress bar has reached its last step). So I am having a hard time understanding why the background worker is "completing" without invoking the event handler for it. I am also assuming that because it is "busy" it hasn't officially completed the call.
The only explanation I can think of is that the "completed" message is queued in the message queue/loop behind the "progress" message that detected the work was technically completed and that the background worker is shut down before "completed" event is invoked. If so, that is just bad design on the part of Microsoft. I figured though that the "IsBusy" would indicate that this is the case (see my End() method in the previous post); but apparently that isn't the case either... which makes Microsoft's code even worse.
modified 3-Dec-17 7:28am.
|
|
|
|
|
I suppose this is all easily fixed by removing the "BackgroundWorker.ReportProgress(100)" call in my Task::End() method. I only included this originally because I did a test program to see how the BackgroundWorker operated before building the logic of my "adapter". I wanted to test how the BackgroundWorker dealt with values over 100 because the BackgroundWorker class was designed without taking into consideration that the programmer might not want to report percentage, but rather tasks (such as files being processed etc...). So I built this test program:
namespace BackgroundWorkerTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void OnDoWork(object sender, EventArgs e)
{
backgroundWorker.RunWorkerAsync();
backgroundWorker.DoWork += backgroundWorker_DoWork;
}
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 10; ++i)
{
for (int x = 0; x < 10; ++x)
{
for (int j = 0; j < 1000000; ++j)
{
bool b = false;
}
}
backgroundWorker.ReportProgress(500);
}
}
private void OnProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value += e.ProgressPercentage;
}
private void OnWorkComplete(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Worker Done!", "Worker Message");
}
}
}
When I run the test, the "OnWorkComplete" method is called, posting the "Worker Done!" message box long before the DoWork method is completed... indicating to me that the "complete" event is also raised when ReportProgress is called with a value of over 100 (this was the behavior I was testing). I added my line in my "End()" post in order to make sure the "completed" event occurred. Apparently both this and completing the DoWork callback can invoke the "complete" event while only the second scenario shuts the worker down. Again... more terrible design on the part of Microsoft.
EDIT NOTE: The BackgroundWorker in this particular test is connected to the last two handlers by the Designer since the test only uses one worker that I directly tied to the GUI.
modified 3-Dec-17 7:59am.
|
|
|
|
|
You shouldn't get hung up on this "reporting %"; it's a totally artificial construct you have to maintain yourself. If your process is indeterminate, it is totally useless.
Most of the time (all the time), I never need the BGW "completed event".
I have a BGW that may handle a queue; nobody cares if the queue is empty, only if it's not being serviced ("DoWork").
Your last iteration is getting to the heart of what I was about (but without the "task" clutter; saving a few lines of code is not always in the best interest).
public interface IDoWork {
void DoWork( object sender, DoWorkEventArgs e );
void ProgressChanged( object sender, ProgressChangedEventArgs e );
void WorkerCompleted( object sender, RunWorkerCompletedEventArgs e );
}
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|
|
You said: "You shouldn't get hung up on this "reporting %"; it's a totally artificial construct you have to maintain yourself. If your process is indeterminate, it is totally useless."
I only care about it because Microsoft has forced us to. This is exactly why forcing us into a percent scheme rather than allowing the user to implement how the user handles the data (integer) passed to BackgroundWorker::ReportProgress(int), and raising the "RunWorkerCompleted" event when the "percent" is at or over 100 is a really stupid design on the part of Microsoft. In addition to the problems already mentioned, I have to use BackgroundWorker::ReportProgress(int, object) to pass an integer higher than (or equal to) 100 in order to prevent the "complete" event from being invoked.
|
|
|
|
|
% is usefull if your "UI" is a progress bar.
Besides being able to pass a "state" object INTO the BGW process (which could have its own "%"), and TO progress and completed events; as well as being able to access "shared properties", "% reporting" is a "bell and whistle" and not a limitation.
That's why I said not to get hung up on it.
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|
|
If it was just a "bell and whistle" as you say, then using it versus the state object would be optional. There would be an overload for just sending a state object. I find it annoying to have to worry about an unused parameter and how it could screw up someone's program if they aren't careful. If you disagree then that is your prerogative. Functionally it works fine, so I will use it; I just try to make my shared classes as versatile to different use cases as possible; I am a bit surprised, (and annoyed because that is my prerogative), that Microsoft didn't do the same here; especially when their own documentation says that it is up to the user to do something with the data.
|
|
|
|
|
It is "optional". You seem to think it somehow controls the event processing; it doesn't. 0, 100, nada makes no difference.
Depending what "examples" one finds, one "assumes" one is more significant than the other (state object vs % vs BOTH vs NONE) based on the code emphasis. You just happended to be focused on the "%" ones.
Belt, suspenders or both?
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
modified 4-Dec-17 12:55pm.
|
|
|
|
|
Actually it does make a difference. If I send an integer >= 100 as the percentProgress parameter to BackgroundWorker.ReportProgress, then the BackgroundWorker.RunWorkerCompleted event fires even if the DoWork callback is not completed. I tested this before I even started this thread to find out what happens when I send a number greater than 100 (because the documentation implies that the number should not be greater than 100).
|
|
|
|
|
I'll take your word for it.
I found no use for it; since few processes I deal with are "determinate".
"Exiting" based on a calculation seems like a bad day.
(The value sounds more like an "input" to a progress bar (i.e. the "ui" part of "reporting progress"); the usual assumption).
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|
|
hello, i want to promote the student one class to another class, by using what field to update automatically the student promotion to the next class and next year.can you give some ideas.
|
|
|
|
|
No, because we have no idea what your code or data organisation looks like.
What you've asked is like phoning a travel agent at random, saying "I want to upgrade my hotel room" and putting the phone down. The travel agent doesn't know who you are, which hotel you are supposed to be staying at, when you are staying, or even what country the hotel is in!
Remember that we can't see your screen, access your HDD, or read your mind - we only get exactly what you type to work with. And at the moment, we don't have anything at all ...
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Use the "promotion transaction".
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|
|
Member 12898590 wrote: by using what field to update automatically the student promotion
First, promotion is a 'process' which means you must write a method that changes some data (for you to figure out) in an existing instance (a student) to represent that.
Second 'automatically' means that something must trigger that method. Up to you to figure out what that means but it could one of the following
1. A method, which really does nothing but call the first method above
2. A timer that runs at some periodic basis which then calls the above method.
3. Some other method, for example something like 'determine final grade' could call the first method above.
|
|
|
|
|
Hi Friends,
I want to calculate the distance between the HD Camera (industrial grade, small tiny one 16mm 1/2" 5MP) There are few formulas that I had found in the net. But not sure which to use.
Can someone suggest the correct formula to calculate the correct distance between the camera and the object. (preferable with the angle of inclination)
Thanks (in advance) for the help
Regards,
Dorairaju
|
|
|
|
|
Well without some frame of reference you can't.
|
|
|
|
|
If you don't have to details on the lens being used by the camera, it's impossible to calculate. You also need to calibrate the system with known points in the image. How accurate you get is also dependent on the object you're measuring distance to. You're not going to get an accurate distance to the center of a ball in the image.
Also, the accuracy is going to be rather poor if you're only using one camera and nothing else.
System.ItDidntWorkException: Something didn't work as expected.
C# - How to debug code[ ^].
Seriously, go read these articles.
Dave Kreskowiak
|
|
|
|
|
Place reference objects / lines in the "frame".
Wonder why there is a "ruler" on the side of doors leading in / out of banks?
Or the "lines on the road" used to measure speed / distance over time (from above).
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|