I want to call a function after a form has been maxmized or restored.
I know I can something like this:
procedure TfrmMain.WMSysCommand;
begin
if (Msg.CmdType = SC_MAXIMIZE) OR (Msg.CmdType = SC_RESTORE) then
begin
Showmessage(IntToStr(frmMain.Height));
end;
DefaultHandler(Msg) ;
end;
But the problem is: this event is fired before the form is actually resized - so when the form is maximized, I get the height of the form BEFORE it was maxmized (but I want the width of the form after it has been maximized).
How to do this? Thanks!
the following link maybe will help you:
http://www.tek-tips.com/viewthread.cfm?qid=809465&page=176
declare this into interface section of this unit
Procedure sizeMove (var msg: TWMSize); message WM_SIZE;
and implementation of this procedure:
Procedure TfrmMain.sizeMove (var msg: TWMSize);
begin
inherited;
if (msg.SizeType = SIZE_MAXIMIZED) OR (msg.SizeType = SIZE_RESTORED)then
resizeQlikViewReports();
end;
You can use OnResize either and check WindowState. It's easier way.
Related
I am currently trying out the TSplitView component in Delphi 10 Seattle. The structure looks like the following:
With the second TButtonCategory I am trying to create items programmatically with the following code:
procedure TMainF.DynamicMenuButtonClick(Sender: TObject);
begin
if sender is TButtonItem then //false
ShowMessage('Sender is TButtonItem');
if sender is TCategoryButtons then //true
ShowMessage('Sender is TCategoryButtons');
end;
procedure TMainF.FormCreate(Sender: TObject);
var
i: integer;
begin
for i:=0 to 10 do begin
catMenuItems.Categories[1].Items[i] := TButtonItem.Create(catMenuItems.Categories[1].Items);
catMenuItems.Categories[1].Items[i].Caption := 'Something';
catMenuItems.Categories[1].Items[i].OnClick := DynamicMenuButtonClick;
end;
end;
In the "DynamicMenuButtonClick" procedure I want to get information about what button was clicked, the problem is that the sender which is of type TCategoryButtons doesn't tell me that. Now I was wondering if I am just missing out on something or if this is indeed just not possible.
The problem is that the sender which is of type TCategoryButtons doesn't tell me that
Yes, it does. Use the SelectedItem property of the TCategoryButtons in your event handler.
procedure TForm1.DynamicMenuButtonClick(Sender: TObject);
var
categoryButtons: TCategoryButtons;
begin
categoryButtons := (Sender as TCategoryButtons);
Memo1.Lines.Add(categoryButtons.SelectedItem.Caption);
end;
TCategoryButtons provides an event OnButtonClicked which gives you the TButtonItem. Perhaps that might be useful here.
TCatButtonEvent = procedure(Sender: TObject; const Button: TButtonItem) of object;
I have to open many forms in an application and I'm using a TtoolButton and TActionlist as a menu bar. I coded a procedure to create/show each form. I'm having difficult to trigger Form OnActivate event inside this procedure.
Each form is opened inside a Tpanel which is located in the main form FormHome.
I appreciatte your help !
See my code in Delphi 10.2
procedure TFormHome.PR_OpenForm(Pform : TFormClass);
var
vform : TForm;
begin
vform := Pform.Create(Application);
vform.Parent := PanelCorpo;
vform.Align := alclient;
vform.BorderIcons := [biSystemMenu];
vform.BorderStyle := bsNone;
vform.Show;
vform.SetFocus;
vform.OnActivate(??); // That is the issue, how to call this event ?
end;
Thanks in advance !
**Adding complimentary information to explain why I need one single method to create/open my forms **
This is the code I use to open each particular forms. I have one method to each form with exactly the same code. The only difference is the Form instance itself :
procedure TFormHome.OpenDiretorioExecute(Sender: TObject);
begin
if Not Assigned(FormDiretorio) then
begin
FormDiretorio := TFormDiretorio.Create(Self);
FormDiretorio.Parent := PanelCorpo;
FormDiretorio.Align := alclient;
FormDiretorio.BorderIcons := [biSystemMenu];
FormDiretorio.BorderStyle := bsNone;
FormDiretorio.Show;
FormDiretorio.SetFocus;
FormDiretorio.OnActivate(Sender); // In this way , OnActivate works fine
end;
end;
What I need/want :
I need only one method to open all forms. This TFormHome.PR_OpenForm(Pform : TFormClass) coded above is almost there, except by the OnActivate method that is not working !
Could you help me to fix that ?
Thanks!
Sample Code - Project with Old code and new code
===> Main Form "FormHome"
... // This is the main Form FormHOme which calls FormA, FormB and FormC
// There is a TToolbar with 3 Toolbutton that uses a TActionlist
// FormA and FormB are called by the old style method Action1Execute
// and Action2Execute
// FormC is called by the new method PR_CreateOpenForm , which
// presents the error
var
FormHome: TFormHome;
implementation
uses
UnitFormA,
unitFormB,
UnitFormC;
{$R *.dfm}
procedure TFormHome.Action1Execute(Sender: TObject);
// Action1 : OnExecute event, called from ToolButton1
begin
if Not Assigned(FormA) then
begin
FormA := TFormA.Create(Self);
end;
FormA.Parent := Panelhome;
FormA.Align := alclient;
FormA.BorderIcons := [biSystemMenu];
FormA.BorderStyle := bsNone;
FormA.Show;
FormA.SetFocus;
FormA.OnActivate(Sender); // There is a code in OnActivate event in FormA
end;
procedure TFormHome.Action2Execute(Sender: TObject);
// Action2 : OnExecute event , called from ToolButton2
begin
if Not Assigned(FormB) then
begin
FormB := TFormB.Create(Self);
end;
FormB.Parent := Panelhome;
FormB.Align := alclient;
FormB.BorderIcons := [biSystemMenu];
FormB.BorderStyle := bsNone;
FormB.Show;
FormB.SetFocus;
FormB.OnActivate(Sender); // There is a code in OnActivate event in FormB
end ;
procedure TFormHome.Action3Execute(Sender: TObject);
// Action3 OnExecute event, called from ToolButton3
// This is the desired code to implment in all Action OnExecute event
begin
PR_CreateOpenForm(TFormC); // Fails in the OnActivate event
end;
procedure TFormHome.PR_CreateOpenForm(PClassform : TFormClass);
// This routine should be used to create/open all forms
//
var
vform : TForm;
begin
if Not Assigned(Tform(PClassform)) then
begin
vform := Pclassform.Create(Application);
end;
vform.Parent := PanelHome;
vform.Align := alclient;
vform.BorderIcons := [biSystemMenu];
vform.BorderStyle := bsNone;
vform.Show;
vform.SetFocus;
vform.onActivate(self); // Does not work !! Tried with : vform.Onactivate(nil) - vform.Onactivate(Tform)
end;
end.
FORMA - OnActivate event
procedure TFormA.FormActivate(Sender: TObject);
begin
Edit1.Text := 'content from OnActivate';
end;
FORMB - OnActivate event
procedure TFormB.FormActivate(Sender: TObject);
begin
Edit1.Text := 'content from OnActivate';
end;
FORMC - OnActivate event
procedure TFormC.FormActivate(Sender: TObject);
begin
Edit1.Text := 'content from OnActivate';
end;
Error when calls PR_CreateOpenForm(TFormC)
DEBUG - running step by step reach this event handler error :
procedure TWinControl.MainWndProc(var Message: TMessage);
begin
try
try
WindowProc(Message);
finally
FreeDeviceContexts;
FreeMemoryContexts;
end;
except
Application.HandleException(Self);
end;
end;
Please let me know if I have to provide any other information/code in order to have your suggestions and valuable tips !
Thank you guys !
Update
You asked in a comment
The point is : given a parameter PClassForm of TformClass class, how to check if there is any instance of the such parameter created in the Application ? " ,
You can do this using a function like the FormInstance one below. The Screens object of a VCL application has a Forms property, and you can iterate that, looking to see if one of the forms is a specified class, which is returned as the function's result (which it Nil otherwise). Once you have found the instance, you could of course use a cast to call some specific method of it.
function FormInstance(AClass : TClass) : TForm;
var
i : Integer;
begin
Result := Nil;
for i := 0 to Screen.FormCount - 1 do begin
if Screen.Forms[i].ClassType = AClass then begin
Result := Screen.Forms[i];
Break;
end;
end;
end;
procedure TMyForm.Button1Click(Sender: TObject);
var
F : TForm;
begin
F := FormInstance(TForm2);
if F <> Nil then
Caption := 'Found'
else
Caption := 'Not found';
end;
original answerThe way you've written your q seems to tangle up your actual technical q
vform.OnActivate(??); // That is the issue, how to call this event ?
with a lot of issues which aren't directly related. Rather that try to
invoke the OnActivate handler (If there is one), it may be better
to override the form's Activate procedure to do whatever special handling you want
and then leave it to the code in TForm to decide when to invoke the OnActivate. This is less likely to wrong-foot other form behaviour (like in TScreen).
The code below shows how to do this.
type
TForm1 = class(TForm)
procedure FormActivate(Sender: TObject);
protected
procedure Activate; override;
public
end;
[...]
procedure TForm1.Activate;
begin
inherited;
Caption := Caption + ' called from TForm1.Activate';
end;
procedure TForm1.FormActivate(Sender: TObject);
begin
Caption := 'Activated';
end;
Of course, maybe you could just put the code you want to execute in OnActivate in the OnShow handler instead.
Try change the working OpenDiretorioExecute method to shown code below and tell me if you still get the error. Use vform.OnActivate(Self) inside PR_OpenForm. Please also show the OnActivate event handler for TFormDiretorio.
procedure TFormHome.OpenDiretorioExecute(Sender: TObject);
begin
FormDiretorio:=PR_OpenForm(TFormDiretorio) as TFormDiretorio;
(* if Not Assigned(FormDiretorio) then
begin
FormDiretorio := TFormDiretorio.Create(Self);
FormDiretorio.Parent := PanelCorpo;
FormDiretorio.Align := alclient;
FormDiretorio.BorderIcons := [biSystemMenu];
FormDiretorio.BorderStyle := bsNone;
FormDiretorio.Show;
FormDiretorio.SetFocus;
FormDiretorio.OnActivate(Sender); // In this way , OnActivate works fine
end; *)
end;
Change PR_OpenForm to return vForm. I assume you need the variable FormDiretorio.
The error is in this piece of code:
if Not Assigned(TForm(PClassform)) then
begin
vform := PClassform.Create(Application);
end;
If you look in the source for implementation of Assigned() you will see that it only checks wheter the passed in argument is nil or not. Thus, your code doesn't check for the existence of a form of type PClassForm, as you might think. It only checks whether the parameter PClassForm is nil or not.
In your case Assigned() returns true, the form is not created and subsequently vform contains whatever happens to be on the stack. That it only crashes at the line where you call OnActivate() is just a coincidence. You may have destroyed significant data (and probably have) by accessing the uninitialized vform variable.
To prevent errors like this to become fatal, you should initialize local pointer variables to nil if they might stay uninitialized. You probably also got a compiler warning for this but neglected it.
Already earlier I wanted to ask you where you plan to hold references to the forms that you create, so that you can access them, but I didn't, because it was not your question.
You need to decide on that and then use those references, both to check for existense and to access the forms.
So basically when a user clicks on the checkbox, I want to add that item in my list, I have tried using the OnChange event but this is not working for me as it gets fired even when the Checkbox is not clicked.
My code is simple and straightforward
procedure LvUserChange(Sender: TObject; Item: TListItem;Change: TItemChange);
var
objUser : TUsers;
begin
if not assigned(objListOfChangedUsers) then
objListOfChangedUsers := TObjectList.Create;
objUser := Item.Data;
objListOfChangedUsers.Add(objUser);
end;
I want this code to be fired ONLY when the checkbox is clicked in the ListView
In Delphi 2009 and later, you can use the TListView.OnItemChecked event to detect checkbox clicks. Delphi 2007 does not have such an event, in which case you will need to detect the notification manually in your own code. I will demonstrate with an interposer class, but there are other ways to do this.
uses
..., CommCtrl, ComCtrls, ...;
type
TListView = class(ComCtrls.TListView)
protected
procedure CNNotify(var Message: TWMNotifyLV); message CN_NOTIFY;
end;
....
procedure TListView.CNNotify(var Message: TWMNotifyLV);
begin
inherited;
if Message.NMHdr.code = LVN_ITEMCHANGED then
begin
if (Message.NMListView.uChanged = LVIF_STATE) and
( ((Message.NMListView.uOldState and LVIS_STATEIMAGEMASK) shr 12)
<> ((Message.NMListView.uNewState and LVIS_STATEIMAGEMASK) shr 12)) then
begin
// changing check box state will land you here
end;
end;
end;
Since OnItemChecked does not exist, you cannot get your event to fire only when the item is checked, but you can filter your event like this
procedure LvUserChange(Sender: TObject; Item: TListItem;Change: TItemChange);
var
objUser : TUsers;
begin
if Change = ctState then
begin
if Item.Checked then
begin
if not assigned(objListOfChangedUsers) then
objListOfChangedUsers := TObjectList.Create;
objUser := Item.Data;
objListOfChangedUsers.Add(objUser);
end
else
begin
// just in case there are any actions when unchecking
end;
end;
end;
I don't have Delphi 2007 but have checked the documentation and it should work.
How can I make a second form can follow the position of the main form wherever the main form shifted. for example, can be seen in this GIF image:
I tried using this delphiDabbler tip, which is to stop a form moving, but did not manage to get something that worked.
In the main form you need this:
type
TMainForm = class(TForm)
protected
procedure WMWindowPosChanged(var Msg: TWMWindowPosChanged);
message WM_WINDOWPOSCHANGED;
end;
....
procedure TMainForm.WMWindowPosChanged(var Msg: TWMWindowPosChanged);
begin
inherited;
if Assigned(OtherForm) and not Application.Terminated then
begin
OtherForm.Left := Left + Width;
OtherForm.Top := Top;
end;
end;
This ensures that whenever the main form's position changes, the other form clamps to it. Note that this message can be sent before the other form is created, and after it is no longer valid. Hence the if statement.
And on the other form do this:
type
TOtherForm = class(TForm)
protected
procedure WMWindowPosChanging(var Msg: TWMWindowPosChanging);
message WM_WINDOWPOSCHANGING;
end;
....
procedure TOtherForm.WMWindowPosChanging(var Msg: TWMWindowPosChanging);
begin
inherited;
if not Application.Terminated then
begin
Msg.WindowPos.x := MainForm.Left + MainForm.Width;
Msg.WindowPos.y := MainForm.Top;
end;
end;
This ensures that any attempts to move the other form are rejected.
Handle WM_WINDOWPOSCHANGING to move your other form(s) at the same time.
...
public
OldTop, OldLeft: Integer;
procedure WindowPosChanging(var Msg: TWMWindowPosChanging);
message WM_WINDOWPOSCHANGING;
end;
...
implementation
...
procedure TForm1.WindowPosChanging(var Msg: TWMWindowPosChanging);
var
DTop, DLeft: Integer;
begin
// well and here inside of you put the relationship of like you
// want him to move.
// an example of this moving them in the same sense can be...
if (Form2 = nil) or (not Form2.Visible) then Exit;
// this line is to avoid the error of calling them when the forms
// are creating or when they are not visible...
DTop := Top - OldTop;
DLeft := Left - OldLeft;
Form2.Top := Form2.Top + DTop;
Form2.Left := Form2.Left + DLeft;
OldTop := Top;
OldLeft := Left;
inherited;
end;
Source:
http://delphi.cjcsoft.net/viewthread.php?tid=43047
(original code updated according to suggestions in comments)
Or something like this
Two forms to snap each other
I believe I'm too tired, and I don't understand why a small callback doesn't work. I have 2 frames, created dynamically, I show the first one and at a click, I show the second one. When I finish the work with the second one, I want to show the first frame and free the second frame. Code is bellow:
code for the first frame:
procedure CommingBackFromFrame(aFrame:TFrame);
procedure TfraMain.ComingBackFromFrame(aFrame:TFrame);
begin
if Assigned(aFrame) then
begin
try
aFrame.Hide;
FreeAndNil(aFrame);
except on e:Exception do
//make a log
end;
Self.Show;//first frame show
end;
//code which creates the second frame
wFrm := TFrameType.Create(Application);//create the second frame
with wFrm do
begin
GoBack:=ComingBackFromFrame(wFrm);//error here
parent:=Self;
Show;
end; //with
Application.ProcessMessages;
code for the second frame:
TCallBack = procedure(aFrame:TFrame) of object;//callback declaration
TFrameType = class(Tframe)
...
private
FGoBack:TCallBack;
public
property GoBack:TCallBack read FGoBack write FGoBack;//publish callback
....
//at a moment, return to frame 1
if Assigned(fgoback) then
GoBack(Self);
Can anyone help me this simple thing?
BTW this is bad practice - free an object from its own code. Try to do that by message handler via PostMessage() to ensure that VCL finished all its work before freeing object.
Something like this:
TFrameType = class(TFrame)
protected
procedure FreeMe(var Msg TMessage) message WM_FREE_MY_FRAME;
public
procedure PostponedFree;
end;
procedure TFrameType.FreeMe(var Msg TMessage);
begin
Free;
end;
procedure TFrameType.PostponedFree;
begin
PostMessage(Self.Handle, WM_FREE_MY_FRAME, 0, 0);
end;
And call PostponedFree.
PS Code may not be accurate - I haven't started Delphi now. Sorry.
You're calling CommingBackFromFrame. So unless it's return-type is TCallBack it's obvious that it doesn't compile.
You might want to do GoBack:=CommingBackFromFrame; instead which subscribes the method CommingBackFromFrame to the even GoBack. Or perhaps GoBack:=wFrm.CommingBackFromFrame; depending on where CommingBackFromFrame is declared.
SideNote: You have a typo, the word is "coming" and not comming
Specify where CommingBackFromFrame comes from and what it does; without that, CodeInChaos` answer is the best you can get.
Is it part of wFrmDblDet, or part of your encompassing scope (the usage of with obfuscates that)?
In your current code, ComingBackFromFrame(wFrm) should return a TCallBack, but I think that was not your intent.
--jeroen
+1 to all.Thank you for your answers, especially to Abelisto et Jeroen, I've resolved now all the problems. Until Abelisto suggested PostMessage I've encountered lots of errors. Entire solution is bellow :
first frame, or FrmMain :
const WM_MY_MESSAGE = WM_USER + 0;
type
TfraMain = class(TFrame)
...
private
FFraChild : TFraChild;//second frame
procedure OnMyMessage(var Msg: TMessage); message WM_MY_MESSAGE;
procedure ComingBackFromFrame(aFrame:TFrame);
....
//step when the second frame is created
FFraChild := TFraChild.Create(Application);
with FFraChild do
begin
GoBack:= ComingBackFromFrame;
parent:=Self;
Show;
end; //with
....
procedure TfraMain.ComingBackFromFrame(aFrame:TFrame);
begin
if aFrame<>nil then
begin
try
aFrame.Hide;
PostMessage(Self.Handle,WM_MY_MESSAGE,0,0);
except on e:Exception do
// log error
end;
end;
end;
procedure TfraMain.OnMyMessage(var Msg: TMessage);
begin
FreeAndNil(FFraChild);
end;
second frame or frame 'child'
type
TCallBack = procedure(aFrame:TFrame) of object;
TFraChild = class(TFrame)
...
private
FGoBack:TCallBack;
public
property GoBack:TCallBack read FGoBack write FGoBack;
....
//after all operations with it are finished
if Assigned(fgoback) then
FGoBack(Self);
#Jeroen, I didn't found something related to Frames as the 'Release' existing in the TForm's implementation.
Best regards,
Radu