I have a TForm as this:
TMyForm = class (TForm)
[MyAttr('Data')]
edit1: TEdit;
private
FData: String;
end
When I try to get the fields of this form via RTTI, I only get the edit1 field, not FDATA, and when I query for edit1 field attributes a get a empty array.
For anoteher class that not inherit TForm, all work ok. ¿why?
edit for sample
type
{$RTTI EXPLICIT FIELDS([vcPrivate,vcProtected, vcPublic])}
TForm3 = class(TForm)
[TCustomAttribute]
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
[TCustomAttribute]
FData: String;
public
{ Public declarations }
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
procedure TForm3.Button1Click(Sender: TObject);
var
LCtx: TRttiContext;
LField: TRttiField;
LAttr: TCustomAttribute;
begin
for LField in LCtx.GetType(Self.ClassInfo).GetDeclaredFields do
begin
Memo1.Lines.Add(lField.Name);
for LAttr in LField.GetAttributes do
Memo1.Lines.Add(#9+LAttr.ClassName);
end;
end;
end.
result where button1 is clicked:
FData
TCustomAttribute
Button1
Memo1
There could be a $RTTI directive somewhere in your project that's causing extended RTTI to not work.
Try adding the following just before the declaration of TMyForm:
{$RTTI EXPLICIT
METHODS(DefaultMethodRttiVisibility)
FIELDS(DefaultFieldRttiVisibility)
PROPERTIES(DefaultPropertyRttiVisibility)}
This will reset RTTI generation for everything declared after it, and you should get proper RTTI after that point.
Related
I am calling a function for SOAP service and one of the required parameters is TWideStringDynArray. How do I initialize and populate this type of array? Or is there another way to convert a normal array to this type?
This is how it's defined in SOAP class
...
ArrayOfString = TWideStringDynArray;
...
PrsDataGet = class(TRemotable)
private
FsRetVal: WideString;
FsInParam: ArrayOfString;
published
property sRetVal: WideString read FsRetVal write FsRetVal;
property sInParam: ArrayOfString read FsInParam write FsInParam;
end;
If i set FsInParam as a array of WideString it says incompatible types
procedure TForm1.Button1Click(Sender: TObject);
var
QuotePrice : Real;
rezultat:WideString;
mnozica:array [0..4] of integer;
parametri: array of WideString;
obstaja:boolean;
PrsGet:PrsDataGet;
PrsFind:PrsDataGet;
begin
PrsGet.sRetVal :=rezultat;
SetLength(parametri, 4);
parametri[0]:= '1234';
parametri[1]:= 'wsprsinfotest';
parametri[2]:= 'efwefawf';
parametri[3]:= 'PRS_MN_P';
PrsGet.sInParam :=parametri;
If i change type of parametri to ArrayOfString it doesn't complain of incompatible types anymore but then it throws an Access Violation at SetLength(parametri, 4);
EDIT
To reproduce the problem:
I import WSDL definition into delphi from https://wwwt.ajpes.si/wsPrsInfo/PrsInfo.asmx?WSDL
Code for the service call:
unit UnKlicServisa;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Rio, SoapHTTPClient,Types,
UnWsPrsInfo_1, UnWsPrsInfo_2;
type
TForm1 = class(TForm)
HTTPRIO1: THTTPRIO;
Edit1: TEdit;
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
rezultat:WideString;
parametri: ArrayOfString;
obstaja:boolean;
PrsGet:PrsDataGet;
begin
PrsGet.sRetVal :=rezultat;
parametri[0]:= '1234';
parametri[1]:= 'wsprsinfotest';
parametri[2]:= '3423445';
parametri[3]:= 'PRS_MN_P';
PrsGet.sInParam :=parametri;
if Trim(Edit1.Text) <> '' then
begin
(HTTPRIO1 as PrsInfoSoap).PrsDataGet(PrsGet);
end
else
begin
MessageDlg('Enter a Valid ISBN code',mtInformation,[mbOk],0);
Edit1.SetFocus;
end;
end;
end.
Note FPC 3.2.0+ also allows array constructors
{$mode Delphi}
uses types;
var arr : TWideStringDynArray;
begin
arr:=TWideStringDynArray.Create('1234','wsprsinfotest','efwefawf','PRS_MN_P');
end.
Is there any event that determines, if the mouse is hovering above an edit box? Basically, I want to show a hint/help for the user, but I want to display an image and simple instructions. What would be the best way to proceed?
Thanks for any help
Use the OnMouseEnter and OnMouseLeave events. In the event handlers, you can set the visibility of a Label or simliar control with the hint text. In the example, I took an empty VCL form and inserted a TEdit and a TLabel. I implemented the OnMouseMEnter and the OnMouseLeave events:
TForm1 = class(TForm)
Edit1: TEdit;
Label1: TLabel;
procedure Edit1MouseEnter(Sender: TObject);
procedure Edit1MouseLeave(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Edit1MouseEnter(Sender: TObject);
begin
Label1.Visible:=True;
end;
procedure TForm1.Edit1MouseLeave(Sender: TObject);
begin
Label1.Visible:=False;
end;
Another solution could be to use the OnMouseEnter and OnMouseLeave events.
This is a sample found on Embarcadero:
type
TForm1 = class(TForm)
Button1: TButton;
StatusBar1: TStatusBar;
Edit1: TEdit;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
procedure DisplayHint(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ Here is the implementation of the OnHint event handler }
{ It displays the application’s current hint in the status bar }
procedure TForm1.DisplayHint(Sender: TObject);
begin
StatusBar1.SimpleText := GetLongHint(Application.Hint);
end;
{ Here is the form’s OnCreate event handler. }
{ It assign’s the application’s OnHint event handler at runtime }
{ because the Application is not available in the Object Inspector }
{ at design time }
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnHint := DisplayHint;
end;
You can use special tag on HINT property of TLabel, then manage the output as you need.
I am new to Delphi and have a problem with creating a new form dynamically. I want to create the new form with the elements properties from the gui I made. Here is the form I want to dynamically create :
unit AddEmployeeF;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons;
type
TAddEmployee = class(TForm)
GroupBox1: TGroupBox;
AddName: TLabel;
AddDept: TLabel;
AddPhone: TLabel;
AddExtension: TLabel;
AddDetails: TLabel;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Edit4: TEdit;
Edit5: TEdit;
BitBtn1: TBitBtn;
BitBtn2: TBitBtn;
procedure CancelButtonClick(Sender: TObject);
private
{ Private declarations }
public
constructor CreateNew(AOwner: TComponent; Dummy: Integer = 0); override;
end;
var
AddEmployee: TAddEmployee;
implementation
{$R *.dfm}
constructor TAddEmployee.CreateNew(AOwner: TComponent; Dummy: Integer = 0; Detail : String);
begin
inherited Create(AOwner);
AddDetails.Caption := Detail;
end;
procedure TAddEmployee.CancelButtonClick(Sender: TObject);
begin
self.Close;
end;
end.
I dont want to create all the gui elements again in the constructor, just to modificate some properties of the elements, like caption but keep the positions and other properties from the gui definition. It's possible? And how to create the form from another form, like this? :
procedure TWelcome.SpeedButton1Click(Sender: TObject);
var
myForm :TAddEmployee;
begin
myForm := TAddEmployee.CreateNew(AOwner, Dummy, Details);
myForm.ShowModal;
end;
You overrode the wrong constructor. The TForm.CreateNew() constructor bypasses DFM streaming, so all of your design-time components will not be created at run-time. Worse, your overridden CreateNew() constructor is calling the inherited TForm.Create() constructor, which calls CreateNew() internally, thus you will get stuck in an endless loop that causes an stack overflow error at runtime.
To do what you are asking for, override the TForm.Create() constructor instead, or define a whole new constructor that calls TForm.Create() internally. Do not involve TForm.CreateNew() at all.
type
TAddEmployee = class(TForm)
...
public
constructor Create(AOwner: TComponent); override; // optional
constructor CreateWithDetail(AOwner: TComponent; Detail : String);
end;
constructor TAddEmployee.Create(AOwner: TComponent);
begin
CreateWithDetail(AOwner, 'Some Default Value Here');
end;
constructor TAddEmployee.CreateWithDetail(AOwner: TComponent; Detail : String);
begin
inherited Create(AOwner);
AddDetails.Caption := Detail;
end;
procedure TWelcome.SpeedButton1Click(Sender: TObject);
var
myForm : TAddEmployee;
begin
myForm := TAddEmployee.CreateWithDetail(AOwner, Details);
myForm.ShowModal;
myForm.Free;
end;
Declare your constructor like this:
constructor Create(AOwner: TComponent; const Detail: string); reintroduce;
Implement it like this:
constructor TAddEmployee.Create(AOwner: TComponent; const Detail: string);
begin
inherited Create(AOwner);
AddDetails.Caption := Detail;
end;
Call it like this:
myForm := TAddEmployee.Create(MainForm, Details);
I'm not sure what you want to pass as the owner. Could be the main form, could be something else.
You should also remove the global variable named AddEmployee and so force yourself to take control over instantiating the form.
I chose to name my constructor Create, and so hide the inherited constructor of that name, to force consumers of the class to supply the Details parameter in order to make an instance of the class.
I have a TDBGrid component. I need to catch the event triggered when I'm resizing a column of the grid.
the only place to get an events seems to be overriding ColWidthChanged...
type
TDBgrid=Class(DBGrids.TDBGrid)
private
FColResize:TNotifyEvent;
procedure ColWidthsChanged; override;
protected
Property OnColResize:TNotifyEvent read FColResize Write FColResize;
End;
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
DBGrid1: TDBGrid;
ADODataSet1: TADODataSet;
DataSource1: TDataSource;
procedure FormCreate(Sender: TObject);
private
procedure ColResize(Sender: TObject);
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TDBgrid }
procedure TDBgrid.ColWidthsChanged;
begin
inherited;
if Assigned(FColResize) then FColResize(self);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
DBgrid1.OnColResize := ColResize;
end;
procedure TForm1.ColResize(Sender:TObject);
begin
Caption := FormatDateTime('nn:zzz',now) ;
end;
you need to create a descendent of TDBGrid and implement the event by yourself. Something like this:
unit MyDBGrid;
interface
type
TMyDBGrid = class(TDBGrid)
private
FOnColResize: TNotifyEvent;
protected
procedure ColWidthsChanged; override;
public
published
property OnColResize: TNotifyEvent read FOnColResize write FOnColResize;
end;
implementation
{ TMyDBGrid }
procedure TMyDBGrid.ColWidthsChanged;
begin
inherited;
if (Datalink.Active or (Columns.State = csCustomized)) and
AcquireLayoutLock and Assigned(FOnColResize) then
FOnColResize(Self);
end;
end.
this should work, I don't have time now to test it.
I am trying to make a callback, sending different object types and some extra info for the objects. So I made this class:
TCallBackObject = class
Sender : string;
ObjectClass : string;
Obj : TObject;
Status : integer;
ID : integer;
end;
In different situations I create different Objects in the Obj field, but I always get the error message when executing DSServer.BroadcastObject "Internal: Cannot instantiate object ..."
Here is my really simple example: http://www.4shared.com/file/fONlAGM3/DataSnapExample.html
Please see the example and tell me what is wrong...
On the client side, the objects classes are not in the executable.
To be sure, try this dirty check. Create a reference in the client code referring to the used classes.
eg.
TForm6 = class(TForm)
SQLConnection1: TSQLConnection;
DSClientCallbackChannelManager1: TDSClientCallbackChannelManager;
Button1: TButton;
Label1: TLabel;
Label2: TLabel;
procedure Button1Click(Sender: TObject);
procedure OnExecute(AValue: TObject);
private
c: TCat; //dummy refernce to the class
d: TDog; //dummy refernce to the class
co: TCAllbackObject; //dummy refernce to the class
public
{ Public declarations }
end;
Now it should works.
A cleaner way is to use an empty register class method for each classes. As the following:
TCallBackObject = class
Sender: string;
ObjectClass: string;
Obj: TObject;
Status: integer;
ID: integer;
class procedure Register;
end;
...
class procedure TCallBackObject.Register;
begin
//
end;
initialization
TCallBackObject.Register;
end.