tags:

views:

459

answers:

2

So, I have a form in VB which is visible except when the program is started silently, in which case the form is still loaded but the interactions are done programmatically. However, in a tiny number of cases I want forms created within the invisible form to be visible. What is the best way to accomplish this, ideally without creating new forms. Some ideas I've come up with but really don't like:

  • Creating a new form, passing that form's hwnd so that it is hosting any applicable windows. Really easy to do, probably pretty flexible. Still ugly.
  • Calling the visible form manually, pulling out the values, passing the data filled in by the user to invisible form. This solution is probably the easiest, but it's also a hack. It's an awful solution if it was used for more than a tiny number of forms, but it will probably.
  • Creating a new class, refactoring the forms to be triggered by events.
  • Setting the main form to be visible, but perverting its load function so that it doesn't actually display anything.
  • Not using the invisible form at all, refactoring code to better separate functionality of form from usage. In truth, this is largely already true, but I don't see a way to do this completely without some repetition with the way the classes are used, since I'll end up needing to have to different classes which include the same function. Probably the idealistic solution, though.

  • What I actually ended up doing: Sticking a shell execute call in the affected place which starts a new instance of a copy of the program which was compiled with different flags. This isn't nearly as bad as it sounds. Part of the problem with using any other solution is that ANY time I want to slap a different UI on my code, I need to create a new controller class which handles the relevant events differently. THe majority of the program's interface is already kept separated from its implementation, but creating a new UI requires me to add extra event handlers and whatnot to it. Admittedly it would probably only need about 3 event handlers handle prompting the user for input when events are triggered.

Edit: To an extent, I have mistated my problem: The issue is that I want to recycle part of my existing UI, but not all of it. Thus, making the parts I don't want the user to see be invisible but making the menus that appear visible. Decoupling the individual UI components will probably end up adding additional complexity to the program.

+1  A: 

the last option is preferable; after refactoring there should be no redundant code

fiddling with invisible forms is a pretty good indication of the need for refactoring!

Steven A. Lowe
The last optiojn is definitely better, but it's also probably a bad idea. Serious refactoring is not something I'd consider a good idea right before releasing a product.
Brian
@Brian: your call on that one; personally i would at least try it, chances are that the refactored code will be better than what you've got from the sound of it! [and remember to test thoroughly]
Steven A. Lowe
Part of the issue is that the relevant GUI code is called through the use of events, which doesn't work when done within a module.
Brian
A: 

I also recommend the last option. What I would do to safely implement with the least amount of changes is as follows.

Make a Backup or check everything into your source control and make a branch.

Make a new ActiveX DLL Project. I would name it some variant of UI_Controller

Note: A new project will force you to identify hidden dependencies within the EXE.

Have it reference everything the EXE is right now.

Have the EXE Project reference the UI_Controller.

Make a class for every major form in the application.

Copy the Minor Dialogs into the UI_Controller DLL.

For each form make a interface class in the UI_Controller DLL (The form will be implementing this)

Example if you have six major forms you then have six form interfaces and six controller classes

Go through the form for each procedure and event COPY the procedure over to the UI Controller Class and make it public.

Make sure each controller class has an Init procedure.

However there is an exception and that is if the code only deals with other controls and the form. For example Resize event rearranging the code. The reason for this is that is logic specific to that style of form. A different form or UI using the controller class will have a different resize algorithm so it makes no sense to move that code over. You will have to use your best judgment.

Go back to the Controller classes. Many of the procedures will need to reference elements on a specific form or fire events on a form. Write a subroutine header or function in your form interface class to allow that event to occur or retrieve the needed information. Note that if you find the same actions grouped together or the same series of information retrieved considered making just one subroutine instead of multiple.

Change any direct references to a form to a call on the interface.

Have your major forms implement each interface. Make a GlobalMultiUse Class in your UI Controller DLL that allows the EXE to register each form with it's controller. The register subroutines should only accept an object of the form interface. In your application initialize have pass each major form to the correct registration function before you do anything else.

Run your project and correct any errors. Note that the actual function of your software should remain the same as you should have only been copying and implementing.

When the software runs without errors. Check it into your source control or make a backup.

Start replace each procedure of ONE of your major forms with a call to corresponding method of your controller form. If any other form calls something on the form you are working on switch that call to the controller.

When you are done make sure the controller form initialization is called in your application initialization section. You make need to switch to using a Sub Main for this to work.

When this works properly work on another form. Keep doing this until all forms are converted.

Clean up anything that is not used.

Test and if it passes you are done.

What you have now is an application where you can rip off the current UI and replace with anything that implements the form interfaces. So you can have a invisible EXE, a unit test EXE, and your regular EXE.

You will have to decide what best for you based on the complexity of your application. You can have a partial conversion where you only give one or two forms a controller class.

RS Conley