I am porting over a VCL component to FMX. 99% of of the code is pure object pascal, so that works just fine - but i have a method which creates a form, poulates it with buttons and a text-box, and this quite simply doesnt work under FMX.
The whole point to creating the form manually and then populating it from code was to make sure it compiled under both VCL, LCL and FMX; and that it also displays just fine under iOS, Android and whatever platform is used.
But i keep getting "Resource /classname/ not found", where /classname/ is whatever classname i give my temporary form class.
Something as simple as this produces the error:
type
TMyDialogForm = Class(TForm);
procedure TForm1.Button1Click(Sender: TObject);
var
LDialog: TMyDialogForm;
begin
LDialog := TMyDialogForm.Create(application.MainForm);
try
LDialog.Caption := 'Yahoo!';
finally
LDialog.Free;
end;
end;
Since the error involves resource, I suspect that it is looking for some type of layout data. I have just started playing around with FMX, and i did notice that different platforms allow for different layouts. But I must admit I expected it to fall-back to the default theme, no matter what platform you target.
So -- how exactly do i create a form by code, populate it and display ut using Firemonkey without running into this kind of bug? It works perfectly fine under VCL and LCL, but FMX keeps going on about resources.
Please dont tell me all forms MUST be designed?
As #RemyLebeau answered a similar question in the delphi forum (How to create a TForm at runtime?):
You are calling the TForm constructor that invokes DFM streaming. The reason
it does not fail in non-FMX apps is because TCustomForm.Create() filters
out TForm specifically so it won't try to stream. In FMX, TCommonCustomForm.Create()
filters out TCommonCustomForm instead of TForm, which is why your TForm in
FMX is trying to stream itself.
Since you know that there is no DFM, you should be using the non-DFM constructor
instead, in both VCL and FMX:
FRM := TForm.CreateNew(Application);
Related
I am working with this legacy Delphi app that is a candidate for being made more modern-looking with VCL styles. One form is causing a show-stopping performance issue.
This problem form dynamically creates controls from a specification. Not only that, but it doesn't create all the controls at once. It has a hierarchical organization, with controls drawn on groupboxes, and clicking a checkbox or radio button can cause the creation of a new nested groupbox with new controls in it. At least part of the time, all of the controls on the form get their states saved, get deleted, and then get re-created and their states restored. This works acceptably with old-school (think Windows NT 3.51) controls, but when VCL Styles are added this form can take over a minute to redraw itself.
I think what's happening is that styling is causing windows events that the legacy form-drawing code is responding to, causing it to repeat things it's already done. I'm wondering if there is a way to temporarily turn styling off to allow this form to draw itself completely before applying any changes that would be made by the style.
Am I doomed to disappointment?
Edit: This is with Delphi XE3.
Use Vcl.Themes.TStyleManager to toggle the style to use, either Windows (meaning normal style) or the Vcl style you have chosen as in following testcode:
uses ..., Vcl.Themes;
TForm11 = class(TForm)
// ...
private
StylesDisabled: boolean;
// ...
end;
procedure TForm11.Button1Click(Sender: TObject);
begin
StylesDisabled := not StylesDisabled;
if StylesDisabled then
TStyleManager.SetStyle('Windows')
else
TStyleManager.SetStyle('Amethyst Kamri');
end;
My Delphi program builds and compiles fine, however as soon as it is run in the debug mode, I get the following error;
Property ClientHeight does Not Exist
After looking through all of the .DFM file sources, in every form the code is there which is;
ClientHeight = 111
I'm not understanding where I go wrong here?
Your forms would have been saved with a newer version of Delphi. Unfortunately you will need to open each form in the IDE and save it again to clear the newer properties. There is a tool that can help you called DFMCheck (http://andy.jgknet.de/blog/ide-tools/dfmcheck/). This is an add on that will go through all of your forms and tell you about any problems with the forms that will only show up at runtime.
The reason why you are seeing the problem is this. Delphi saves the forms with all of the properties. It uses streaming to load the forms at runtime. When it tries to load a form with properties that don't exist then you will get an error like this as the streaming system is trying to set a property on a component when the property doesn't exist.
I know this is old thread, but hopefully this will help others that has this problem.
In cases like this where your class inheriteds from other and you know the properties are there, just re-publish them .Add a published section and add them again for example:
published
property ClientWidth;
property ClientHeight;
This then forces the compiler to compile these typeinfo for parts where the parents classes might have forward declarations and thus , resolve your issue.
Hope it helps somebody, took me 3 days to get to the solution eventually.
Same bug happens in modern Delphi (e.g. Rio 10.3) with FMX frames. After some investigation it revealed to be caused by tweaking TFrame inheritance. Example below:
type
// Declaration of custom type
TFrameEx = class(TFrame) .. {here I override a couple of methods} end;
// Causes a bug (described below)
TMyFrame = class(TFrameEx)
// Works fine
TMyFrame = class(TFrame)
Explanation:
Due to changed type, Delphi was unable to correctly choose TMyFrame type between FMX and VCL. So when the TMyFrame was opened in IDE it would ask to strip out FMX properties (non-existent in VCL, e.g. Size.Width) and add VCL properties (e.g. ClientWidth). When saved, that would make the TMyFrame buggy - it would show the "Property ClientHeight does Not Exist" error in runtime upon init.
Had similar bug. First you need a dfm file for your frame.
When you inherit a frame, the dfm file must starts with "inherited MyFrame: TFRameEx" and NOT "object MyFrame: TFrameEx". Without the inherited, when I did it, it was adding TForm properties and in the editor the frame had TForm events, in Delphi 10.3. So delphi really needs the dfm to find the right type. If you use the ide menu, it will be done automatically. New->Others->inheritables it will create the dfm with the inherited line, create a file with {$R *.dfm} in it and a line in the project source "unitname in '......pas' {MyFrame TFrame};" Or you can do it by hand.
As for the possibility of having multiple frames in the same unit, havent tested it myself but since the line is {$R *.dfm} it might be doable.
wanted it to be a comment for the solution of kromster but cant comment apparently.
In my case, I was inheriting TFrame that was save in Delphi 7, and I change the .dfm to resolve.
The first line: "object" frmMain: TfrmMain
I changed to "inherited", like this: inherited frmMain: TfrmMain
There is no any ShellListView/ShellTreeView component In Delphi FMX.
So, How to make an explorer like file manager in Delphi FMX?
Just simple as to count how many files/folders, certain type files..
and create text , read/write files.
How to deal file system in Delphi FMX?
I can not find System.IOUtils in Delphi FMX.
I think, maybe I totally mis-understand FMX framework.
Any hints?
Thanks!!!
Mitchell Hu
Firemonkey is a visual framework. People get so caught up on the way Embarcadero have marketed it that they often don't realize that much of what can be done with VCL can also be done with Firemonkey - it just requires a different approach and perspective.
With VCL, you'd deal with controls that are native to Windows itself. The VCL simply exposes these through the Delphi (and C++) languages.
With Firemonkey, the controls are created with vector shapes and so they're not native to a specific platform. Because of this, it's possible to create any sort of interface and run it on numerous platforms. At the same time as FMX was released, Embarcadero made their units crossplatform which means that most of the logic and data units included with Delphi can function on any platform which Firemonkey supports. Some functions are still platform specific but the majority is platform inclusive thanks to clever usage of IFDEFs.
With your example, the System.IOUtils isn't specific to a framework and it does work with Firemonkey. You do need to add it to your uses before you can work with it, but the unit includes everything you'd need to create a file manager. System.IOUtils.TDirectory contains routines for creating, renaming, deleting, traversing and manipulating the properties of directories. Many of the old example routines you can find on Google still work (you may want to look for 'recursive directory' examples). System.IOUtils.TFile provides a similar set of routines, but for files instead of directories while System.IOUtils.TPath provides them for paths instead.
System.IOUtils on XE3 Docwiki
System.IOUtils.TDirectory
System.IOUtils.TFile
Expansion 14 April 2013
Using the TTreeView with TTreeViewItem children would work to create the directory structure in the visual.
Using System.IOUtils.TDirectory.GetDirectories('C:\') would return a dynamic string array (TStringDynArray). Something like this may work (Note: Tested - Confirmed that the code example below works);
var
DirArraySize, i : Integer;
DirArray : TStringDynArray;
TreeItem : TTreeViewItem;
begin
DirArray := System.IOUtils.TDirectory.GetDirectories('C:\');
DirArraySize := Length(DirArray);
for i := 0 to DirArraySize-1 do
begin
TreeItem := TTreeViewItem.Create(TreeView1);
TreeItem.text := DirArray[i];
TreeItem.Parent := TreeView1;
end;
end;
I've now tested the code above and after a small correction (changing MyArray to DirArray on line 6 as it should have been) and can confirm that it outputs a list of folders/directories in C:\ into a TTreeView. Making it recursive wouldn't be too difficult and perhaps I'll expand on that in the near future.
I'm working with DirectShow in firemonkey, and i have a problem with outputting Video.
iVideoWindow.Put_Owner(Panel1.Handle);
I need handle of TPanel, to display video at it. But FMX controls have no handle. I know, that Firemonkey is not based on traditional windows and FMX do not provide this, but how to solve this problem? I have no idea, please, help me.
If you want to get a window handle as an HWND (Win32 API) type, you can now call this function:
WindowHandleToPlatform(form1.Handle).wnd
Put this in your uses clause:
uses
FMX.Platform.Win;
Note that just calling WindowHandleToPlatform(form1.Handle) will not work, you have to access its .Wnd property to obtain the Win32 handle.
Since this makes an application less portable, it is also a good idea to put {$IFDEF MSWINDOWS} around the code whenever doing this, and if you ever port to MacOS, you'll have to write separate code for that platform. Or, put this code into a separate unit that deals only with MSWindows-related stuff, and IFDEF that unit into your uses.
FmxHandleToHWND is marked deprecated now.
WindowHandleToPlatform will convert given FireMonkey handle to its platform dependent window handle (in your case a Windows handle).
Use Timage control, then you can assign your output to Image1.Bitmap.Handle. Its the only component that provides window Handle under FMX
To get handle of a panel, try this :
uses
FMX.Platform.Win;
var
Handle : HWND;
begin
Handle := TWinWindowHandle (Panel1).Wnd;
end;
I'm trying to put together some components for using OpenGL in RAD Studio 2009. I want to have multiple Rendering Contexts.
My idea is to have something like a "main OpenGL component", called GLMaster. It's a TFrame descendant so it should provide me with a DC. Also there's a component for a GLMonitor, acting as camera.
I create an OpenGL RC in the overriden GLMaster.Loaded inside an "if not (csDesigning in ComponentState) then"-clause. The DC for this I do not keep, it's stated in the RAD Studio help that you shouldn't:
"TWinControl.Handle
Do not refer to the Handle property during component creation or streaming. The underlying window does not exist until the first time the Handle property is referenced. When this occurs, the HandleNeeded method is called automatically."
I handle this by function pointers in components using GLMaster pointing to GLMaster.GetCurrentDC (returns a HDC).
During destruction GLMonitor wants to clean up some render-to textures and other OpenGL resources. When retrieving the DC for a call to wglMakeActive the function pointer is followed and execution jumps to GLMaster.GetCurrentDC. Pointer(Self) tells me that it's the same GLMaster that we created the "master RC" in during the streaming of the components. The property [GLMaster.]Handle is now invalid! If I however free the GLMonitor in the app form's OnClose (using simply GLMonitor_1.Free;) the Handle inside GLMaster.GetCurrentDC is valid end everything works.
I managed to see that the handle used in GLMaster.Loaded is not the same (!?!?) as after the initialization is done and the app form is showed to the user. When I found this I googled my problem and added an overriden CreateParams to add CS_OWNDC to the GLMaster component. A breakpoint there tells me that it's being executed.
Why does the VCL/Delphi silently provides the newly created components with other Handles, and thereby indirectly other DCs, even if I provide the OWNDC flag? Is there someway to get an "ALL components are now loaded and their properties are read from the .dfm file and"-message so I can run GLMaster.InitEverything inside the component?
Right now I'm thinking the fastest way onward is to put this InitEverything in the main form's OnShow handler. And then a call to GLMatser.CleanAll in the main form's OnClose. But I don't want any code in the form, that's the reason I created the components in the first place!
P.S.
Not that I think it matters for this particular problem but I use OpenGL 3.2 Core Profile.
I'm answering the "Why does the VCL/Delphi silently provides the newly created components with other Handles".
Why an control's Window Handle might change at run-time
Your TFrame (an TWinControl descendant) is placed on an other TWinControl, let's say a TForm. The parent container provides property wrappers around many settings, allowing us to easily make changes; Some of those changes can't be implemented without re-creating the windhow handle. You can get a long list of properties that cause this to happen by doing a search for RecreateWnd in Forms.pas, Controls.pas etc.
Examples of properties that call RecreateWnd to implement a change at run-time:
TScollBox.BorderStyle
TForm.BorderIcons (for MDI childs)
When your TFrame's parent needs to change Window Handle, your TFrame is going to have to change Window Handle as well. Your Delphi Control, no matter the descendense, needs to be handle this, and other controls have it worst: Controls that are implemented as wrappers around Windows controls need to save state and reload state (TEdit, TComboBox, ...)
What I think is going on
While streaming (loading your form) some control does something that needs a Window Handle before loading finishes. It's very likely your own code! After everything finishes loading the Form might need to re-create it's Handle, and this in turn causes your Frame's handle to be changed.
What you might want to override
Given the way the VCL works, you need to be prepared for your Window Handle to change. You should do a search in Controls.pas for the words Handle, HDC. If your control is so intimately linked to it's Window Handle and HDC, it's in your best interest to read up on this stuff.
Take a look at those routintes. Maybe you can sopot a better place to hook:
CreateHandle
CreateWnd
DestroyHandle
DestroyWnd
GetDeviceContext
If the title is the question, then no, Delphi does not ignore CS_OWNDC when creating a frame. Just tested with the below procedure with a frame on a form. When the flag is not specified, the line is drawn on itself again and again, resulting with a line from (0,0) to (10,10). When the DC is owned, the line extends from (0,0) to (50,50), no need to tell but the retrieved DC is same at all times.
procedure TFrame2.WmPaint(var Msg: TWMPaint);
var
i: Integer;
DC: HDC;
Pt: TPoint;
begin
inherited;
for i := 1 to 5 do begin
DC := GetDC(Handle);
GetCurrentPositionEx(DC, #Pt);
LineTo(DC, Pt.X + 10, Pt.Y + 10);
ReleaseDC(Handle, DC);
end;
end;
if your question is this:
ALL components are now loaded and their properties are read from the .dfm file and
The answer is:
Yes, just override the Loaded method of your component. It is called when all the references are valid (after dfm reading is finished).