Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Maintaining a list of TIWFrames and descendants of TIWFrame
#1
I have a single form and many frames that descend from TIWFrame:

TIWFrame->TIWFrameBase
TIWFrame->TIWFrameBase->TIWFrameGRID
TIWFrame->TIWFrameBase->TIWFrameCRUD

TIWFrameBase includes some new properties and a few IW components
TIWFrameGRID includes ado dataset grid, controls, buttons
TIWFrameCRUD includes ado dataset, edit controls, buttons

and would like to store these varying TIWFrames in a collection where they can be
retrieved as needed.  The program will know the index of the desired frame and it will know the desired frame's type. 

My initial thought was to create an array of TIWFrameBase, but complained when I tried to insert a TIWFrameGRID into the array. 

My gut tells me that this should be super simple and I'm a little ticked off I can't figure it out, even with Google.

TIA for your help.
Scott
Reply
#2
What was the complaint, and when (runtime/designtime)?
Did it sound like a name collision or something?

Dan
Reply
#3
i found that working dynamically with Frames in iw is the same as in a vcl app, but it's not so simple and it's not like using a regular component.
i think you would need to register the Frame as a Class.

in EVERY Frame that need it, just before your Frame's type, add this :
type TFrameClass = Class of TIWAppForm;

then as usual :
type
TFrameMyRegisteredFrame = class(TFrame)
...
private
...
public
end;

and before the "end." add :
initialization
RegisterClass(TFrameMyRegisteredFrame);


another way is to work with Interfaces, but i never tried it myself so maybe you can dig into that and see if it's better for you.

don't forget to set the Frame's Parent when you create it in runtime, and also assign different names if you have more then one of the same frame.

currently, my app have about 300 Forms and Frames, so i looked for a more efficient way.
after many tries, i got to the conclusion that it doesn't worth the pain, so i converted many of my frames to be Forms...
Reply
#4
Hi Scott,

If you show some sample code for both

1. the definition of your descendent frame (simplified if necessary)
2. The place where the code is blowing up and the exact compiler/runtime error you are getting.

That would be helpful. In principle, what you are doing is correct. You just have to make sure that there are difference instances of your frames in every session, and that you are not freeing the frames in one form and then trying to reuse the freed objects elsewhere.
Reply
#5
I have created two general use frames; one for a grid and one for a single record CRUD.

The two frames work hand in hand where I assign values to the grid frame properties and it's smart enough to get the data for the grid, create the desired columns with headings and provide navigation and single record CRUD when one of the CRUD events is fired.  The grid frame includes a property for setting the CRUD frame (TframeBaseEdit).  

Basically, by assigning the appropriate CRUD frame to the Grid frame, we have a reusable method of grid display + CRUD.

Currently I am using a separate strongly typed array to maintain the list of "live" frames, separated by type:


 TframeBase = class(TIWFrame)
 TframeEditBase = class(TframeBase)
 TframeGridBase = class(TframeBase)

 FframeArray: Array of TIWFrame;  
 FframeBaseArray: Array of TframeBase;  
 FframeEditBaseArray: Array of TframeEditBase;
 FframeGridBaseArray: Array of TframeGridBase;

Currently...when I create the various frames, I insert them into the appropriate array, based on the frame type.  

I make sure that all of the frames are invisible and have no daddy. This is one example where I have 4 separate arrays, each of a type that is descended from the previous.  A single array of type ??? would help.  Or, should I create an array of pointers and assign the frames to the array by reference?
Code:
 for i := 0 to iFrameCount - 1 do
   begin
     if Assigned(Self.FframeArray[i]) then
       begin
         (Self.FframeArray[i] as TIWFrame).Visible := False;
         (Self.FframeArray[i] as TIWFrame).Parent := nil;
       end;
     if Assigned(Self.FframeBaseArray[i]) then
       begin
         Self.FframeBaseArray[i].Visible := False;
         Self.FframeBaseArray[i].Parent := nil;
       end;
     if Assigned(Self.FframeGridBaseArray[i]) then
       begin
         Self.FframeGridBaseArray[i].Visible := False;
         Self.FframeGridBaseArray[i].Parent := nil;
       end;
     if Assigned(Self.FframeEditBaseArray[i]) then
       begin
         Self.FframeEditBaseArray[i].Visible := False;
         Self.FframeEditBaseArray[i].Parent := nil;
       end;
   end;

This code is showing where I initialize the frame (the end result of .Initialize is that the frame is rendered).  Clearly this would be better with a single array rather than an array for each type of frame.

Code:
     if Assigned(Self.FframeArray[iFrame]) then
       begin
         SetFrameParent(Self.FframeArray[iFrame], Self.regionMain);
       end;
     if Assigned(Self.FframeBaseArray[iFrame]) then
       begin
         SetFrameParent(Self.FframeBaseArray[iFrame], Self.regionMain);
         Self.FframeBaseArray[iFrame].Initialize;
       end;
     if Assigned(Self.FframeGridBaseArray[iFrame]) then
       begin
         SetFrameParent(Self.FframeGridBaseArray[iFrame], Self.regionMain);
         Self.FframeGridBaseArray[iFrame].Initialize;
       end;
     if Assigned(Self.FframeEditBaseArray[iFrame]) then
       begin
         SetFrameParent(Self.FframeEditBaseArray[iFrame], Self.regionMain);
         Self.FframeEditBaseArray[iFrame].Initialize;
       end;
Here is some of the code where I'm creating the various regular frames, grid frames  and their CRUD frames:

Code:
 SetLength(Self.FframeGridBaseArray, iFrameCount);
 SetLength(Self.FframeEditBaseArray, iFrameCount);
 SetLength(Self.FframeBaseArray, iFrameCount);
 SetLength(Self.FframeArray, iFrameCount);
 for i := 0 to iFrameCount - 1 do
   begin
       begin
         if (i = Ord(TApplicationFrames.afInvalidClientIPAddress)) then
           begin
             Self.FframeInvalidClientIPAddress := TframeInvalidClientIPAddress.Create(Self);
             Self.FframeInvalidClientIPAddress.Name := Include.C_FRAME_INVALIDCLIENTIPADDRESS;
             Self.FframeInvalidClientIPAddress.Request := Webapplication.Request;
           end
         else if (i = Ord(TApplicationFrames.afLogin)) then
           begin
             Self.FframeLogin := TframeLogin.Create(Self);
             Self.FframeLogin.Name := Include.C_FRAME_LOGIN;
             Self.FframeLogin.DefaultFrameName := Include.C_FRAME_MAIN;
           end
         else if (i = Ord(TApplicationFrames.afMain)) then
           begin
             Self.FframeArray[i] := TframeMain.Create(Self);
             Self.FframeArray[i].Name := Include.C_FRAME_MAIN;
             (Self.FframeArray[i] as TframeMain).LockIndicator := Self.IWCGJQLockIndicator1;
           end
         else if (i = Ord(TApplicationFrames.afPreferencesCompany)) then
           begin
             Self.FframeEditBaseArray[i] := TframeEditPreferencesCompany.Create(Self);
             Self.FframeEditBaseArray[i].Name := Include.C_FRAME_EDIT_PREFERENCES_COMPANY;
           end
         else if (i = Ord(TApplicationFrames.afUsersList)) then
           begin
             Self.FframeGridBaseArray[i] := TframeGridBase.Create(Self);
             Self.FframeGridBaseArray[i].Name := Include.C_FRAME_GRID_USERS;
             Self.FframeGridBaseArray[i].ViewData := Include.C_VIEW_DATA_LIST_USERS;
             Self.FframeGridBaseArray[i].EditFrame := TframeEditUser.Create(Self);
             Self.FframeGridBaseArray[i].EditFrame.Name := Include.C_FRAME_EDIT_USER;
             // Frame.UnInitialize will call EditFrame.RefreshGridProc if we assign it
             Self.FframeGridBaseArray[i].EditFrame.RefreshGridProc := Self.FframeGridBaseArray[i].RefreshGridData;
             // Assign EditFrame.EditData procedure to GridFrame.EditProc so that when you select a row it will display the EditFrame
             Self.FframeGridBaseArray[i].EditProc := (Self.FframeGridBaseArray[i].EditFrame as TframeEditUser).EditData;
           end
         else if (i = Ord(TApplicationFrames.afSecurityProfileList)) then
           begin
             Self.FframeGridBaseArray[i] := TframeGridBase.Create(Self);
             Self.FframeGridBaseArray[i].Name := Include.C_FRAME_GRID_SECURITY_PROFILES;
             Self.FframeGridBaseArray[i].ViewData := Include.C_VIEW_DATA_LIST_SECURITY_PROFILES;
             Self.FframeGridBaseArray[i].EditFrame := TframeEditSecurityProfile.Create(Self);
             Self.FframeGridBaseArray[i].EditFrame.Name := Include.C_FRAME_EDIT_SECURITY_PROFILE;
             // Frame.UnInitialize will call EditFrame.RefreshGridProc if we assign it
             Self.FframeGridBaseArray[i].EditFrame.RefreshGridProc := Self.FframeGridBaseArray[i].RefreshGridData;
             // Assign EditFrame.EditData procedure to GridFrame.EditProc so that when you select a row it will display the EditFrame
             Self.FframeGridBaseArray[i].EditProc := (Self.FframeGridBaseArray[i].EditFrame as TframeEditSecurityProfile).EditData;
           end
         else if (i = Ord(TApplicationFrames.afEditPassword)) then
           begin
           end
         else if (i = Ord(TApplicationFrames.afExportData)) then
           begin
             Self.FframeArray[i] := TframeExportData.Create(Self);
             Self.FframeArray[i].Name := Include.C_FRAME_EXPORT_DATA;
             (Self.FframeArray[i] as TframeExportData).LockIndicator := Self.IWCGJQLockIndicator1;
           end;

       end;
     if Assigned(Self.FframeArray[i]) then
       begin
         Self.FframeArray[i].Parent  := nil;
         Self.FframeArray[i].Visible := False;
         Self.FframeArray[i].Height  := Self.regionMain.Height;
         Self.FframeArray[i].Width   := Self.regionMain.Width;
         Self.FframeArray[i].Align   := TAlign.alClient;
       end;
     if Assigned(Self.FframeBaseArray[i]) then
       begin
         Self.FframeBaseArray[i].Parent  := nil;
         Self.FframeBaseArray[i].Visible := False;
         Self.FframeBaseArray[i].Height  := Self.regionMain.Height;
         Self.FframeBaseArray[i].Width   := Self.regionMain.Width;
         Self.FframeBaseArray[i].Align   := TAlign.alClient;
         Self.FframeBaseArray[i].LockIndicator := Self.IWCGJQLockIndicator1;
       end;
     if Assigned(Self.FframeGridBaseArray[i]) then
       begin
         Self.FframeGridBaseArray[i].Parent  := nil;
         Self.FframeGridBaseArray[i].Visible := False;
         Self.FframeGridBaseArray[i].Height  := Self.regionMain.Height;
         Self.FframeGridBaseArray[i].Width   := Self.regionMain.Width;
         Self.FframeGridBaseArray[i].Align   := TAlign.alClient;
         Self.FframeGridBaseArray[i].LockIndicator := Self.IWCGJQLockIndicator1;
       end;

The short of it is this:  How do I maintain a single list of these various frame types?

Array of objects?
Array of pointers?
Crayola and Big Chief Pad? Big Grin

I appreciate y'all letting me have a few spin cycles from your personal beanie propeller to help me figure this out.

Thanks,
Scott

(05-04-2018, 04:51 PM)DanBarclay Wrote: What was the complaint, and when (runtime/designtime)?  
Did it sound like a name collision or something?

Dan

myFrames: Array of TframeBase;
...
   myFrames[i] := TframeGridBase.Create(Self);   // << This assignment does not throw an exception, but the parent type (TframeBase) causes me to lose the added functionality in TframeGridBase
Reply
#6
(05-04-2018, 04:51 PM)EitanArbel Wrote: i found that working dynamically with Frames in iw is the same as in a vcl app, but it's not so simple and it's not like using a regular component.
i think you would need to register the Frame as a Class.

in EVERY Frame that need it, just before your Frame's type, add this :
type TFrameClass = Class of TIWAppForm;

then as usual :
type
 TFrameMyRegisteredFrame = class(TFrame)
...
 private
...
 public
end;

and before the "end." add :
initialization
 RegisterClass(TFrameMyRegisteredFrame);


another way is to work with Interfaces, but i never tried it myself so maybe you can dig into that and see if it's better for you.

don't forget to set the Frame's Parent when you create it in runtime, and also assign different names if you have more then one of the same frame.

currently, my app have about 300 Forms and Frames, so i looked for a more efficient way.
after many tries, i got to the conclusion that it doesn't worth the pain, so i converted many of my frames to be Forms...

Thank you for the advise Smile

I've had a high degree of success using frames for pretty much everything; assigning the frames to a single form. 

After some thought, I figured out what my question actually is:

Since I have multiple frames, all of differing types, does my array need to be of pointers where I would assign the frame to the array element by reference?  The system "knows" the individual array element's Type (TframeBase, TframeEditBase, TframeGridBase, etc...) so I can cast the array element using the appropriate frame type.

(05-04-2018, 04:51 PM)EitanArbel Wrote: i found that working dynamically with Frames in iw is the same as in a vcl app, but it's not so simple and it's not like using a regular component.
i think you would need to register the Frame as a Class.

in EVERY Frame that need it, just before your Frame's type, add this :
type TFrameClass = Class of TIWAppForm;

then as usual :
type
 TFrameMyRegisteredFrame = class(TFrame)
...
 private
...
 public
end;

and before the "end." add :
initialization
 RegisterClass(TFrameMyRegisteredFrame);


another way is to work with Interfaces, but i never tried it myself so maybe you can dig into that and see if it's better for you.

don't forget to set the Frame's Parent when you create it in runtime, and also assign different names if you have more then one of the same frame.

currently, my app have about 300 Forms and Frames, so i looked for a more efficient way.
after many tries, i got to the conclusion that it doesn't worth the pain, so i converted many of my frames to be Forms...

Thank you for the advise Smile

I've had a high degree of success using frames for pretty much everything; assigning the frames to a single form. 

After some thought, I figured out what my question actually is:

Since I have multiple frames, all of differing types, does my array need to be of pointers where I would assign the frame to the array element by reference?  The system "knows" the individual array element's Type (TframeBase, TframeEditBase, TframeGridBase, etc...) so I can cast the array element using the appropriate frame type.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)