Friday, December 19, 2008

Delphi Wizard Framework - SetAllowDeletion

In my previous posts about my wizard framework, I discussed some of the original design decisions, as well as how to perform simple forward navigation.

The Delphi Wizard Framework is currently being hosted by google code hosting, which I briefly reviewed in a previous post.

One of the problems that I ran into quickly after starting this system was the issue of displaying a wizard form that doesn't allow user input but is there to show the user that something is being performed, to please wait patiently. The wizard framework as it navigates from one form to another automatically adds the last form displayed to the deleted queue and sends itself a message which will free all the forms in the queue. This works great and performing cleanup, but can cause access violations if someone wants to do something like the following:

begin
fWizMgr.NavigateToPage('TStatuspage');
if not Supports(fWizMgr.CurrForm,IWizPerformStatus,StatusForm) then
raise Exception.Create('form does not support IWizPerformStatus');
for ix := 0 to 100 do
begin
StatusForm.DoWork(ix);
StatusForm.ShowProgress(ix);
Application.ProcessMessages;
end;
fWizMgr.NavigateToPage('TResultspage',false);
end;

Now the intentions here are good, but the second the ProcessMessages is handled by the system the form which is executing this code is going to be freed, which will ultimately generate an exception. The solution to this is to use the framework method SetAllowDeletion to delay the deletion of the current form until later. Using it the correct method of the above routine should look like the following:

begin
fWizMgr.SetAllowDeletion(false);
try
fWizMgr.NavigateToPage('TStatuspage');
if not Supports(fWizMgr.CurrForm,IWizPerformStatus,StatusForm) then
raise Exception.Create('form does not support IWizPerformStatus');
for ix := 0 to 100 do
begin
StatusForm.DoWork(ix);
StatusForm.ShowProgress(ix);
Application.ProcessMessages;
end;
fWizMgr.NavigateToPage('TResultspage',false);
finally
fWizMgr.SetAllowDeletion(True);
end;
end;

Sunday, December 14, 2008

The Delphi Wizard Framework - Navigation

In a previous entry, I discussed some of the design decisions that allowed me to reach the current solution. One more decision which greatly impacted the design of the framework... I wanted to make sure that it could be data driven.

One of the projects that I was working on at the time was an editor for an XML datafile. For simplicity sake, the file had a structure something like the following:

<data>
<book>
<author>
<name>Steven King</name>
</author>
</book>
<movie>
<director>
<name>Stanley Kubrick</name>
</director>
</movie>
</data>

The goal of the program was to create an editor that could handle each child element. I created two forms, one named tBookWizardForm and another named tMovieWizardForm. Invoking these pages from the first page of the wizard looked something like the following:

procedure EditChild(ChildNode:IXmlNode);
var
NodeAware : IXmlNodeAware;
begin
fWizMgr.NavigateToPage('T'+ChildNode.NodeName+'WizardForm');
if Supports(fWizMgr.CurForm,IXmlNodeAware,NodeAware) then
NodeAware.SetXmlNode(ChildNode);
end;

Note in this example: The call to NavigateToPage automatically inserts the current page into the "to be deleted" list so there is no need to perform any manual cleanup.

The interface IXmlNodeAware actually exists in another unit which is shared by both wizard forms, it looks like the following:

type
IXmlNodeAware = interface
['{F78DF157-CBAF-48A3-BC4C-CE338514C898}']
Procedure SetXmlNode(Node:IXmlNode);
end

This would automatically perform the dispatch to the proper wizard form, if it ran into a node which it did not understand, it would generate an exception which would be handled and displayed by the wizard manager (displays a dialog that states that the requested form was not registered).

In my next post, I will cover SetAllowDeletion, and why one would need to use it.

Wednesday, December 10, 2008

Google Code Hosting -- Painless Open Source Management

Google Code Hosting really makes it simple to host open sourced projects. Especially if you like Subversion. In my few days with it I have tried just about all of the basic features and have found them easy to use and manage. Initial setup was very painless. The only desirable thing that is missing would be some method of adding to an rss/atom feed. Wait, thats what my blog and twitter are for.

The Delphi Wizard Framework - Design Decisions

The Delphi Wizard Framework, as I introduced in the previous post, was originally designed to replace a complex configuration system which was put together using a TNotebook component (yes, from the Delphi 1 days) on a single form. The first problem this faced was adding pages was more complex than it needed to be. The second problem was re-using entire pages in another project became a copy and paste fest, with plenty of room for missing a critical event.

My first goal was to take each page out of the single form, and create a form of its own that contains all of the logic just for that page. This worked very well, but I still had the problem where each form had to be added to the uses clause, and a circular reference back to the main containing form didn't feel right. Taking my research into using interfaces outside of COM, I developed a simple interface for the "FormManager" and another for the "FormPage", named IWizardManager and IWizardPage respectfully. When each page is displayed, the formmanager would pass forward its reference and the page would record this for later use.

With that out of the way, I then turned to how to let the system know about each form. I decided that the best way to address that problem would be to use the class registry system already existing in Delphi and call RegisterClass(classname) for each form that will be used. This cuts back on the need to add each unit to the uses clause and makes reuse almost as simple as adding the form to the project. I say almost, as the default behavior of Delphi when a form is added to the project is to auto-create it...this is not necessary with the framework as the framework will be responsible for the creation and deletion of each form.

In my next post, I will discuss form navigation.

Sunday, December 07, 2008

Introducing the Delphi Wizard Framework

I just posted my latest version of my wizard framework to http://code.google.com/p/delphiwizardframework/.  

This is some general purpose code that I have found myself using in many of my projects, as most of them at some point involve the need to step a user through many different "wizard" screens.  The concept behind it is quite simple, rather than code the button clicks on each form, instead use a container to hold the form which contains the buttons.  Each "wizard page" is a separate form, and can be invoked by the classname rather than a specific class.  This allows for the wizard to be data driven, and the loose binding into the project allows for easy reuse of wizard pages in other wizards.

For those who have used my tFrameManager component in Delphi 5, will see a few simularities.

Over the next few weeks, I'll be posting further instructions on how to put the system into action, along with some advanced tricks which are not currently evident in the sample project.

Wednesday, October 08, 2008

Fun with Generics.collections

I guess the best way to learn about new language features are to put them in practice, and with that I jumped into using the TDictionary to keep track of some xml nodes I was editing.  It was very easy to implement, but what got me stumped was then how to iterate thru the entire list and apply changes to all of the nodes at once.

Thats when I recalled the new for ... in ... syntax.  Using it was dirt simple.  Since my collection was created as tDictionary all I had to do was create a new variable of tPair and then use the for..in.. syntax to invoke a body of code against every IXmlNode in my tDictionary.


var
aPair : tPair<string,ixmlnode>
begin
for aPair in fDictionary do
aPair.Value.Attributes['dirty'] := '-1';
end;

In my option, this generates code which is much more readable and feels more natural.  Now, on to the next challenge.... finding a place in my project where I can put annoymous methods to practical use.

(EDIT: Updated syntax on variable declaration, as it was incorrect...seems the problem with generics is that they HIDE when copied to HTML.)

Tuesday, September 02, 2008

Google Chrome now available

Download and play with it now. http://tools.google.com/chrome/?hl=en-US

Very small footprint, very fast browser.

First look, browser has much more room compared to IE/Firefox out of the box....although my webmail provider (not Gmail of course) doesn't recognize the browser yet so it doesn't give me the rich experience I'm used too.