Disclaimer: In this question tab, page, an dialog actually mean the same thing, sorry. My excuse: I am not sure what the final product should look like - a bunch of separate windows or all in one.
I am looking to improve an existing, hard-to-maintain Wizard baked with WinForms. I need to try to keep the look and feel about the same, but I need to clean up the internal logic. There are 5 dialogs in total, all of which get displayed one after another (after the Next button is clicked of course) inside one giant method. The way to jump back and forth is with ... 5 or 6 labels and GOTOs!
Now, this Wizard is linear, not a tree. From any one dialog/page you should be able to go to at most two others. Somehow doubly0linked list comes to mind. Right now there are 5 * 4 = 20
potential state transitions, while only 2*1 + 3*2 = 8
of them are valid. I do not have to use goto
s. They are usually evil, and in this case they are - it is hard to maintain this already... and I am thinking of adding another, 6th page. The reason why goto
s are in there are most likely because A) time pressure when v. 1.0 was being made, B) It was 5 years ago, so the best examples/tutorials on Wizards available at the time may not have been great.
Now, most pages of the Wizard ask for user's input. The subsequent pages are rendered depending on what the user have entered. If the user is say on page 3 and decided to ht a back button all the way to 1, and has not changed anything, and hits Next twice, then the state should not change. However, changing things on page x will generally invalidate stuff on pages x + 1 and further. There are exceptions to this, however, as some or all settings on a page x might depend on page x-1, x-2, etc., but pages x+1, x+2, etc do not depend on that x for some x.
I hope things are clear so far. We try to help the user by defaulting some stuff for them. The way things are stored is not great either. The dialog has read/write properties, from/to which stuff is copied to/from the actual controls. Then, in the main method, there is a "super-storage" which holds storages for each page. So, when the user is done with page x and hits next, stuff is first being copied from controls into storage that is local to the class, and then that stuff gets saved into the appropriate member of the super-storage.
Arrays (of dialogs/storages) and indices are not being used. There is separate yet similar "create&populate" logic for every goto destination (label). The dialogs objects get thrown away when the page is no longer displayed (they are not disposed, but every time they are to be shown, they get re-created and re-populated anew. I do not believe this is necessary, as only a single handle is needed, and after it has been shown, and closed, I believe it can be shown again in the same state, without having to re-populate controls. If wasting memory was the only issue, I would probably let things slide, but the thing is not very maintainable, so I might as well fix it all up.
I am thinking:
As you can see, there are a few challenges. I am hopeful, however, because thinking through a good Wizard design is a wheel that surely was [re-]invented before. Perhaps you can recommend an open source C# / mono app which come with a linear, yet non-trivial Wizard, so that I can take a peek at the implementation. Heck, maybe even Java/Swing will probably suit me as long as the Wizard is similar in nature. WPF would be yet another challenge for me, I do not want to have 2 problems instead of 1.
Let me know what you can think of. Should I just keep the gotos but clean up other parts as much as I can? Feel free to ask questions. Thanks,
-HG
I have some WInForms wizard code this is the general architecture:
On main wizard have StepList As List(Of baseWizardStep) .. list of all possible steps which is created when the wizard main form is.
Also Next Back and Cancel buttons.
As with Alex above, have a base class (baseWizard) main form with a panel that hosts a user control for the current step. Each wizard step itself is a user control (Inherits from baseWizardStep that inherits from UserControl) baseWizardStep has some built in events (ArriveAtStep, ValiateStep, LeaveStep etc..) Also in an InitliaseStep sub that takes main wizard as a parameter and stores a ref to main wizard form in property. All the steps access the main wizard to store their data in properties/objects/datasets etc.. (Storing of data is usually done on the LeaveStep event) The steps load data into their controls on the ArriveAtStep event.
I sometimes need to share wizard steps amongst many wizards, in which case the mainWizard implements an interface and the wizard steps cast their main wizard property to the interface to access/store data.
I have 2 events top control flow through the wizard.
If you don't handle any of these 2 events, then the wizard goes through from 1st in StepList to the last in order one step at a time.
Event 1: If you handle ShoudlStepOccur this event allows developer to decide should a step occur in the list of steps (so you can skip steps) This seems to cope with most wizards in an easy to undserstand metaphor. Event gives you the step and the diurection (Forward, back)
Event 2: (advanced control) THen there is the other event on the main form NavigateToStep, event gives you the step it was intending to go to, but you can change it to navigate to a completely differeent step. We use this to go round in a loop many times (Eg. In the Enrol on a course wizard, we go throurgh some steps of the wizard many times once for each course)
I also have a grid that lists all steps with the current step highlighted and that the user can click on to jump thourgh the wizard. I use the StepShouldOccur event to know which steps to disdplay in the step list at any time.
One gatchas: -When close wizard, you must dispose the steps that are no currenlty hosted in the panel and just lying around in the StepList becuase they will not releae their windows handles otherwise.
Not sure how much sense that's making so I'm going to leave it there.
Maybe one of these days I'll put this wizard code up on code project or something, I'm quite happy with how it works for us.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With