I have created a simple Delphi form with a button that, when pressed, creates a label object in run time. I have created an on double click event for the label that shows a message to the screen. The problem is that after creating the label, I have to double click on the form before the double click event works on the label. Obviously this is not ideal as I would like to be able to double click on the label and trigger the event without having to first double click the form.
Here is the code for my form:
unit Unit4;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm4 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormDblClick(Sender: TObject);
procedure MyLabelDblClick(Sender:TObject);
private
{ Private declarations }
LabelObject: TLabel;
public
{ Public declarations }
end;
var
Form4: TForm4;
implementation
{$R *.dfm}
procedure TForm4.Button1Click(Sender: TObject);
begin
LabelObject := TLabel.Create(Self);
LabelObject.Left := 100;
LabelObject.Top := 100;
LabelObject.Width := 200;
LabelObject.Height := 20;
LabelObject.Visible := True;
LabelObject.Parent := Self;
LabelObject.Caption := 'My Run Time Label';
LabelObject.Cursor := crHandPoint;
end;
procedure TForm4.FormDblClick(Sender: TObject);
begin
LabelObject.OnDblClick := MyLabelDblClick;
end;
procedure TForm4.MyLabelDblClick(Sender: TObject);
begin
showmessage('You double clicked My Run Time Label');
end;
end.
Thanks in advance for any help with this matter.
The problem is that after creating the label, I have to double click on the form before the double click event works on the label.
Assign LabelObject.OnDblClick when creating the label, i.e. inside the Button1Click event.
Related
Changing properties on a component is in my Delphi vocabulary. I created a button by writing code and it appears on the Form as its Parent, but I do not know how to execute anything with it.
Sample - create runtime TButton and set him event OnClick...
unit Unit1;
interface
uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
type
{ TForm1 }
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
FButton : TButton;
procedure OnButtonClickTest(Sender: TObject);
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
begin
FButton := TButton.Create(nil);
FButton.Parent := self;
FButton.Left := 10;
FButton.Top := 10;
FButton.Width := 75;
FButton.Height := 25;
FButton.Caption := 'Click';
FButton.OnClick := OnButtonClickTest;
end;
procedure TForm1.OnButtonClickTest(Sender: TObject);
begin
FButton.Caption := 'Test OK';
end;
end.
I create a dynamic button FButton. Place it on the main form (Parent: Self) and set the event handler to click on it (method: OnButtonClickTest). When you click on button, on her caption change text to "Test OK"
I am looking to create an effect similar to the lightbox effect seen on many website where the background of the screen fades out and the content you want to emphasize does not. What would be the best way to go about creating such an effect in delphi ?
The content I want to emphasize in this case is a movable panel located on my form and basically all I want to do is to fade out any area of the screen that is not directly under that panel.
Thanks.
Oscar
Create a new form and add this code to the FormCreate method. You could also change the properties using the properties inspector, but I'm choosing to show you the relevant properties using code:
unit Unit1;
// This is a full screen partially transparent black form.
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure FormClick(Sender: TObject);
end;
var
Form1: TForm1;
implementation
uses Unit2;
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
BorderStyle := bsNone;
Self.WindowState := wsMaximized;
AlphaBlend := true;
Alphablendvalue := 127;
Color := clBlack;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
Form2.Show;
end;
procedure TForm1.FormClick(Sender: TObject);
begin
Close;
end;
end.
Here's a second form which has no border, which I am showing over top. It does not have alpha blending turned on, and the form style should be fsStayOnTop, or else you should use the ParentWindow property (on versions of Delphi that support that).
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm2 = class(TForm)
Label1: TLabel;
procedure FormDeactivate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormActivate(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
FAutoDeactivate: Boolean;
FCounter: Integer;
procedure WMUser1(var Message:TMessage); message WM_USER+1;
public
property AutoDeactivate:Boolean read FAutoDeactivate write FAutoDeactivate;
end;
var
Form2: TForm2;
implementation
uses Unit1;
{$R *.dfm}
procedure TForm2.FormDeactivate(Sender: TObject);
begin
if Self.Visible and FAutoDeactivate then
begin
FAutoDeactivate := false;
Form1.Close;
end;
end;
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Form1.Close;
end;
procedure TForm2.FormActivate(Sender: TObject);
begin
PostMessage(Self.Handle, WM_USER+1, 0, 0);
end;
procedure TForm2.WMUser1(var Message: TMessage);
begin
FAutoDeactivate := true;
end;
procedure TForm2.FormCreate(Sender: TObject);
begin
BorderStyle := bsNone;
Color := clWhite;
FormStyle := fsStayOnTop; // or set parent
end;
end.
That addresses how to make the whole screen "go dim", and then show something on top of that "dimmed area", but what you describe as "showing a panel in your main form" would require you to move that content out of your main form, or else clip a region out of form1, or use a combination of alpha blend plus transparency, but I don't have any code for those to show you.
If I was doing it, I would just float the thing I want not to be dimmed, above the full screen borderless 50% alpha form, as shown below.
But as you see, the screen isn't dimmed (screen brightness is not reduced), it's merely that we've done a 50% transparent layer of black which has blended in and darkened the overall screen appearance.
I have the same need as Oscar. After some search on the net, I found what is shown here.
It has helped me to do this, since it works. You can move what is emphasized in a Form instead of a Panel.
I use two forms. The first is use as "fader" and the second as dialogbox.
First
unit uFormFaded;
interface
uses
...
type
TFormFaded = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Déclarations privées }
public
{ Déclarations publiques }
end;
var
FormFaded: TFormFaded;
implementation
{$R *.dfm}
procedure TFormFaded.FormCreate(Sender: TObject);
begin
Align := alClient;
AlphaBlend := true;
AlphaBlendValue := 100;
BorderStyle := bsNone;
Color := clBlack;
Enabled := false;
FormStyle := fsStayOnTop;
end;
end.
Second
unit UFormDlgBox;
interface
uses
...
type
TFormDlgBox = class(TForm)
procedure FormShow(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Déclarations privées }
public
{ Déclarations publiques }
end;
var
FormDlgBox: TFormDlgBox;
implementation
{$R *.dfm}
uses uFormFaded;
procedure TFormDlgBox.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FormFaded.Close;
end;
procedure TFormDlgBox.FormShow(Sender: TObject);
begin
FormFaded.Show;
end;
end.
The use
FormDlgBox.ShowModal;
I tried to reproduce this schema creating the forms in run-time an make the TFormDlgBox Owns and create the TFormFaded but it doesn't work. It seems it works only with forms created in design-time.
I am using PNGImage library in my project, which entire GUI is made up of .png images, which i loaded to TImages at run-time. For some purposes i have to dynamically create plenty of components groups that are similar to each other. Every group consists of some TImages and have a button that lets user proceed to another page with more details about clicked item.
The code i am using:
procedure TMain_Frame.selection_click(Sender: TObject);
var id: string;
begin
id := StringReplace(TLabel(sender).Name, 'label_item_select_', '', [rfReplaceAll]);
hide_created_components; // It does Free all components
show_details(id);
end; // (1)
Access violation error occurs at (1). The odd thing is that it happenes completly random: error may happen at the very first click or may not happen for 10 clicks. If no error occured, F8 leads me inside PNGImage library where some stuff is done. However when error occurs, F7/8 immediately throws it without doing what it has to. This problem happenes only when i go from dynamicaly created objects to static.
CPU window shows that error occured at this ASM code:
movzx ecx, [edi]
ecx value is 755A2E09, edi is 00000000
Is it correct to .Free all dynamically created components? Or should be .Destroy used instead? And why does PNGImage goes inside itself on procedure end;?
Demo:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, pngimage, ExtCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Image1: TImage;
procedure selection_click(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure create_label;
var Button: TLabel;
begin
Button := TLabel.Create(Form1);
with Button do
begin
Name := 'dynamic_label_1';
Parent := Form1;
Autosize := false;
Left := 100;
Top := 100;
Width := 150;
Height := 20;
Caption := 'Dynamic Label: Click Me';
BringToFront;
Cursor := crHandPoint;
end;
Button.OnClick := Form1.selection_click;
end;
procedure hide_dyn_label(L: TLabel; mode: boolean);
begin
if mode then
begin
L.Free;
Form1.Image1.Picture.LoadFromFile(PAnsiChar('button_close.png'));
Form1.Image1.Visible := true;
end
else
create_label;
end;
procedure TForm1.selection_click(Sender: TObject);
var id: string;
begin
id := StringReplace(TLabel(Sender).Name, 'dynamic_label_', '', [rfReplaceAll]);
Form1.Button1.Visible := true;
hide_dyn_label(Form1.FindComponent('dynamic_label_1') as TLabel, true);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
create_label;
Form1.Image1.Visible := false;
Form1.Button1.Visible := false;
end;
end.
You are freeing the TLabel while still in its OnClick event handler, Selection_Click which calls hide_dyn_label() which calls L.Free. You can't do that. Use some kind of delayed destruction, f.ex. with a boolean variable FreeDynLabels which you can check in Application.OnIdle. Or post a custom message to the form.
I am trying to re-sort the checkboxes that are presented to the user by a button click to go up or down. I can get the checkmark boxes to resort successfully on each click but the problem is that the selected checkmark box loses focus after I click on the button. This makes it so that I cannot keep clicking the button to continuously move the checkmark box up or down. I have to re-select the checkmark box to continue moving it.
Can somebody help me with keeping the focus on the checkmark box after each button click?
My Code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, CheckLst;
type
TForm1 = class(TForm)
CheckListBox1: TCheckListBox;
upButton: TButton;
downButton: TButton;
procedure FormCreate(Sender: TObject);
procedure upButtonClick(Sender: TObject);
procedure downButtonClick(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.downButtonClick(Sender: TObject);
var
i:Integer;
begin
i:=CheckListBox1.ItemIndex;
if(i<CheckListBox1.Count-1) Then
CheckListBox1.Items.Move(i,i+1);//Here
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
CheckListBox1.Items.Add('One');
CheckListBox1.Items.Add('Two');
CheckListBox1.Items.Add('Three');
CheckListBox1.Items.Add('Four');
CheckListBox1.Items.Add('Five');
CheckListBox1.Items.Add('Six');
end;
procedure TForm1.upButtonClick(Sender: TObject);
var
i:Integer;
begin
i:=CheckListBox1.ItemIndex;
if(i>0) Then
CheckListBox1.Items.Move(i,i-1);//Here
end;
end.
You should use a method for calculating the new index and another for moving and follow the item. Then you will have a more speaking, DRY and reuseable for any control derived from TCustomListBox code:
procedure TForm1.MoveAndFollow( ListBox : TCustomListBox; OldIndex, NewIndex : integer );
begin
ListBox.Items.Move( OldIndex, NewIndex );
ListBox.ItemIndex := NewIndex;
end;
procedure TForm1.MoveCurrentItem( ListBox : TCustomListBox; MoveUp : Boolean );
var
LOld, LNew : integer;
begin
LOld := ListBox.ItemIndex;
if MoveUp then
LNew := LOld - 1
else
LNew := LOld + 1;
if
Math.InRange( LOld, 0, ListBox.Count -1 )
and
Math.InRange( LNew, 0, ListBox.Count -1 )
then
MoveAndFollow( ListBox, LOld, LNew );
end;
procedure TForm1.downButtonClick( Sender : TObject );
begin
MoveCurrentItem( CheckListBox1, False );
end;
procedure TForm1.upButtonClick( Sender : TObject );
begin
MoveCurrentItem( CheckListBox1, True );
end;
as an extension of my previous post forms an a pagecontrol at runtime
I need a solution how to pass a buttonclick event back to the parent pagecontrol.
Do I have a assign a click function as a property and assign a new click function to all my forms for all the buttons i have placed :-( .... much work , any better solution
MyMainForm = CLass( )
....
aPagecontrol : TPageControl;
aTabForm_1 : TTabForm_1 ; // in the real case I use an dynamic array
aTabForm_2 : TTabForm_2 ;
aTabForm_3 : TTabForm_3 ;
....
UserData : TUserdata ; // lot of user data ....
function MyMainForm.CreateTabAndForm: TTabForm_1;
var
tabSheet : TTabSheet;
begin
//Create a new tab sheet
tabSheet := TTabSheet.Create(PageControl1) ;
tabSheet.PageControl := PageControl1;
//create a form
Result := TTabForm_1.Create(tabSheet) ;
Result.Parent := tabSheet;
Result.Align := alClient;
Result.BorderStyle := bsNone;
Result.Visible := true;
tabSheet.Caption := Result.Caption;
//activate the sheet
PageControl1.ActiveSheet := tabSheet;
end;
// program code , now failing :
aTabForm_1 := CreateTabAndForm;
aTabForm_1.onclick := MyButtonOnclick; // here AV happens !!
....
end;
the definition of the form
//
TTabForm_1 = class(TForm)
...
property clickButton1 : TClickfunction .......
end;
Solution #1 -> pass all the data to TTabForm_1 using properties
Solution #2 -> pass Button Click event to Mainform
target : readable code - good design
One way to expose events raised inside a form or control is like this :
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm2 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
FButton1Clicked : TNotifyEvent; //Create a private TNotifyEvent field
public
// ...and expose it as a property
property OnButton1Click : TNotifyEvent read FButton1Clicked
write FButton1Clicked;
end;
implementation
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
begin
// Execute the method if it has been assigned when Button1 is clicked.
if Assigned(FButton1Clicked) then FButton1Clicked(Sender);
end;
end.
Which you would consume like :
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Unit2;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
FForm2 : TForm2;
procedure Form2ButtonClick(sender : TObject);//Create a TNotifyEvent handler
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
FForm2 := TForm2.Create(self);
//Assign a method to your custom event property
FForm2.OnButton1Click := Form2ButtonClick;
FForm2.Show;
end;
procedure TForm1.Form2ButtonClick(sender: TObject);
begin
// Do Something...
end;
end.
Of course, you don't have to use a TNotifyEvent, you can create any custom event, with parameters, that you like. For example
type
TFooEvent = procedure(ANumber : double; Sender : TObject) of object;
Which you could then use to send data with the click event :
if Assigned(FButton1Clicked) then FButton1Clicked(1.23, Button1);