views:

134

answers:

5

I am working on a VB.NET batch PDF exporting program for CAD drawings. The programs runs fine, but the architecture is a mess. Basically, one big function takes the entire process from start to finish. I would like to make a separate class, or several, to do the exporting work.

Here's the problem:
Sometimes the pdf file which will be created by my program already exists. In this case, I would like to ask the user if he/she would like to overwrite existing PDFs. I only want to do this if there is actually something which will be overwritten and I only want to do this once. In other words, "yes" = "yes to all." It seems wrong to have the form (which will be calling this new PDF exporting class) figure out what the PDF files will be called and whether there will be any overwrites. In fact, it would be best to have the names for the PDF files determined as the individual CAD drawings are processed (because I might want to use information which will only become available after loading the files in the CAD program in the background).

Here's the question:
How should I handle the process of prompting the user? I would like to keep all GUI logic (even something as simple as a dialog box) out of my PDF exporting class. I need a way for the PDF exporting class to say, "Hey, I need to know if I should overwrite or skip this file," and the form class (or any other class) to say, "Um, ok, I'll ask the user and get back to you."

It seems there ought to be some pattern to handle this situation. What is it?

Follow-ups:

Events: It seems like this is a good way to go. Is this about what the code should look like in the PDF exporting class?

    Dim e As New FileExistsEventArgs(PDFFile)
    RaiseEvent FileExists(Me, e)
    If e.Overwrite Then
        'Do stuff here
    End If

A crazy idea: What about passing delegate functions to the export method of the PDF exporting class to handle the overwrite case?

A: 

So the appropriate method on your class needs an optional parameter of

[OverwriteExisting as Boolean = False]

However your form will need to handle the logic of establishing whether or not a file exists. It seems to me that this would not be a function that you would want encapsulated within your PDF export class anyway. Assuming that your form or other function/class ascertains that an overwrite is required then the export methos is called passing True as a Boolean to your export class.

Charlie
+1  A: 

Your PDF making class should raise an event. This event should have an eventargs object, which can have a boolean property called "Overwrite" which the GUI sets in whatever fashion you want. When the event returns in your PDF class you'll have the user's decision and can overwrite or not as needed. The Gui can handle the event anyway it likes.

Also, I commend you for working to keep the two seperate!

C. Ross
Listeners should not change the behavior of the sender. I know this is violated in "Cancelable" events, but it's just a hack.
David Lay
Why not? As long as it's well documented, what is the issue?
C. Ross
@David LayIf this isn't the right way of doing this, what is? Is there another way to use events that would be better or would you propose something else instead of events?
Daniel Straight
+4  A: 

You could use an Event, create a custom event argument class with a property on it that the application can call. Then when your app is handling the event prompt the user and then tell the exporter what to do. I'm a c# guy so let me give you a sample in there first:

void form_Load(object sender,EventArgs e)
{
   //We are subscribing to the event here. In VB this is done differently
   pdfExporter.FileExists+=new FileExistsEventHandler(pdfExporter_fileExists)
}

void pdfExporter_fileExists(object sender, FileExistsEventArgs e)
{
   //prompUser takes the file and asks the user
   if (promptUser(e.FileName)) 
   {
   }
}
JoshBerke
Should the code in the pdf exporter look something like this then:<br />Dim e As New FileExistsEventArgs(PDFFile)<br /> RaiseEvent FileExists(Me, e)<br />If e.Overwrite Then...
Daniel Straight
Yep exactly! In C# we need to validate that fileExists is not null but I'm guessing VB handles this for you. This type of system should work preety well
JoshBerke
A: 

You could do a two phase commit type of thing.

The class has two interfaces, one for prepping the filenames and filesystem, and another for doing the actual work.

So the first phase, the GUI calls the initialization interface, and gets a quick answer as to whether or not anything needs over-writing. Possibly even a comprehensive list of the files that will get over-written. User answers, the boolean variable in the other responses is known, and then the exporter gets to the real work, and the user can go do something else.

There would be some duplication of computation, but it's probably worth it to get the user's part of the operation over with as soon as possible.

Sean Cavanagh
But what if part of the name of PDF file can only be determined after opening the CAD file? There is no way to get a quick answer on that, and I certainly don't want to load each CAD file twice.
Daniel Straight
I was guessing that opening the CAD file and analyzing it for the filename would be an order of magnitude faster than doing the full transform to PDF. Only you know for sure, but I would encourage you to think about it. If faster, it's worth the extra global time to let the user do something else.
Sean Cavanagh
A: 

You can't keep the GUI stuff out of the PDF Exporting Code. but you can precisely define the minimum needed and not be tied to whatever framework you are using.

How I do it is that I have a Status class, and a Progress class. The two exist because Status is design to update a status message, and the Progress Bar is designed to work with a indicator of progress.

Both of them work with a object that has a class type of IStatusDisplay and IPrograssDisplay respectfully.

My GUI defines a object implementing IStatusDisplay (or IProgressDisplay) and registers as the current display with the DLL having Status and Progress. The DLL with Status and Progress also have two singleton called NullStatus and NullProgress that can be used when there is no UI feedback wanted.

With this scheme I can pepper my code with as many Status or Progress updates I want and I only worry about the implementations at the GUI Layer. If I want a silent process I can just use the Null objects. Also if I completely change my GUI Framework all the new GUI has to do is make the new object that implements the IStatusDisplay, IProgressDisplay.

A alternative is to raise events but I find that confusing and complicated to handle at the GUI level. Especially if you have multiple screen the user could switch between. By using a Interface you make the connection clearer and more maintainable in the longe.

EDIT You can create a Prompt Class and a IPromptDisplay to handle situation like asking whether you want to overwrite files.

For example

Dim P as New Prompt(MyPromptDisplay,PromptEnum.YesNo)
'MyPromptDisplay is of IPromptDisplay and was registered by the GUI when the application was initialized

If PromptYesNo.Ask("Do you wish to overwrite files")= PromptReply.Yes Then
    'Do stuff here
End If
RS Conley
I really don't understand how this relates to answering the question of whether the files should be overwritten.
Daniel Straight
Isn't the default VB.NET MessageBox() the same thing as Prompt?
HardCode
@HardCode In this trivial example it could be. But putting it behind a interface means you can replace message box with a customized prompt with say your company logo or with validation.
RS Conley