Wednesday, March 25, 2009

Dynamic Creation of IntraWeb Components

For the past couple of days I’ve been working on an experimental IntraWeb (a.k.a., "VCL for the Web") project. Since I typically generate user interfaces dynamically, I wanted to see how that worked with IntraWeb. On the surface, it’s fairly similar to the regular VCL, but there are some important differences.


Rule number one is that all components you create must have the Name property set. The regular VCL doesn’t require this for components created in code, but IntraWeb uses the Component.Name as part of the HTML id, and if the id isn’t set, the JavaScript generated by IntraWeb won’t work. So always set the Name.


Another thing to note is that the default property values don’t always make sense. If a component looks different when you dynamically generated at runtime versus when you drop it on an IntraWeb form in the designer, take a close look at the text DFM. You’ll probably find non-default property values set by the designer. If you replicate this in your code, you should end up with a component looks the same at runtime.


With all that said, here’s some simple code to add tabs at runtime to an existing tab control:



uses   Graphics, IWContainerBorderOptions;

procedure TfrmUIPageGroup.ClearPageControls;
var
iPage: integer;
begin
for iPage := Pred(TabControl.Pages.Count) downto 0 do begin
TObject(TabControl.Pages[iPage]).Free;
end;
end;

procedure TMyIntraWebForm.AddSomeTabs;
var
iPage, iTabTop, iTabHeight, iTabWidth: integer;
NewTab: TIWTabPage;
begin
// need to have a design-time page to copy sizing properties from
Assert(TabControl.Pages.Count > 0);
iTabTop := TIWTabPage(TabControl.Pages[0]).Top;
iTabHeight := TIWTabPage(TabControl.Pages[0]).Height;
iTabWidth := TIWTabPage(TabControl.Pages[0]).Width;
ClearPageControls;
for iPage := 0 to 2 do begin
NewTab := TIWTabPage.Create(TabControl);
NewTab.BorderOptions.NumericWidth := 0;
NewTab.BorderOptions.BorderWidth := cbwNumeric;
NewTab.BorderOptions.Style := cbsNone;
NewTab.BorderOptions.Color := clNone;
NewTab.Color := clWebWHITE;
NewTab.Top := iTabTop;
NewTab.Width := iTabWidth;
NewTab.Height := iTabHeight;
// WordPress changes the single quote to a typographic quote here, so cutting
// and pasting the next two lines won't work. Sorry; I haven't figured out how
// to stop that
NewTab.Name := 'DynamicPage' + IntToStr(iPage);
NewTab.Title := 'Page ' + IntToStr(iPage);
NewTab.Parent := TabControl;
// TabOrder determines the order of tabs (really!) in this control
// It must be set *after* Parent

NewTab.TabOrder := iPage;
end;
end;


Alignment may not work as expected, either. If I build on this example by creating a control, parenting it to the tab sheet, and setting the Align property to alClient, that control’s size or position doesn’t actually change.


Finally, there is the issue of layout managers. If you use TIWTemplateProcessorHTML or TIWLayoutMgrHTML, there will not be a placeholder in your template for your dynamically generated component. There is probably a way around this, but I have not fully investigated the issue yet. One possible solution would be to do the dynamic component generation inside of a custom IntraWeb control.

1 comment:

omonien said...

be aware, that you cannot create control sin Async events. They won't render in that way. (There is no mechanism to send a new control through an Async response)