• We’re currently investigating an issue related to the forum theme and styling that is impacting page layout and visual formatting. The problem has been identified, and we are actively working on a resolution. There is no impact to user data or functionality, this is strictly a front-end display issue. We’ll post an update once the fix has been deployed. Thanks for your patience while we get this sorted.

Need some Delphi help

SunnyD

Belgian Waffler
Anyone that might be willing to answer a few questions for me specifically regarding Delphi and visual bits 'n pieces? This environment is driving me nuts.

#1 - I inherited a project, and it's ugly. I'm fairly new to Delphi, so I'm happy just plugging along, but there's certain things that are confusing me coming from a Microsoft background. Particularly runtime creation of UI components. Basically, right now, the app's UI is a bunch of panels hiding other components, which get hidden when certain data shows up and whatnot. Think tabbed pages for the most part - but each page can have multiple "layers" on it. Having something like 6 or 7 "layers" is making it difficult to modify the UI any. Is this normal?

#2 - Again, I'm used to MS-land, where you create an instance of an object and it stays until it's out of scope. However, Delphi happily lets you do something like the attached code. The thing is, once the procedure goes out to scope, the Frame (or form, or whatnot) is still happily there and running - with no way to access it (that I'm aware of). I know the answer is "don't do that", but honestly - why is this even allowed?!

#3 - I can't seem to do the same thing, except using *.Create instead of *.CreateForm. If I use *.Create, weird things happen depending on even weirder scenarios (like if the "variable" is local, global or class - only local works, and then only in certain situations).

#4 - If I create a component based on the action of another component, using "Self" as the parent (as everything I've seen suggests), the original component disappears, moves, or otherwise gets altered. Such as a form with a button, on clicking the button dynamically creating another button causes the original button to go away. This should not happen?

I've got tons more, but these are the first ones off the top of my head.

 
Originally posted by: SunnyD
Anyone that might be willing to answer a few questions for me specifically regarding Delphi and visual bits 'n pieces? This environment is driving me nuts.

#1 - I inherited a project, and it's ugly. I'm fairly new to Delphi, so I'm happy just plugging along, but there's certain things that are confusing me coming from a Microsoft background. Particularly runtime creation of UI components. Basically, right now, the app's UI is a bunch of panels hiding other components, which get hidden when certain data shows up and whatnot. Think tabbed pages for the most part - but each page can have multiple "layers" on it. Having something like 6 or 7 "layers" is making it difficult to modify the UI any. Is this normal?
In Delphi, panels are parents to other visual controls. The other controls appear to be contained inside the panel. My setting the panel's visibility, you can control what controls are available to the user or not.

Is this normal? Hmm, some developers do it. I can't say it's optimal. What is probably better is to partition out the application across forms. Being a form, you can create and display it on its own OR you can create the form and embed it in a panel if that's easier for the users. Implementing via forms is not really harder, but it gives you more flexibility.

With the code you showed, it looks like they used frames, which isn't bad either. Frames can be helpful.

#2 - Again, I'm used to MS-land, where you create an instance of an object and it stays until it's out of scope. However, Delphi happily lets you do something like the attached code. The thing is, once the procedure goes out to scope, the Frame (or form, or whatnot) is still happily there and running - with no way to access it (that I'm aware of). I know the answer is "don't do that", but honestly - why is this even allowed?!
Delphi, like C++ and other languages, is not a managed environment. This means when an object is created, it is your responsibility to free it, i.e. clean up after yourself. A variable pointing to an object is just that, a pointer, even though you don't syntactically write is as one (^TObject). The variable is just a pointer to an address. If the variable leaves scope and you didn't free the object, that just means your variable is free, but the memory it was pointing to is not.

Now that I explained that, there is one caveat: ownership. One object can own another object. When the owner is freed, all the objects it owns are freed. You specify the owner of an object when you create the object. You will see anything descending from TComponent takes an owner parameter in its Create method. Lastly, just because something is owned doesn't mean you can't free at some time before the owner is freed.

#3 - I can't seem to do the same thing, except using *.Create instead of *.CreateForm. If I use *.Create, weird things happen depending on even weirder scenarios (like if the "variable" is local, global or class - only local works, and then only in certain situations).
When you create a frame, I would make sure the owner you pass to the create method is the form that will hold it. You also need to make sure to assign its parent. (The parent being the visual control that will host or hold the frame. It might be the form itself.)

#4 - If I create a component based on the action of another component, using "Self" as the parent (as everything I've seen suggests), the original component disappears, moves, or otherwise gets altered. Such as a form with a button, on clicking the button dynamically creating another button causes the original button to go away. This should not happen?

When you create a component, you specify an owner in the Create. Assigning a parent comes later, maybe the next line. Your choice when. Not every object can own other objects. Descendants of TComponent are built with automatic ownership management. You can to spin your own management for any class less than that.

Just make sure you understand the difference between ownership and parentage. Two different things.

These links might be helpful.
http://www.marcocantu.com/md2005/
http://www.marcocantu.com/epascal/default.htm
http://www.marcocantu.com/edelphi/default.htm
 
Originally posted by: kamiller42
In Delphi, panels are parents to other visual controls. The other controls appear to be contained inside the panel. My setting the panel's visibility, you can control what controls are available to the user or not.

Is this normal? Hmm, some developers do it. I can't say it's optimal. What is probably better is to partition out the application across forms. Being a form, you can create and display it on its own OR you can create the form and embed it in a panel if that's easier for the users. Implementing via forms is not really harder, but it gives you more flexibility.

With the code you showed, it looks like they used frames, which isn't bad either. Frames can be helpful.

Sorry, I used frames in that example, because it looked like what I was supposed to be using. The app is currently written with a single form, which is composed of various controls. For example - a static UI toolbar type of thing, then a large area to host the data UI. It's the data UI which is composed of multiple "layers" of components and panels. This is where I was mostly confused - it works, fine. But in my experience in MS-land, I would generally use that area to dynamically load in something like a frame (hence my example above), and destroy that frame and create a different one when different data presentation is needed. The current implementation loads most of the UI panels with data, and then just hides all except the requested panel. This may be efficient from the speed standpoint, but not from the memory standpoint IMHO. And it really makes using the design editor a pain in the butt.

Now that I explained that, there is one caveat: ownership. One object can own another object. When the owner is freed, all the objects it owns are freed. You specify the owner of an object when you create the object. You will see anything descending from TComponent takes an owner parameter in its Create method. Lastly, just because something is owned doesn't mean you can't free at some time before the owner is freed.

Okay, that makes sense then. As I said, I'm used to things like MFC where when an object goes out of scope, it destroys itself generally.

When you create a frame, I would make sure the owner you pass to the create method is the form that will hold it. You also need to make sure to assign its parent. (The parent being the visual control that will host or hold the frame. It might be the form itself.)

...

When you create a component, you specify an owner in the Create. Assigning a parent comes later, maybe the next line. Your choice when. Not every object can own other objects. Descendants of TComponent are built with automatic ownership management. You can to spin your own management for any class less than that.

Just make sure you understand the difference between ownership and parentage. Two different things.

That's the thing. If I do something like in the main form:

TForm1.AddFrame(Sender: TObject) // Form1's Button1's OnClick handler

...
Frame.Create(Self);
Edit: I'm a retard - I just realized this should be Frame := TFrame2.Create(Self);
Frame.Parent(Self);
...

I get an access violation error - depending on where I put the variable for the frame. If I make it global or a class member, it dies. If I put it in the function that's actually creating it (local), it works, but doesn't create the appropriate frame - rather just does weird stuff with the component that send the call.
 
Originally posted by: SunnyD
Sorry, I used frames in that example, because it looked like what I was supposed to be using. The app is currently written with a single form, which is composed of various controls. For example - a static UI toolbar type of thing, then a large area to host the data UI. It's the data UI which is composed of multiple "layers" of components and panels. This is where I was mostly confused - it works, fine. But in my experience in MS-land, I would generally use that area to dynamically load in something like a frame (hence my example above), and destroy that frame and create a different one when different data presentation is needed. The current implementation loads most of the UI panels with data, and then just hides all except the requested panel. This may be efficient from the speed standpoint, but not from the memory standpoint IMHO. And it really makes using the design editor a pain in the butt.
Your intuition is correct. The strategy you describe works fine for small windows where its functionality is limited and fairly autonomous. It does not work well when designing the main form of an application. What I mentioned before about using forms and embedding them in host panels. Those panels could be TPanels, but it is much easier to use a TTabSheet in TPageControl with its TabVisible set to False.

I wrote an Outlook like framework where the form is embedded in a page control. Each module is a form. When the user clicks a module in the Outlook bar, the form is created and embedded in a TTabSheet. As the user leaves a module and enters another, I can choose whether to free the module's form or keep it resident.

This method allowed UI flexibility. The user can choose whether he prefers an MDI or SDI style interface. If he chooses SDI, I free the page control from the main form, adjust its size, and adjust the outlook bar. When the user clicks a module, I simply create the form and display it.

When I need to do maintenance on a module, I simply bring up the form, not go digging through panels.

Okay, that makes sense then. As I said, I'm used to things like MFC where when an object goes out of scope, it destroys itself generally.
C or C++ development with the MFC requires allocating memory and deallocating memory. It's not a managed environment. .NET is a managed environment, an environment where memory is automatically freed.

That's the thing. If I do something like in the main form:

TForm1.AddFrame(Sender: TObject) // Form1's Button1's OnClick handler

...
Frame.Create(Self);
Edit: I'm a retard - I just realized this should be Frame := TFrame2.Create(Self);
Frame.Parent(Self);

I get an access violation error - depending on where I put the variable for the frame. If I make it global or a class member, it dies. If I put it in the function that's actually creating it (local), it works, but doesn't create the appropriate frame - rather just does weird stuff with the component that send the call.

I assume when you say "frame," we are talking descendants of TFrame. See the code below. It is all it takes to create a frame and display it in a TPanel. Just change the parent assignment to embed on a form or page control.

In my code, lFrame is a local variable, so I won't be able to reference outside my code. If I want to reference the frame and what's on the frame, one way to do it is to keep a reference to the frame created. That can be achieved by a) use a class variable, b) keep the reference on a variable list, like a TObjectList, c) making the reference a global. Option C should be avoided. Global variables in general should be avoided.

Just noticed the Attach Code feature of this forum software is poor. Strips out carriage returns. Here is my code again...

procedure TForm1.btn1Click(Sender: TObject);
var
lFrame: TFrame2;
begin
lFrame :=TFrame2.Create(Self); // Self, the owner, is the main form.
lFrame.Parent := pnl1;
end;
 
lFrame :=TFrame2.Create(Self); // Self, the owner, is the main form.
As mention in my edit - this was the part that screwed me. It's all about understanding the language - coming from a C background the semantics of it screwed me up. I'm good now (for the most part) with this particular thing.
 
Back
Top