Opened 6 years ago

Closed 5 years ago

#1762 closed task (done)

Move Hive Progress Control to MainForm.WindowsForms

Reported by: ascheibe Owned by: abeham
Priority: medium Milestone: HeuristicLab 3.3.7
Component: MainForm.WindowsForms Version: 3.3.7
Keywords: Cc:

Description (last modified by ascheibe)

Move the Hive Progress Control from Clients.Hive.Views.Progress to MainForm.WindowsForms.Controls. Also use it for the side bar and the samples panel.

Change History (29)

comment:1 Changed 6 years ago by ascheibe

  • Description modified (diff)

comment:2 Changed 5 years ago by ascheibe

r7582 moved ProgressView from Hive to MainForms

comment:3 Changed 5 years ago by ascheibe

r7583 added missing event deregistration

Last edited 5 years ago by ascheibe (previous) (diff)

comment:4 Changed 5 years ago by ascheibe

  • Status changed from new to accepted

comment:5 Changed 5 years ago by ascheibe

r7585 adapted MetaOpt to changes in trunk

Last edited 5 years ago by ascheibe (previous) (diff)

comment:6 Changed 5 years ago by mkommend

  • Milestone changed from HeuristicLab 3.3.x Backlog to HeuristicLab 3.3.7

comment:7 Changed 5 years ago by ascheibe

  • Owner changed from ascheibe to abeham
  • Status changed from accepted to reviewing

Hi Andreas, maybe you can have a look at this. I would be especially interested if the way this control is used makes sense.

comment:8 Changed 5 years ago by abeham

  • Owner changed from abeham to ascheibe
  • Status changed from reviewing to assigned

ProgressView.cs:

  • The assignment of null to the Progress property will cause a NullReferenceException RegisterProgressEvents
  • 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 the Canceled event instead of the Progress object? I wouldn't put the event in the control, but rather put a CancelRequested property in the IProgress object and then have the control listen to a Canceled 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 to IProgress and a BusyChanged 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; ReadOnly = true; 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.

comment:9 Changed 5 years ago by ascheibe

r8135 fixed activation of controls after displaying the progress is finished

comment:10 Changed 5 years ago by ascheibe

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

comment:11 follow-up: Changed 5 years ago by 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?

comment:12 Changed 5 years ago by ascheibe

r8146 fixed unit test

comment:13 in reply to: ↑ 11 Changed 5 years ago by ascheibe

Replying to 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?

comment:14 follow-up: Changed 5 years ago by 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.

comment:15 Changed 5 years ago by ascheibe

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.

comment:16 in reply to: ↑ 14 ; follow-up: Changed 5 years ago by ascheibe

Replying to 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.

comment:17 in reply to: ↑ 16 Changed 5 years ago by ascheibe

Replying to ascheibe:

Replying to 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...

Last edited 5 years ago by ascheibe (previous) (diff)

comment:18 Changed 5 years ago by ascheibe

r8159 removed blind disabling of controls

comment:19 Changed 5 years ago by ascheibe

r8160 removed unnecessary SetEnabledStateOfControls calls

comment:20 Changed 5 years ago by ascheibe

  • Owner changed from ascheibe to abeham
  • Status changed from assigned to reviewing

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.

comment:21 Changed 5 years ago by abeham

  • Owner changed from abeham to ascheibe

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 a Cancel(int timeout) function instead which raises a CancelRequested 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 a ContentView and the default view for IProgress
  • I changed the whole cancel/finish thing by introducing a state enum and a state changed event.
  • The ProgressView reacts to changes in the Progress 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 the CancelRequested event. If there are no event handlers a NotSupportedException is thrown. If the Cancel() function is called on the Progress, but CanBeCanceled is false nothing happens (I was throwing a NotSupportedException already, but then thought it's better to silently handle this case). The CanBeCanceled state might change during the life of a Progress 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, and ProgressView
  • 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.

comment:22 Changed 5 years ago by abeham

r8166: Fixed error detected in unit test

comment:23 Changed 5 years ago by ascheibe

  • Owner changed from ascheibe to abeham
  • Status changed from reviewing to readytorelease

Thanks for improving this. I have tested all views which use the progress view and found no problems. Looks good to me.

comment:24 Changed 5 years ago by ascheibe

  • Status changed from readytorelease to assigned

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.

comment:25 Changed 5 years ago by abeham

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 an InvokeRequired check missing to delegate the creation to the UI thread.

comment:26 Changed 5 years ago by abeham

  • Owner changed from abeham to ascheibe
  • Status changed from assigned to reviewing

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.

comment:27 Changed 5 years ago by ascheibe

  • Owner changed from ascheibe to abeham
  • Status changed from reviewing to readytorelease

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!

comment:28 Changed 5 years ago by abeham

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!

comment:29 Changed 5 years ago by mkommend

  • Resolution set to done
  • Status changed from readytorelease to closed
  • Version changed from 3.3.6 to 3.3.7
Note: See TracTickets for help on using tickets.