Is there anyway to stop TWebBrowser from clearing when I set Self.Parent := nil;?
When it gets to that line (which is necessary for a maximizing function), all the TWebBrowsers in my form clears. Why does it do that and what can I do to avoid this?
EDIT:
'Self' is the current form (in this case AnsForm) being shown on the MainForm.
Previous value of Parent is a Tab in MainForm.
I tried setting Self.Parent to something else but the same thing happens.
Reassigning the TWinControl.Parent property causes that control (in this case, your TForm object) to destroy its HWND (as a child HWND cannot exist without a parent HWND), and a new HWND is not created until the next time that control's Handle property is accessed (if no Parent is available by then, an exception is raised). When a control destroys its HWND, all of its child controls, and their child controls, and so on, destroy their own HWNDs as well. Without an HWND, there is nothing for a control to display, and any content stored in those HWNDs is lost. That is why your TWebBrowser objects get cleared.
Some components cache their current content in memory when their HWND is destroyed and then restore that content when a new HWND becomes available, but TWebBrowser does not (and cannot) do that. Your only option in this situation is to manually reload the current URL again. Otherwise, re-design your UI so the TWebBrowser objects do not reside on a parent control whose Parent property changes.
Related
I'm using Delphi 10.2.3. I have a descendent of a TCustomPanel for which I've overridden the MouseDown/MouseMove/MouseUp events to allow selection, dragging, custom painting, etc. Descendents of this panel will have various controls - TImage, TStaticText, TStringGrid, etc. - added dynamically at runtime, and some will have several such controls. The problem is that clicking anywhere in the panel that has some other child control in that location, that control gets the mouse events rather than the underlying panel.
Is there some way of having a control pass its mouse events to its parent, without having to dynamically assign OnMouseXxx handlers for every control added to the panel?
If a child is a TGraphicControl descendant, it does not have its own HWND in the OS, so for all practical purposes it doesn't exist to the OS. Thus, a click on the child will be sent to that child's immediate parent for processing. The VCL will then look for a child at the click coordinates, and if found then send the click to that child, otherwise handle it as a click on the parent. The methods involved in this lookup and delegation are not virtual or dynamic, so you can't override them, and they are invoked before the virtual Mouse(Down|Move|Up) methods are called on the determined target control.
If a child is a TWinControl descendant, it has its own HWND in the OS, thus a click on the child will be sent directly to that child for processing. The parent will not know the child has been clicked on, unless the parent has assigned appropriate event handlers to the child, or the child otherwise notifies the parent.
So, your TCustomPanel (which is a TWinControl descendant) can handle clicks transparently but only for non-windowed children, if it handles the WM_(L|M|R)BUTTON(DOWN|UP)/WM_MOUSEMOVE messages directly before passing them along to any inherited handler. That is not the case for windowed children. To intercept those messages, you would need to use a thread-specific mouse/message hook via the Win32 SetWindowsHookEx() function, looking for HWNDs that have your Panel's HWND as their (grand)parent.
I'd like to swap the subforms that appear in a container on my main form.
I've found a method of assigning one form as a subform of another here by making it's parent a container on the main form.
procedure TParentForm.EmbeddForm(AParent:TControl; AForm:TCustomForm);
begin
while AForm.ChildrenCount>0 do
AForm.Children[0].Parent:=AParent;
end;
This works pretty much as I expected it to for adding a subform; however, I can't seem to replace it once it's already been made a subform.
niling the parent for the subform doesn't seem to do the trick, nor does setting its parent to it self.
Is there a way to un-embed this subform from its container?
I'd rather not destroy an recreate these subforms all the time, just swap them out.
In Firemonkey, to embed a form, you should first encapsulate all your controls on a TLayout of some sort. Then, you can assign the parent of that layout to whatever container you need.
MyLayout.Parent := MyContainerInAnotherForm;
This way, you only have one variable (of the layout) to reference the entire "form". Bear in mind that FMX is widely based around layouts.
When you want to re-assign the original form, just assign that layout's parent back to its original container.
MyLayout.Parent := MyOriginalContainerForm;
I am bit curious about the two properties of a Delphi VCL control.
Each component has 2 properties as Owner and Parent at runtime. Can anyone help me to understand the difference between both? And how are they used by windows for displaying control or form?
Owner
Owner is a property introduced in TComponent, and Owner itself has type TComponent. The Owner is used primarily to manage the lifetime of designed components. That is, components that you place on the form designer (or indeed other design surfaces) and whose lifetime is managed entirely by the framework. The documentation says:
Indicates the component that is responsible for streaming and freeing this component.
When a form is created, the streaming framework parses the .dfm file and instantiates the components that are listed within. These components are typically created with Owner specified to be the form.
At the other end of a component's life is destruction. When a component is destroyed, it also destroys all of the components which it owns. Consider, for the sake of a concrete example, a TButton that sits on a TForm, placed there at design time. The streaming framework creates the button setting its Owner to be the form. The form, as a descendent of TComponent maintains a list of all the components that it owns. When the form is destroyed it walks over that list of owned components and destroys them. The button is destroyed in this manner.
There are many nuances to this:
Components don't need to be owned by the form. For instance, components created at run time are owned by whatever component you pass to the constructor.
Components need not have an owner, you can pass nil to the constructor. In this case, the programmer remains responsible for destroying the component.
Ownership can be changed during the lifetime of a component.
Because the streaming framework instantiates components, the constructor of TComponent is declared virtual:
constructor Create(AOwner: TComponent); virtual;
If you derived from a TComponent descendent, and you wish for that derived component to be placed on a design surface, then you must respect this virtual constructor. If you define a constructor in your TComponent descendent then it must override this virtual constructor.
It is worth pointing out that Win32 has a completely different concept of a window's owner, that should not be confused with the VCL concept of the same name. The Windows documentation says:
Owned Windows
An overlapped or pop-up window can be owned by another overlapped or
pop-up window. Being owned places several constraints on a window.
- An owned window is always above its owner in the z-order.
- The system automatically destroys an owned window when its owner is destroyed.
- An owned window is hidden when its owner is minimized.
Only an overlapped or pop-up window can be an owner window; a child
window cannot be an owner window.
In VCL terms, this concept is exposed by the PopupParent property. That property was introduced after Delphi 7 so will not be available to you. In Delphi 7, the framework sets the window owner, and gives no easy mechanism to override the framework's choice. If you do need to influence window ownership then you must override CreateParams and set Params.WndParent. Unfortunately, there are a number of problems with the VCL handling of ownership in Delphi 7 and it is sometimes necessary to grub around in these somewhat gory details.
To show how easy it is to get confused, the VCL documentation says:
WndParent: The window handle of the parent window. This is the same as the Handle property of the parent control.
This is simply wrong. For a top-level window, this is the owner rather than the parent.
Parent
Parent is a property defined in TControl and has type TWinControl. This property is used, broadly, to expose the Win32 concept of parent and child controls. The Windows documentation says:
A window can have a parent window. A window that has a parent is called a child window. The parent window provides the coordinate system used for positioning a child window. Having a parent window affects aspects of a window's appearance; for example, a child window is clipped so that no part of the child window can appear outside the borders of its parent window. A window that has no parent, or whose parent is the desktop window, is called a top-level window.
Essentially, the VCL Parent property maps directly onto the Win32 parent concept.
Note however, that Parent is defined in TControl. Now, TControl is not windowed, so a TControl is not a child control in the Win32 sense, because a Win32 window's children are themselves windows. So, a TControl with a defined Parent is a child in the VCL sense, known as a non-windowed child control. These non-windowed controls paint themselves as part of their parent's paint handlers. The canonical example of such a control is TLabel.
Note that when a windowed control, that is a TWinControl descendent, is destroyed, it also destroys all of its children.
A TWinControl descendent can enumerate its children using the ControlCount and Controls[] properties. These enumerate both windowed and non-windowed children.
I have a base form that all MDI child forms inherit from. The OnCreate makes a call to the notify the main form a new tab needs to be created. That tab's caption uses the MDIChild's caption.
When child window caption is changed at run-time, how can I capture or be notified so its corresponding tab can be updated with the new caption?
Looking for something like how the Main form's WindowMenu property works.
I have tried to capture the WM_MDISETMENU message in the main form, but no luck:
procedure TMainForm.WMMDISetMenu(var Msg: TWMDISetMenu); message WM_MDISETMENU;
I have researched high and low, but to no avail. Is there a real answer out there somewhere?
Since you've subclassed the child-window class already, all you have to do is handle the wm_SetText message. Call the inherited handler, and then update the tab caption.
I'm looking for the correct way in order to remove a TChart and deallocate all the memory.
I am using Delphi2007 with the standard TeeChart 7
I create the TChart programmatically:
var parentform: TForm;
begin
newchart:= TChart.Create(parentform);
newchart.Parent:= parentform;
...
Then, I want to remove only the chart from the form (not closing the form itself), but I get only that the chart becomes blank and stays on the form:
newChart.FreeAllSeries;
FreeAndNil(newChart);
if I use
NewChart.Parent := nil,
I don't see the chart anymore, but I think the TChart object still exists (until the parentform is destroyed). Is there a specific method for doing this?
Thank you
The most straightforward way to get rid of a TChart control, or just about any control, for that matter, is to call Free on it:
newChart.Free;
You can call FreeAndNil instead if you wish. That has the same effect of calling Free, but also sets the variable's value to nil. That's useful if you later test the variable's value to detect whether you still have access to the control. If you never reference the variable again, then FreeAndNil doesn't get you much.
The control will automatically free the other things it owns, such as the series you manually freed with FreeAllSeries. You don't need to free them yourself.
Merely clearing the control's Parent property does not free the control. You can prove that by re-assigning the Parent property and watching as the control re-appears on the screen. That wouldn't happen if the control had ceased to exist.
If the control remains visible on the screen after you free it, then you have other problems. Maybe the parent control hasn't repainted itself properly. You might try calling Refresh on the parent control. You might also have multiple controls visible. After all, the question's code creates two chart controls, so maybe one of them is still visible, and you've mistaken it for the control you destroyed.