How to make an explorer like file manager in Delphi FMX? - delphi

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.

Related

Create Firemonkey form and populate by code

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);

FMX: Handle of controls

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;

How can I get a dataset of in-memory objects?

Does anyone know of a TDataset descendant that works with Generics and RTTI, so that I can write code like this, and make use of data-aware components in the GUI? :
...
ds:TDataset<TPerson>;
...
procedure DoStuff;
begin
ds:=TDataset<TPerson>.create;
ds.add(TPerson.Create('A.','Hitler',77));
ds.add(TPerson.Create('O.','Bin Laden',88));
end;
This should be possible. The fielddefs can be created via RTTI because the exact type of the data is known. Values can also be automatically marshalled back and forth, so you can both view and edit data that's in a class or a record.
I hate having to write a lot of useless marshalling code, while the required information for that is available via RTTI already.
Or maybe somebody once wrote some sort of TEnumerable <-> TDataset adapter?
Does something like that exist, or should I start writing one?
...
The closest thing that I could find is an (excellent!) example by Marco Cantu, from Mastering Delphi 7, but the code itself doesn't make use of new language features like generics, the new RTTI system, or attributes, and it doesn't work with Unicode delphi. TDataset has changed since D7 too.
The TAureliusDataSet included in TMS Aurelius comes very close to that.
Take a look at EverClassy Dataset from Inovativa at www.inovativa.com.br/public.
another one is Snap Object Dataset http://digilander.libero.it/snapobject/
DotNet4Delphi by A-Dato Scheduling Technology from the Netherlands is good for you.
Quotes:
From Torry's Delphi
Hook up any collection to your data aware controls.
DotNet4Delphi implements many .Net collection classes, including
generic types like List<> and Dictionary<>. Different from their
Delphi counterpart is that our generic collections also implement the
non-generic interfaces (IList, IDictionary) allowing you to access
your collections in multiple ways. This opens the door to use any
collection as a data source for data aware controls which is exactly
what the (also included) TListDataset component provides.
It targets Delphi XE and XE2.
It's an open source initiative, Delphi rocks !!!
I have found a more relevant resource and can't help sharing it! So relevant that I think it deserves a separate post rather than a mere update in my first answer.
The Dduce library for Delphi XE2-XE6 makes use of TListDataSet<...> a generic dataset component that can be used to expose a generic list as a TDataSet.
The most relevant units pertaining to the implementation of the generic dataset are:
DDuce.Components.VirtualDataSet.pas (The original SO post is itself cited by the author within the source code as a reference among others!!!)
DDuce.Components.ListDataSet.pas
Class hierarchy:
TDataSet <= TCustomVirtualDataset <= TListDataset <= TListDataset<T>
Yes, it inherits lots of features... my only wish is to have at my disposal a version working with a lessen requirement (Delphi XE without most of the other bells and whistles).
Look and feel:

Memory management for a Delphi plugin framework based on TInterfacedClass

For a server-side plugin framework I would like to implement DLLs which expose a RegisterPlugin method that returns a class reference (TInterfacedClass).
The host application then creates the instance(s) of this class, and instances will run in the context of the host thread(s). (This is different for example from the Jedi VCL plugin framework which instantiates the plugin in the DLL or BPL and returns the instance to the host.)
First tests showed no problems so far. However, are there hidden problems with memory management I should be aware of? As I am using Delphi 2009 for this project, FastMM4 is the default memory manager.
Here a sketch of the plugin DLL project:
library ExamplePlugin;
uses
...
type
TPluginOne = class(TInterfacedObject, ...)
...
end;
function RegisterPlugin: TInterfacedClass; stdcall;
begin
Result := TPluginOne;
end;
exports
RegisterPlugin;
{ TPluginOne }
// ... plugin class implementation
begin
end.
No problems with memory manager, because FastMM works as a shared memory manager between the EXE and the DLL. But I'm really not comfortable with the concept of passing pure objects, or (worst) metaclasses between the DLL and the EXE. The problem is, TInterfacedObject from the EXE is not the same as TInterfacedObject from the DLL! Sure, they might look exactly the same, but they're not! And if you ever upgrade the version of Delphi for the EXE or for any of the DLL's, you'll need to rebuild everything (thus losing whatever advantage you gained from implementing a Plugin framework).
A much more portable solution would be to return a "Factory Interface", something along the lines of:
IFactoryInterface = interface
[GUId-goes-here]
function MakeWhateverInterfaceYouNeed: IUnknownDerivate
end;
then export a function with this signature:
function RegisterPlugin: IFactoryInterface;
Your code is incomplete but from what you have included there is one obvious flaw.
You appear to be exporting a class (TInterfacedClass) from a DLL. This is going to cause problems when clients try to consume your class with a different version of Delphi. What's more it's going to leave them helpless if they want to write plug-ins in a different language.
Personally I'd go for a COM based interface which will allow plug-in authors to create plug-ins in any language. This is in fact the very problem that COM was invented to solve.
If you are happy to be constrained to using the same compiler for plug-ins and host app, and you prefer to expose classes to COM interfaces, then you need to make sure that all deallocation is performed with the same memory manager as allocated the memory. The simplest way is to use ShareMem and then you'll be safe.
UPDATE
Cosmin points out in a comment another flaw with exporting classes across module boundaries. It's basically something that you shouldn't do. COM was designed for this very purpose and it still should be the first choice for you. Delphi interfaces which are COM compatible so you can get the same benefits of binary interoperability without having to create servers, register CLSID's etc.
I think your plug-in should look like this:
library ExamplePlugin;
type
TPluginOne = class(TInterfacedObject, IPlugin)
[GUID]
public
constructor Create(const Host: THostApp);
end;
function RegisterPlugin(const Host: IHostApp): IPlugin; stdcall;
begin
Result := TPluginOne.Create(Host);
end;
exports
RegisterPlugin;
begin
end.

Is possible to include files (linking) based on a component property?

Delphi 2007/2009 odd question here:
It's possible, based on a component property defined in design-time, to include files in linking or leave them ?
Example: If I leave SomeProperty true, when compiling, the unit SomeUnit will be included into my project. Otherwise it will not be included.
My second approach to this problem is to deploy a second component, which when dropped in the form (or not) will include the unit in uses clause. But if it can be done with a property, that'll be better.
I want to avoid conditional compilation via IFDEF because that forces the component to be built every time the projects are built. Or not?
I am trying to achieve an easy way of including some units in project, and then those units will provide support for specific databases. Having these into an option, at the connection component, will be ideally easy: Check support and that's done. Uncheck, and get some less KBs in your compiled APP.
edit: I'll stay with the component way for instance. I knew the IFDEF method and things, but that forces the component to be built everytime the projects are built. Or not?
I was trying to achieve an easy way of including some units in project, and then that units will provide support for specific databases. Having these into an option, at the connection component, will be ideally easy: Check support and that's done. Uncheck, and get some less KBs in your compiled APP.
No.
What are you trying to solve?
You could add a postcompiling step that would optionally include some resource based on a component property - but you'd have to do some coding to implement such a feature.
You can use the {$IFDEF youridentifier} optional code {$ENDIF} method to conditionally compile data in to your application and then to enable it just go to your project options and enter youridentifier into the appropriate option field. Another method of doing this is to add the following to the top of your unit (or in an include file):
{$DEFINE youridentifier}
which will force youridentifier on. To disable, just place a period right before the $:
{.$DEFINE youridentifier}
Using these techniques its easy to conditionally bring in code or replace code with each compile.
Write an IDE add-in. Handle the "before compile" notification and check whether any forms or data modules in the project have components of the type you're interested in, and then check their properties. Based on what you find there, you can try modifying the contents of a unit to use the other unit of your choice. It certainly doesn't sound easy, but it seems possible.
Your second idea is very easy. It's exactly what the TXPManifest component does, for example. Beware that removing such a component from a form does not "unuse" the associated unit.
To conditionally add support for different databases, you might consider using run-time packages. (That's how the IDE manages to support so many different kinds of components, after all.) Put each database's custom code into a different package. Then, the databases you support are simply whichever ones have packages available at run time. No compile-time or design-time configuration required. The obstacle to this, however, is to manage which packages are available, and determine which of them are the packages that provide database support.
Your second approach will not necessarily work the way you want it to. While Delphi will helpfully add the necessary unit to your uses list when you drop the component onto the form, it will not remove the unit when you delete the component. And even if you don't use the component or any other exported entitiy from that unit, it is possible that the unit will be linked to your application anyway, when there is code in the initialization or finalization part of the unit. This is unfortunately very often the case, even when it would be possible to initialize stuff on-demand.
There is no way to do what you are asking, but something you might not be aware of is that units which are included in your uses list but never referenced will have a minimal impact on the size of your executable. The smart linker that is in Delphi does a very good job of removing code which is never used. If you are careful about your "optional unit" which is referenced by the component, and don't have any code in it which is executed globally (everything is self contained in a class or classes) then it shouldn't matter if it is in the uses clause and not used, or not in the uses clause at all.
This would easily allow you to do what I think your wanting to do, which would be to drop a component on a form which includes a unit that then can be linked to your application. Removing the component would have the effect of not linking in the unit. I believe, however, that any resources (for example forms or other items included by the $R directive) which are in the used unit would still be included in the executable.
You could use DesignIntf.RegisterSelectionEditor to register a selection editor (see comments in Delphi source code about ISelectionEditor), then use the RequiresUnits procedure to include extra units in uses clause.
TMySelectionEditor = class(TSelectionEditor)
public
procedure RequiresUnits(Proc: TGetStrProc); override;
end;
procedure Register;
implementation
procedure TMySelectionEditor.RequiresUnits(Proc: TGetStrProc);
var
comp: TMyComponent;
I: Integer;
begin
inherited RequiresUnits(Proc);
Proc('ExtraUnit');
// might be a better way of doing the code from here onwards?
if (Designer=nil)or(Designer.Root=nil) then Exit;
for I := 0 to Designer.Root.ComponentCount - 1 do
begin
if (Designer.Root.Components[i] is TMyComponent) then
begin
comp := TMyComponent(Designer.Root.Components[i]);
if comp.SampleProperty = True then
Proc('ExtraUnit2');
Proc(comp.ObjProperty.UnitName);
end;
end;
end;
procedure Register;
begin
RegisterSelectionEditor(TMyComponent, TMySelectionEditor);
end;

Resources