Closed HeuristicLab-Trac-Bot closed 12 years ago
Hi Andreas, maybe you can have a look at this. I would be especially interested if the way this control is used makes sense.
ProgressView.cs:
- The assignment of null to the
Progress
property will cause a NullReferenceExceptionRegisterProgressEvents
- Don't use "this." to reference to instance variables in general
- The cancel button doesn't call
OnCanceled
... I don't understand this. Why does the view raise theCanceled
event instead of theProgress
object? I wouldn't put the event in the control, but rather put aCancelRequested
property in theIProgress
object and then have the control listen to aCanceled
event of the progress and only then close the window. There's no reason to close this view when the operation has not been successfully cancelled.- I don't like the self disposing in
Finish()
, somebody created this object and that instance is responsible for destroying it.- You could also add a
Busy
property toIProgress
and aBusyChanged
event instead of Finished. That way you know whether a progress has started, is in progress, has finished, or is not in progress. You can then have the control just attached to a parent view and never care for it again. You just give it a new progress object whenever you have one.- You shouldn't need to set all control's enabled property to false. Setting the parent view to
Locked # true; ReadOnlytrue;
should set the enabled state of the parent's children. Why does it not work like this?- Decide if you throw an
ArgumentNullException
in case parentView is null or handle that case.IProgressReporter.cs:
- Needs only to return a progress object, the rest can be moved to the progress object itself. It's a bit strange to have part of the feedback coming directly from the ProgressReporter and other part through the IProgress object. Why not put all in one place?
I think you should implement the Cancel stuff thoroughly and also support that in Hive. I like to be able to Cancel things that are running for very long.
I improved the code a little in r8111.
r8135 fixed activation of controls after displaying the progress is finished
r8145 implemented review comments:
- removed self disposing. The progress view now reacts if a progress is set and Finish() is now called on the progress object and not the view.
- Moved Cancel event from ProgressView to Progress
- throw ArgumentNullException if the parent view is null
Hmm looking over this again, I think we should not let the progress control do the parent control disabling/enabling. The parent control should do this by itself. What do you think?
Replying to [comment:11 abeham]:
Hmm looking over this again, I think we should not let the progress control do the parent control disabling/enabling. The parent control should do this by itself. What do you think?
I thought that this is a very cool feature because the parent view doesn't need to know anything about the progress view and therefore we don't have to change the code of the parent views. Why do you think the parent control should do the disabling of the controls?
Yes it's a convenience, but then the progress control just assumes a lot of things of the parent, foremost that it should actually be disabled, that every control should be disabled and that every control should then be enabled again. Won't it also enable controls afterwards that shouldn't be enabled due to the state of the parent?
I mean this
foreach (Control c in this.parentView.Controls) c.Enabled = false;
is not really nice code. At least you would have to set the parentView's Locked and ReadOnly property so that proper locking is in place as defined by the parentView. Currently, this is just "disable everything I don't care" code. And this code
foreach (Control c in this.parentView.Controls) c.Enabled = true;
just enables everything, even stuff that should probably be disabled.
r8156 Removed IProgressReporter and changed the Hive Job Manager accordingly. I think that IProgressReporter is just a result of the strange way how RefreshableJob, HiveClient and the RefreshableHiveJobView work together. It's not used in any other view so I removed it.
Replying to [comment:14 abeham]:
Yes it's a convenience, but then the progress control just assumes a lot of things of the parent, foremost that it should actually be disabled, that every control should be disabled and that every control should then be enabled again. Won't it also enable controls afterwards that shouldn't be enabled due to the state of the parent?
I mean this
foreach (Control c in this.parentView.Controls) c.Enabled = false;
is not really nice code. At least you would have to set the parentView's Locked and ReadOnly property so that proper locking is in place as defined by the parentView. Currently, this is just "disable everything I don't care" code. And this code
foreach (Control c in this.parentView.Controls) c.Enabled = true;
just enables everything, even stuff that should probably be disabled.
Ok, now i see your point, you are right. The problem is that the progress view can't call SetEnabledStateOfControls on the parent. That's the reason why all users of the progress view call it themselves after the progress Finish call. I will remove the disabling of all controls. Should i also remove the setting of Locked and ReadOnly from the progress view? I'm not sure if there is a use case where the controls of the parent view should be enabled, especially because the progress view probably hides some controls of the parent anyway.
Replying to [comment:16 ascheibe]:
Replying to [comment:14 abeham]:
Yes it's a convenience, but then the progress control just assumes a lot of things of the parent, foremost that it should actually be disabled, that every control should be disabled and that every control should then be enabled again. Won't it also enable controls afterwards that shouldn't be enabled due to the state of the parent?
I mean this
foreach (Control c in this.parentView.Controls) c.Enabled = false;
is not really nice code. At least you would have to set the parentView's Locked and ReadOnly property so that proper locking is in place as defined by the parentView. Currently, this is just "disable everything I don't care" code. And this code
foreach (Control c in this.parentView.Controls) c.Enabled = true;
just enables everything, even stuff that should probably be disabled.
Ok, now i see your point, you are right. The problem is that the progress view can't call SetEnabledStateOfControls on the parent. That's the reason why all users of the progress view call it themselves after the progress Finish call. I will remove the disabling of all controls. Should i also remove the setting of Locked and ReadOnly from the progress view? I'm not sure if there is a use case where the controls of the parent view should be enabled, especially because the progress view probably hides some controls of the parent anyway.
Ah, i just saw that SetEnabledStateOfControls gets called when ReadOnly/Locked is set...
So if the parent view now wants to lock if the progress view is shown, it has to implement this in the SetEnabledStateOfControls like the RefreshableHiveJobView does it now.
There were some points that weren't very appealing. I think some things were more complicated now and not intuitve. So I rewrote some parts of this feature in r8165 and hope to have improved it:
- I have no intuitive explanation for what
void SignalSuccesfulCancelation()
should do. I didn't find any references to it, so I removed it.- Reviewing the cancellation options, I rethought abuot the
CancelRequested
property and think that in the end it's not the right way to handle this. I introduced aCancel(int timeout)
function instead which raises aCancelRequested
event to which there must be at least one subscriber (an exception is thrown in case the event handler is null). Maybe the timeout is overengineering this, I'm not sure.- We shouldn't have setters in
IProgress
, but rather in the concrete object. Only the instance that holds the concrete object should be allowed to state updates and progress.- The
ProgressView
now is aContentView
and the default view forIProgress
- I changed the whole cancel/finish thing by introducing a state enum and a state changed event.
- The
ProgressView
reacts to changes in theProgress
object- Cancellation works such that there is a property
CanBeCanceled
which determines if the operation is cancelable. If the process can be canceled, there must be at least one event handler for theCancelRequested
event. If there are no event handlers aNotSupportedException
is thrown. If theCancel()
function is called on theProgress
, butCanBeCanceled
is false nothing happens (I was throwing aNotSupportedException
already, but then thought it's better to silently handle this case). TheCanBeCanceled
state might change during the life of aProgress
object.- I also added some API doc comments to the
IProgress
interface- I noticed a problem with defining a progressView local variable in some views: The progessView will not be disposed! Since the progressView is not part of the Controls collection when it's not visible, the Dispose() method of the progressView will never be called. So I changed some code to dynamically create
ProgressView
instances (using (var view = new ProgressView()) { ... }
). In the other views, where the lifetime of the progressView spans over several method calls, I added some code to make sure the controls are disposed correctly. I used the(De)RegisterContentEvents
methods for this. This is not optimal, but I don't know of a better way for now.
- We should think if it maybe would be better not to add/remove from the parent controls, but I can understand this is more convenient than going to the annoying forms designer which sometimes doesn't display all the controls from the solution.
- I updated all views that made use of the
IProgress
,Progress
, andProgressView
- Last but not least I removed the requirement for a
ParentView
and (hopefully) handled the case where no parent view is defined. I wanted this because, if you want to use this in a dialog for example there is no (mainform-) view, but just a Form or a UserControl.- I also changed alignment to
Anchor.None
so that the control always remains in the middle of the view when it is resized. Otherwise it would sit in the same position.Please take a look at the changes. I would also beg you to test if I broke anything. I tried it with a
RefreshableHiveJobView
where it seemed to work.
Thanks for improving this. I have tested all views which use the progress view and found no problems. Looks good to me.
I have forgotten to test the OptimizerHiveTaskView. If you click e.g. on the stop button to stop an Hive task the following exception is thrown: ArgumentException: Controls created on one thread cannot be parented to a control on a different thread.
Maybe this happens in those cases where the control is created in a
using
construct to surround the body of a method. It could be that there is anInvokeRequired
check missing to delegate the creation to the UI thread.
r8181: Fixed progressView creation. I have not tested it, but I think this was the cause. I also found a similar situation in the OKBUploadExperimentView.
I have tested the changes and fixed the activation/deactivation of the progress view in r8186. Thanks a lot for your input and help with this ticket!
One last change
r8187: Moved the
ProgressView
to the folder Controls instead of Views.I had the impression that it fits better in the folder where the ViewHost and other views/controls reside. In the Views folder there are only the View, ContentView, and AsynchronousContentView bases classes.
Thx for fixing this!
Issue migrated from trac ticket # 1762
milestone: HeuristicLab 3.3.7 | component: MainForm.WindowsForms | priority: medium | resolution: done
2012-01-17 13:00:24: @Shabbafru created the issue