Loading FireMonkey style resources with RTTI - delphi

I am trying to write class that inherits from FMX TStyledControl. When style is updated it loads style resource objects to cache.
I created project group for package with custom controls and test FMX HD project as it describes in Delphi help. After installing package and placing TsgSlideHost on the test form I run test app. It works well, but when I close it and try to rebuild package RAD Studio says “Error in rtl160.bpl” or “invalid pointer operation”.
It seems what problem in LoadToCacheIfNeeded procedure from TsgStyledControl, but I’m not understand why. Is there any restriction on using RTTI with FMX styles or anything?
TsgStyledControl sources:
unit SlideGUI.TsgStyledControl;
interface
uses
System.SysUtils, System.Classes, System.Types, FMX.Types, FMX.Layouts, FMX.Objects,
FMX.Effects, System.UITypes, FMX.Ani, System.Rtti, System.TypInfo;
type
TCachedAttribute = class(TCustomAttribute)
private
fStyleName: string;
public
constructor Create(const aStyleName: string);
property StyleName: string read fStyleName;
end;
TsgStyledControl = class(TStyledControl)
private
procedure CacheStyleObjects;
procedure LoadToCacheIfNeeded(aField: TRttiField);
protected
function FindStyleResourceAs<T: class>(const AStyleLookup: string): T;
function GetStyleName: string; virtual; abstract;
function GetStyleObject: TControl; override;
public
procedure ApplyStyle; override;
published
{ Published declarations }
end;
implementation
{ TsgStyledControl }
procedure TsgStyledControl.ApplyStyle;
begin
inherited;
CacheStyleObjects;
end;
procedure TsgStyledControl.CacheStyleObjects;
var
ctx: TRttiContext;
typ: TRttiType;
fld: TRttiField;
begin
ctx := TRttiContext.Create;
try
typ := ctx.GetType(Self.ClassType);
for fld in typ.GetFields do
LoadFromCacheIfNeeded(fld);
finally
ctx.Free
end;
end;
function TsgStyledControl.FindStyleResourceAs<T>(const AStyleLookup: string): T;
var
fmxObj: TFmxObject;
begin
fmxObj := FindStyleResource(AStyleLookup);
if Assigned(fmxObj) and (fmxObj is T) then
Result := fmxObj as T
else
Result := nil;
end;
function TsgStyledControl.GetStyleObject: TControl;
var
S: TResourceStream;
begin
if (FStyleLookup = '') then
begin
if FindRCData(HInstance, GetStyleName) then
begin
S := TResourceStream.Create(HInstance, GetStyleName, RT_RCDATA);
try
Result := TControl(CreateObjectFromStream(nil, S));
Exit;
finally
S.Free;
end;
end;
end;
Result := inherited GetStyleObject;
end;
procedure TsgStyledControl.LoadToCacheIfNeeded(aField: TRttiField);
var
attr: TCustomAttribute;
styleName: string;
styleObj: TFmxObject;
val: TValue;
begin
for attr in aField.GetAttributes do
begin
if attr is TCachedAttribute then
begin
styleName := TCachedAttribute(attr).StyleName;
if styleName <> '' then
begin
styleObj := FindStyleResource(styleName);
val := TValue.From<TFmxObject>(styleObj);
aField.SetValue(Self, val);
end;
end;
end;
end;
{ TCachedAttribute }
constructor TCachedAttribute.Create(const aStyleName: string);
begin
fStyleName := aStyleName;
end;
end.
Using of TsgStyledControl:
type
TsgSlideHost = class(TsgStyledControl)
private
[TCached('SlideHost')]
fSlideHost: TLayout;
[TCached('SideMenu')]
fSideMenuLyt: TLayout;
[TCached('SlideContainer')]
fSlideContainer: TLayout;
fSideMenu: IsgSideMenu;
procedure ReapplyProps;
procedure SetSideMenu(const Value: IsgSideMenu);
protected
function GetStyleName: string; override;
function GetStyleObject: TControl; override;
procedure UpdateSideMenuLyt;
public
constructor Create(AOwner: TComponent); override;
procedure ApplyStyle; override;
published
property SideMenu: IsgSideMenu read fSideMenu write SetSideMenu;
end;

Using TRttiField.GetAttributes leads to errors in design-time. It's a bug in Delphi XE2. See QC Report.

Related

Creating XML document with IXMLNodes

Hy! I want to add IXMLNodes to an XML document. My main procedure is btnXmlSaveToFileClick which Saves XML structure to a file. My problem is I cannot add IXMLNode as a child. Sorry for my English.
Any help or advice would be greatly appreciated.
Here is my example:
*
unit Unit3;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
XMLDoc, XMLIntf, Vcl.StdCtrls;
type
IXMLDataSample1 = interface(IXMLNode)
['{D7B7E084-1BF8-4185-85D1-1C83B74EA651}']
function GetXmlDAtaSample1: WideString;
procedure SetXmlDAtaSample1(const Value: WideString);
property XmlDataSample1: WideString read GetXmlDataSample1 write SetXmlDataSample1;
end;
IXMLDataSample2 = interface(IXMLNode)
['{6D7CE38B-3F57-4F18-A337-8405E72637E6}']
function GetXmlDataSample2: WideString;
procedure SetXmlDataSample2(const Value: WideString);
property XmlDataSample2: WideString read GetXmlDataSample2 write SetXmlDataSample2;
end;
TXMLHandler<IXMLType: IXMLNode> = class
private
class function ConvertXMLNodeToXMLType(aNode: IXMLNode): IXMLType;
protected
class function GetXMLClass: TClass; virtual; abstract;
class function GetXMLRootElementName: WideString; virtual; abstract;
class function GetXMLIfType: TGUID; virtual; abstract;
class procedure SaveXMLToFile(aXML: IXMLType; const aFileName: string);
class function NewXML: IXMLType; virtual;
end;
TDataSampleXMLHandler1 = class(TXMLHandler<IXMLDataSample1>)
public
class function GetXMLClass: TClass; override;
class function GetXMLRootElementName: WideString; override;
class function GetXMLIfType: TGUID; override;
end;
TDataSampleXMLHandler12 = class(TXMLHandler<IXMLDataSample2>)
protected
class function GetXMLClass: TClass; override;
class function GetXMLRootElementName: WideString; override;
class function GetXMLIfType: TGUID; override;
end;
TForm3 = class(TForm)
btnXmlSaveToFile: TButton;
procedure btnXmlSaveToFileClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
type
TXMLSampleDataObj1 = class(TXMLNode, IXMLDataSample1)
private
function GetXmlDataSample1: WideString;
procedure SetXmlDataSample1(const Value: WideString);
end;
TXMLSampleDataObj2 = class(TXMLNode, IXMLDataSample2)
private
function GetXMlDataSample2: WideString;
procedure SetXmlDAtaSample2(const Value: WideString);
end;
procedure TForm3.btnXmlSaveToFileClick(Sender: TObject);
//Works well
vXML1 := TDataSampleXMLHandler1.NewXML;
vXML1.XmlDataSample1 := 'test data';
TDataSampleXMLHandler1.SaveXMLToFile(vXml1, 'C:\Teszt\XmlSample.XML');
//My problem here, cannot add IXMLNode to XMLDocument
vXMLDoc := NewXMLDocument;
vXMLDoc.ChildNodes.Add(vXML1);
//And add vXML2 and more...
vXMLDoc.SaveToFile('C:\Teszt\XmlDocSaveSAmple.XML');
end;
{ TXMLHandler<IXMLType> }
class function TXMLHandler<IXMLType>.ConvertXMLNodeToXMLType(
aNode: IXMLNode): IXMLType;
begin
Result := default(IXMLType);
try
Supports(aNode, GetXMLIfType, result);
finally
aNode := nil;
end;
end;
class function TXMLHandler<IXMLType>.NewXML: IXMLType;
begin
Result :=
ConvertXMLNodeToXMLType(NewXMLDocument.GetDocBinding(GetXMLRootElementName, GetXMLClass, ''));
end;
class procedure TXMLHandler<IXMLType>.SaveXMLToFile(aXML: IXMLType;
const aFileName: string);
begin
aXML.OwnerDocument.SaveToFile(aFileName);
end;
{ TDataSampleXMLHandler12 }
class function TDataSampleXMLHandler12.GetXMLClass: TClass;
begin
Result := TXMLSampleDataObj2;
end;
class function TDataSampleXMLHandler12.GetXMLIfType: TGUID;
begin
Result := IXMLDataSample2;
end;
class function TDataSampleXMLHandler12.GetXMLRootElementName: WideString;
begin
Result := 'Sample2'
end;
{ TDataSampleXMLHandler1 }
class function TDataSampleXMLHandler1.GetXMLClass: TClass;
begin
Result := TXMLSampleDataObj1;
end;
class function TDataSampleXMLHandler1.GetXMLIfType: TGUID;
begin
Result := IXMLDataSample1;
end;
class function TDataSampleXMLHandler1.GetXMLRootElementName: WideString;
begin
Result := 'Sample2';
end;
{ TXMLSampleDataObj1 }
function TXMLSampleDataObj1.GetXmlDataSample1: WideString;
begin
Result := ChildNodes['XmlDataSample1'].Text;
end;
procedure TXMLSampleDataObj1.SetXmlDataSample1(const Value: WideString);
begin
ChildNodes['XmlDataSample1'].NodeValue := Value;
end;
{ TXMLSampleDataObj2 }
function TXMLSampleDataObj2.GetXMLDataSample2: WideString;
begin
Result := ChildNodes['XmlDataSample2'].Text;
end;
procedure TXMLSampleDataObj2.SetXmlDataSample2(const Value: WideString);
begin
ChildNodes['XmlDataSample2'].NodeValue := Value;
end;
end.*
Saving a single XML node to a document is working well. But I want to add these nodes to an XML document the application stoppes, got no error.
My problem here is:
procedure TForm3.btnXmlSaveToFileClick(Sender: TObject);
//Works well
vXML1 := TDataSampleXMLHandler1.NewXML;
vXML1.XmlDataSample1 := 'test data';
TDataSampleXMLHandler1.SaveXMLToFile(vXml1, 'C:\Teszt\XmlSample.XML');
//My problem here, cannot add IXMLNode to XMLDocument
vXMLDoc := NewXMLDocument;
vXMLDoc.ChildNodes.Add(vXML1);
//And add vXML2 and more...
vXMLDoc.SaveToFile('C:\Teszt\XmlDocSaveSAmple.XML');
end;
My finally XML what I want to looks like:
<?xml version="1.0"?>
<Sample2>
<XmlDataSample2>test data</XmlDataSample2>
</Sample2>
<Sample1>
<XmlDataSample1>test data</XmlDataSample1>
</Sample1>
The XML standard allows only 1 top-level element in a document. You are going to have to make your <Sample... > elements be children of another top-level element, eg:
<?xml version="1.0"?>
<Samples>
<Sample2>
<XmlDataSample2>test data</XmlDataSample2>
</Sample2>
<Sample1>
<XmlDataSample1>test data</XmlDataSample1>
</Sample1>
</Samples>
procedure TForm3.btnXmlSaveToFileClick(Sender: TObject);
var
vXMLDoc: IXMLDocument;
vXML1: IXMLDataSample1;
vXMLSamples: IXMLNode;
begin
vXML1 := TDataSampleXMLHandler1.NewXML;
vXML1.XmlDataSample1 := 'test data';
vXMLDoc := NewXMLDocument;
vXMLSamples := vXMLDoc.AddChild('Samples');
vXMLSamples.ChildNodes.Add(vXML1);
//And add vXML2 and more...
vXMLDoc.SaveToFile('C:\Teszt\XmlDocSaveSample.XML');
end;

How to allow a Windows Service (written in Delphi) to access an Amazon Bucket?

I would like to create a Windows Service (with Delphi) that will attempt, every hour, to retrieve a specific file from a specific Amazon S3 Bucket.
I have no problem accessing the Amazon S3 Bucket with my VCL application. However, if I try to run the same function through my Windows Service, it returns absolutely nothing. I believe that it is a permission issue: my Service does not permission to access the outside world.
What should I do to remedy my problem?
I am using Delphi Tokyo Update 3, my Service is built upon a DataSnap Server.
Here is the code for my 'server container' unit:
unit UnitOurDataSnapServerContainer;
interface
uses
System.SysUtils, System.Classes, System.Win.Registry, Vcl.SvcMgr,
Datasnap.DSTCPServerTransport,
Datasnap.DSServer, Datasnap.DSCommonServer,
IPPeerServer, IPPeerAPI, Datasnap.DSAuth;
type
TServerContainer_OurCompany = class(TService)
DSServer_OurCompany: TDSServer;
DSServerClass_OurCompany: TDSServerClass;
DSTCPServerTransport_OurCompany: TDSTCPServerTransport;
procedure DSServerClass_OurCompanyGetClass(DSServerClass: TDSServerClass; var PersistentClass: TPersistentClass);
procedure ServiceStart(Sender: TService; var Started: Boolean);
private
{ Private declarations }
protected
function DoStop: Boolean; override;
function DoPause: Boolean; override;
function DoContinue: Boolean; override;
procedure DoInterrogate; override;
public
function GetServiceController: TServiceController; override;
end;
var
ServerContainer_OurCompany: TServerContainer_OurCompany;
implementation
{$R *.dfm}
uses
Winapi.Windows,
UnitOurDataSnapServerMethods;
procedure TServerContainer_OurCompany.DSServerClass_OurCompanyGetClass(DSServerClass: TDSServerClass; var PersistentClass: TPersistentClass);
begin
PersistentClass := UnitOurDataSnapServerMethods.TOurDataSnapServerMethods;
end;
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
ServerContainer_OurCompany.Controller(CtrlCode);
end;
function TServerContainer_OurCompany.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
function TServerContainer_OurCompany.DoContinue: Boolean;
begin
Result := inherited;
DSServer_OurCompany.Start;
end;
procedure TServerContainer_OurCompany.DoInterrogate;
begin
inherited;
end;
function TServerContainer_OurCompany.DoPause: Boolean;
begin
DSServer_OurCompany.Stop;
Result := inherited;
end;
function TServerContainer_OurCompany.DoStop: Boolean;
begin
DSServer_OurCompany.Stop;
Result := inherited;
end;
procedure TServerContainer_OurCompany.ServiceStart(Sender: TService; var Started: Boolean);
begin
{$IFDEF RELEASE}
DSServer_OurCompany.HideDSAdmin := True;
{$ENDIF}
DSServer_OurCompany.Start;
end;
end.
Here is the code for my 'servermethods' unit:
unit UnitOurDataSnapServerMethods;
interface
uses
System.SysUtils, System.Classes, Datasnap.DSServer, Datasnap.DSAuth;
type
{$METHODINFO ON}
TOurDataSnapServerMethods = class(TComponent)
private
{ Private declarations }
public
{ Public declarations }
function Get_ListOfFilesInS3Bucket(aS3Path: String; aFileExtension: String) : Integer;
end;
{$METHODINFO OFF}
implementation
uses
Data.Cloud.CloudAPI, Data.Cloud.AmazonAPI;
function TOurDataSnapServerMethods.Get_ListOfFilesInS3Bucket(aS3Path: String; aFileExtension: String) : Integer;
var
iFileList: TStrings;
iFileExtension: String;
iOptionalParams: TStrings;
iResponseInfo: TCloudResponseInfo;
iStorageService: TAmazonStorageService;
iAmazonBucketResult: TAmazonBucketResult;
iAmazonObjectResult: TAmazonObjectResult;
iAmazonConnectionInfo: TAmazonConnectionInfo;
begin
Result := 0;
iFileExtension := aFileExtension;
if Pos('.', iFileExtension) = 0 then
iFileExtension := '.' + iFileExtension;
try
iAmazonConnectionInfo := TAmazonConnectionInfo.Create(nil);
iAmazonConnectionInfo.AccountName := 'AKIA****************';
iAmazonConnectionInfo.AccountKey := 'BzNn************************************';
iOptionalParams := TStringList.Create;
iOptionalParams.Values['prefix'] := aS3Path;
iStorageService := TAmazonStorageService.Create(iAmazonConnectionInfo);
iResponseInfo := TCloudResponseInfo.Create;
iAmazonBucketResult := nil;
iFileList := TStringList.Create;
try
iAmazonBucketResult := iStorageService.GetBucket('our-s3-bucket', iOptionalParams, iResponseInfo);
for iAmazonObjectResult in iAmazonBucketResult.Objects do
begin
if Pos(iFileExtension, iAmazonObjectResult.Name) <> 0 then
iFileList.Add(iAmazonObjectResult.Name);
end;
Result := iFileList.Count;
except
on e: Exception do
;
end;
FreeAndNil(iAmazonBucketResult);
finally
iFileList.Free;
iResponseInfo.Free;
iStorageService.Free;
iOptionalParams.Free;
iAmazonConnectionInfo.Free;
end;
end;
end.
It is the call to 'iStorageService.GetBucket' that returns nothing.

delphi component property: TObjectList<TPicture>

I'm trying to create a VCL component, that lets you insert multiple TImages of different sizes as properties.
I was told to best use a TObjectList ( Delphi component with a variable amount of TPictures ), but now I'm struggling to make the single TPictures assignable in the Property editor.
What i have at the moment: (it compiles)
unit ImageMultiStates;
interface
uses
Vcl.Graphics, Vcl.StdCtrls, System.SysUtils, System.Classes, Vcl.Controls, Vcl.ExtCtrls, Forms, Generics.Collections;
type
TImageMultiStates = class(TImage)
private
FPictures: TObjectList<TPicture>;
procedure SetPicture(Which: Integer; APicture: TPicture);
function GetPicture(Which: Integer): TPicture;
public
Count: integer;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Activate(Which: Integer);
published
// property Pictures: TObjectList<TPicture> read GetPicture write SetPicture;
// property Pictures[Index: Integer]: TObjectList<TPicture> read GetPicture write SetPicture;
property Pictures: TObjectList<TPicture> read FPictures write FPictures;
end;
procedure Register;
implementation
constructor TImageMultiStates.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FPictures := TObjectList<TPicture>.Create;
end;
destructor TImageMultiStates.Destroy;
begin
FPictures.Free;
inherited Destroy;
end;
procedure TImageMultiStates.SetPicture(Which: Integer; APicture: TPicture);
begin
FPictures[Which] := APicture;
if Which=0 then
Picture.Assign(APicture);
end;
function TImageMultiStates.GetPicture(Which: Integer): TPicture;
begin
Result := FPictures[Which];
end;
procedure TImageMultiStates.Activate(Which: Integer);
begin
Picture.Assign(FPictures[Which]);
end;
procedure Register;
begin
RegisterComponents('Standard', [TImageMultiStates]);
end;
end.
What doesn't work is the final result in the PropertyEditor. It shows one single item named "Pictures", with the value "(TObjectList)". Clicking it doesn't do anything, i don't get a proper editor. Other ideas for the line in question have been commented out, they bring other errors:
The first one throws the compiler error "E2008 Incompatible Types", The second one throws "Published property 'Pictures' can not be of type ARRAY".
The IDE has no idea how to edit a TObjectList at design-time, and the DFM streaming system has no idea how to stream a TObjectList. You would have to implement a custom property editor and custom streaming logic. While that is certainly possible, it is a LOT of work.
What you are attempting to do is better handled by using System.Classes.TCollection instead. Both the IDE and the DFM streaming system have built-in support for handling TCollection editing and streaming automatically for you.
Try something more like this:
unit ImageMultiStates;
interface
uses
System.Classes, Vcl.Controls, Vcl.ExtCtrls, Vcl.Graphics;
type
TImagePictureItem = class(TCollectionItem)
private
FPicture: TPicture;
procedure PictureChanged(Sender: TObject);
procedure SetPicture(Value: TPicture);
public
constructor Create(Collection: TCollection); override;
destructor Destroy; override;
published
property Picture: TPicture read FPicture write SetPicture;
end;
TImagePictureEvent = procedure(Sender: TObject; Index: Integer) of object;
TImagePictures = class(TOwnedCollection)
private
FOnPictureChange: TImagePictureEvent;
function GetPicture(Index: Integer): TImagePictureItem;
procedure SetPicture(Index: Integer; Value: TImagePictureItem);
protected
procedure Update(Item: TCollectionItem); override;
public
constructor Create(Owner: TComponent); reintroduce;
property Pictures[Index: Integer]: TImagePictureItem read GetPicture write SetPicture; default;
property OnPictureChange: TImagePictureEvent read FOnPictureChange write FOnPictureChange;
end;
TImageMultiStates = class(TImage)
private
FActivePicture: Integer;
FPictures: TImagePictures;
function GetPicture(Index: Integer): TPicture;
procedure PictureChanged(Sender: TObject; Index: Integer);
procedure SetActivePicture(Index: Integer);
procedure SetPicture(Index: Integer; Value: TPicture);
procedure SetPictures(Value: TImagePictures);
protected
procedure Loaded; override;
public
constructor Create(Owner: TComponent); override;
function Count: integer;
property Pictures[Index: Integer]: TPicture read GetPicture write SetPicture;
published
property ActivePicture: Integer read FActivePicture write SetActivePicture default -1;
property Picture stored False;
property Pictures: TImagePictures read FPictures write SetPictures;
end;
procedure Register;
implementation
{ TImagePictureItem }
constructor TImagePictureItem.Create(Collection: TCollection);
begin
inherited Create(Collection);
FPicture := TPicture.Create;
FPicture.OnChange := PictureChanged;
end;
destructor TImagePictureItem.Destroy;
begin
FPicture.Free;
inherited;
end;
procedure TImagePictureItem.PictureChanged(Sender: TObject);
begin
Changed(False);
end;
procedure TImagePictureItem.SetPicture(Value: TPicture);
begin
FPicture.Assign(Value);
end;
{ TImagePictures }
constructor TImagePictures.Create(Owner: TComponent);
begin
inherited Create(Owner, TImagePictureItem);
end;
function TImagePictures.GetPicture(Index: Integer): TImagePictureItem;
begin
Result := TImagePictureItem(inherited GetItem(Index));
end;
procedure TImagePictures.SetPicture(Index: Integer; Value: TImagePictureItem);
begin
inherited SetItem(Index, Value);
end;
procedure TImagePictures.Update(Item: TCollectionItem);
begin
if Assigned(FOnPictureChange) then
begin
if Item <> nil then
FOnPictureChange(Self, Item.Index)
else
FOnPictureChange(Self, -1);
end;
end;
{ TImageMultiStates }
constructor TImageMultiStates.Create(Owner: TComponent);
begin
inherited Create(Owner);
FPictures := TImagePictures.Create(Self);
FPictures.OnPictureChange := PictureChanged;
FActivePicture := -1;
end;
procedure TImageMultiStates.Loaded;
begin
inherited;
PictureChanged(nil, FActivePicture);
end;
function TImageMultiStates.Count: Integer;
begin
Result := FPictures.Count;
end;
procedure TImageMultiStates.PictureChanged(Sender: TObject; Index: Integer);
begin
if (FActivePicture <> -1) and ((Index = -1) or (Index = FActivePicture)) then
Picture.Assign(GetPicture(FActivePicture));
end;
function TImageMultiStates.GetPicture(Index: Integer): TPicture;
begin
Result := FPictures[Index].Picture;
end;
procedure TImageMultiStates.SetPicture(Index: Integer; Value: TPicture);
begin
FPictures[Index].Picture.Assign(Value);
end;
procedure TImageMultiStates.SetActivatePicture(Value: Integer);
begin
if FActivePicture <> Value then
begin
if ComponentState * [csLoading, csReading] = [] then
Picture.Assign(GetPicture(Value));
FActivePicture := Value;
end;
end;
procedure Register;
begin
RegisterComponents('Standard', [TImageMultiStates]);
// the inherited TImage.Picture property is published, and you cannot
// decrease the visibility of an existing property. However, if you move
// this procedure into a separate design-time package, you can then use
// DesignIntf.UnlistPublishedProperty() to hide the inherited
// Picture property at design-time, at least:
//
// UnlistPublishedProperty(TImageMultiStates, 'Picture');
//
// Thus, users are forced to use the TImageMultiStates.Pictures and
// TImageMultiStates.ActivePicture at design-time. The inherited
// Picture property will still be accessible in code at runtime, though...
end;
end.

Firemonkey and TDownloadUrl

I have an (Delphi XE2) VCL app containing an object TDownloadUrl (VCL.ExtActns) to check several webpages, so I wonder if there is an equivalent object in FireMonkey, 'cause I wanna take advantage of rich features from this new platform.
A Firemonkey app demo using threads would appreciate. Thanks in advance.
Actions don't exist yet with FireMonkey.
BTW, you can create the same behavior with a code like this:
IdHTTP1: TIdHTTP;
...
procedure TForm2.MenuItem1Click(Sender: TObject);
const
FILENAME = 'C:\Users\Whiler\Desktop\test.htm';
URL = 'http://stackoverflow.com/questions/7491389/firemonkey-and-tdownloadurl';
var
// sSource: string;
fsSource: TFileStream;
begin
if FileExists(FILENAME) then
begin
fsSource := TFileStream.Create(FILENAME, fmOpenWrite);
end
else
begin
fsSource := TFileStream.Create(FILENAME, fmCreate);
end;
try
IdHTTP1.Get(URL, fsSource);
finally
fsSource.Free;
end;
// sSource := IdHTTP1.Get(URL);
end;
The commented lines can replace the others if you just need the source in memory...
If you want to use a thread, you can manage it like this:
unit Unit2;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, FMX.Menus;
type
TDownloadThread = class(TThread)
private
idDownloader: TIdHTTP;
FFileName : string;
FURL : string;
protected
procedure Execute; override;
procedure Finished;
public
constructor Create(const sURL: string; const sFileName: string);
destructor Destroy; override;
end;
type
TForm2 = class(TForm)
MenuBar1: TMenuBar;
MenuItem1: TMenuItem;
procedure MenuItem1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.fmx}
procedure TForm2.MenuItem1Click(Sender: TObject);
const
FILENAME = 'C:\Users\Whiler\Desktop\test.htm';
URL = 'http://stackoverflow.com/questions/7491389/firemonkey-and-tdownloadurl';
var
// sSource: string;
fsSource: TFileStream;
begin
TDownloadThread.Create(URL, FILENAME).Start;
end;
{ TDownloadThread }
constructor TDownloadThread.Create(const sURL, sFileName: string);
begin
inherited Create(true);
idDownloader := TIdHTTP.Create(nil);
FFileName := sFileName;
FURL := sURL;
FreeOnTerminate := True;
end;
destructor TDownloadThread.Destroy;
begin
idDownloader.Free;
inherited;
end;
procedure TDownloadThread.Execute;
var
// sSource: string;
fsSource: TFileStream;
begin
inherited;
if FileExists(FFileName) then
begin
fsSource := TFileStream.Create(FFileName, fmOpenWrite);
end
else
begin
fsSource := TFileStream.Create(FFileName, fmCreate);
end;
try
idDownloader.Get(FURL, fsSource);
finally
fsSource.Free;
end;
Synchronize(Finished);
end;
procedure TDownloadThread.Finished;
begin
// replace by whatever you need
ShowMessage(FURL + ' has been downloaded!');
end;
end.
Regarding this:
A Firemonkey app demo using threads would appreciate.
You can find a FireMonkey demo which is using Thread here: https://radstudiodemos.svn.sourceforge.net/svnroot/radstudiodemos/branches/RadStudio_XE2/FireMonkey/FireFlow/MainForm.pas
type
TImageThread = class(TThread)
private
FImage: TImage;
FTempBitmap: TBitmap;
FFileName: string;
protected
procedure Execute; override;
procedure Finished;
public
constructor Create(const AImage: TImage; const AFileName: string);
destructor Destroy; override;
end;
...
TImageThread.Create(Image, Image.TagString).Start;
if you don't have this demo in your sample directory, you can check it out from the subversion repository used in the link above.
You can using this code.
unit BitmapHelperClass;
interface
uses
System.Classes, FMX.Graphics;
type
TBitmapHelper = class helper for TBitmap
public
procedure LoadFromUrl(AUrl: string);
procedure LoadThumbnailFromUrl(AUrl: string; const AFitWidth, AFitHeight: Integer);
end;
implementation
uses
System.SysUtils, System.Types, IdHttp, IdTCPClient, AnonThread;
procedure TBitmapHelper.LoadFromUrl(AUrl: string);
var
_Thread: TAnonymousThread<TMemoryStream>;
begin
_Thread := TAnonymousThread<TMemoryStream>.Create(
function: TMemoryStream
var
Http: TIdHttp;
begin
Result := TMemoryStream.Create;
Http := TIdHttp.Create(nil);
try
try
Http.Get(AUrl, Result);
except
Result.Free;
end;
finally
Http.Free;
end;
end,
procedure(AResult: TMemoryStream)
begin
if AResult.Size > 0 then
LoadFromStream(AResult);
AResult.Free;
end,
procedure(AException: Exception)
begin
end
);
end;
procedure TBitmapHelper.LoadThumbnailFromUrl(AUrl: string; const AFitWidth,
AFitHeight: Integer);
var
Bitmap: TBitmap;
scale: Single;
begin
LoadFromUrl(AUrl);
scale := RectF(0, 0, Width, Height).Fit(RectF(0, 0, AFitWidth, AFitHeight));
Bitmap := CreateThumbnail(Round(Width / scale), Round(Height / scale));
try
Assign(Bitmap);
finally
Bitmap.Free;
end;
end;
end.

Prevent Delphi IDE creating component icons at design time

I have created a custom control TOuterControl that is the parent for multiple TInnerControls.
Everything is working fine except that the IDE is creating icons for each the child TInnerControl's (InnerControl1 and InnerControl2 in the screenshot). How do I prevent the IDE from generating the icons?
unit TestControl;
interface
Procedure Register;
implementation
Uses
Classes,
Controls,
SysUtils,
DesignEditors,
DesignIntf,
VCLEditors;
Type
TOuterControl = Class;
TInnerControl = Class(TComponent)
Protected
FOuterControl : TOuterControl;
function GetParentComponent: TComponent; Override;
Function HasParent : Boolean; Override;
procedure SetParentComponent (Value: TComponent); Override;
End;
TOuterControl = Class(TCustomControl)
Protected
FInnerControls : TList;
Procedure Paint; Override;
Public
Constructor Create(AOwner : TComponent); Override;
Procedure AddInnerControl(AInnerControl : TInnerControl);
procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
End;
TOuterControlEditor = Class(TDefaultEditor)
Public
Procedure ExecuteVerb(Index : Integer); Override;
Function GetVerb (Index : Integer) : String; Override;
Function GetVerbCount : Integer; Override;
End;
procedure TOuterControl.AddInnerControl(AInnerControl: TInnerControl);
begin
AInnerControl.FOuterControl := Self;;
FInnerControls.Add(AInnerControl);
Invalidate;
end;
constructor TOuterControl.Create(AOwner: TComponent);
begin
inherited;
FInnerControls := TList.Create;
end;
procedure TOuterControl.GetChildren(Proc: TGetChildProc; Root: TComponent);
var
I : Integer;
begin
inherited;
For I := 0 To FInnerControls.Count - 1 Do
Proc(FInnerControls[I]);
end;
procedure TOuterControl.Paint;
begin
inherited;
Canvas.FillRect(ClientRect);
Canvas.TextOut(0,0, Format('Inner Control Count = %d', [FInnerControls.Count]));
end;
function TInnerControl.GetParentComponent: TComponent;
begin
Result := FOuterControl;
end;
function TInnerControl.HasParent: Boolean;
begin
Result := True;
end;
procedure TInnerControl.SetParentComponent(Value: TComponent);
begin
If Value Is TOuterControl Then
If FOuterControl <> Value Then
Begin
FOuterControl := TOuterControl(Value);
FOuterControl.AddInnerControl(Self);
End;
end;
procedure TOuterControlEditor.ExecuteVerb(Index: Integer);
Var
OuterControl : TOuterControl;
InnerControl : TInnerControl;
begin
inherited;
OuterControl := TOuterControl(Component);
If Index = 0 Then
Begin
InnerControl := TInnerControl.Create(OuterControl.Owner);
OuterControl.AddInnerControl(InnerControl);
End;
end;
function TOuterControlEditor.GetVerb(Index: Integer): String;
begin
Result := 'Add Inner';
end;
function TOuterControlEditor.GetVerbCount: Integer;
begin
Result := 1;
end;
Procedure Register;
Begin
RegisterComponents('AA', [TOuterControl]);
RegisterComponentEditor(TOuterControl, TOuterControlEditor);
End;
Initialization
Classes.RegisterClasses([TInnerControl]);
end.
You can prevent them from appeaing on the form with:
RegisterNoIcon([TInnerControl]);
More info on RegisterNoIcon can be found at http://docwiki.embarcadero.com/VCL/e/index.php/Classes.RegisterNoIcon
It's a little confusing having classes with a name that end with "Control" that aren't normal visual controls though.
If TInnerControl is meant to be used only inside a TOuterControl, then you should call SetSubComponent(True) during/after the TInnerControl's creation.
When you create the inner controls, you tell them that their owner is the form (the owner of the outer control). Therefore, the form draws them, just like it draws all the other components it owns. You probably want the outer control to own the inner ones.

Resources