Simulate Align position in Delphi Custom Panel - delphi

I'm building a custom panel in Delphi XE5 and I'm having a hard time simulating a new "Gravity" property where I can combine two coordinates (like Right + Bottom) and the effect is similar to "Align" however, it does not resize the object, direction. The main problem I encountered is to simulate this behavior. My initial intention was to create a panel in memory with the same "Parent" in my custom panel and then align to the position defined in "Gravity" overwriting the "SetBounds" method. It's working, but a bit precarious, especially in "Design Time". Could someone suggest me how to more effectively simulate this alignment using VCL?
function TZPanel.GetPosition: TCustomPanel;
var
sid: TZSide;
anch: TAnchors;
panTest: TPanel;
function getGravity(al: TAlign): TRect;
var
panGravity: TPanel;
I: Integer;
begin
try
//Self.Visible := False;
panGravity:= TPanel.Create(Self);
panGravity.BevelInner := panTest.BevelInner;
panGravity.BevelOuter := panTest.BevelOuter;
panGravity.BevelWidth := panTest.BevelWidth;
panGravity.BorderWidth := panTest.BorderWidth;
panGravity.ParentBackground := True;
panGravity.SetBounds(panTest.Left, panTest.Top, panTest.Width, panTest.Height);
panGravity.Parent:= Self.Parent;
panGravity.Align := al;
Result:= panGravity.BoundsRect;
finally
panGravity.Destroy;
Self.Visible := True;
end;
end;
begin
panTest := TPanel.Create(Self);
panTest.Align := Align;
panTest.Anchors := Anchors;
panTest.BevelInner := BevelInner;
panTest.BevelOuter := BevelOuter;
panTest.BevelWidth := BevelWidth;
panTest.BorderWidth := BorderWidth;
panTest.SetBounds(Left, Top, Width, Height);
if (FGravity = []) then
begin
//
end
else
begin
panTest.Align := alCustom;
anch := [];
for sid in FGravity do
begin
case sid of
sTop:
begin
panTest.Top := getGravity(alTop).Top;
anch := anch + [akTop];
end;
sRight:
begin
panTest.Left := getGravity(alRight).Left;
anch := anch + [akRight];
end;
sBottom:
begin
panTest.Top := getGravity(alBottom).Top;
anch := anch + [akBottom];
end;
sLeft:
begin
panTest.Left := getGravity(alLeft).Left;
anch := anch + [akLeft];
end;
end;
end;
panTest.Anchors := anch;
end;
Result := panTest;
end;

Related

Creating custom button in Delphi with different actions [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I am creating a custom component, a button-switch like this :
In my "form activate" function, I wrote a for loop in which I call 3 times the button function with a different position parameter like : SwitchButton(30); where 30 is the top position.
What I want to do is assign at these 3 buttons different actions, here some code.
Code for button creation :
procedure TFMain.SwitchButton(posPulsante: Integer);
var
i: Integer;
posDescrizionePulsante: Integer;
strDescrizione: String;
begin
posDescrizionePulsante := 32;
lastPressed := 1;
Pulsante := TPanel.Create(FMain);
BordoPulsante := TShape.Create(self);
LevaPulsante := TPanel.Create(self);
DescrizionePulsante := TLabel.Create(self);
//Proprietà Descrizione
with DescrizionePulsante do
begin
Parent := PComandi;
Top := posDescrizionePulsante;
Left := 100;
Caption := 'Visualizza finestra utenti';
Font.Name := 'Tahoma';
Font.Size := 12;
Font.Style := [fsBold];
Font.Color := clWhite;
end;
//Proprietà Pulsante
with Pulsante do
begin
Parent := PComandi;
ParentColor := false;
ParentBackground := false;
BevelOuter := bvNone;
Color := clWhite;
Width := 57;
Height := 25;
Top := posPulsante;
Left := 20;
Visible := true;
end;
//Proprietà Bordo
with BordoPulsante do
begin
Parent := Pulsante;
Align := alClient;
Brush.Style := bsClear;
Brush.Color := RGB(122,136,201);
Pen.Color := clWhite;
Pen.Style := psSolid;
Pen.Width := 3;
end;
//Proprietà Leva
with LevaPulsante do
begin
Parent := Pulsante;
ParentBackground := false;
ParentColor := false;
BevelOuter := bvNone;
Color := clWhite;
Cursor := crHandPoint;
Width := 23;
Height := 13;
Top := 6;
Left := 28;
LevaPulsante.OnClick := SwitchState;
end;
end;
Code for creating Button object :
procedure TFMain.FormActivate(Sender: TObject);
var
i: Integer;
posPulsante: Integer;
begin
posPulsante := 30;
for i := 1 to 3 do
begin
SwitchButton(posPulsante);
posPulsante := posPulsante + 50;
end;
end;
Would be nice to have some : if SwitchButton 1 is clicked then do something. if SwitchButton 2 is clicked then do something else.
I think this is an acceptable use of the Tag property. You can specify some number, for example the index of the for loop, in each of the panels:
procedure TFMain.FormActivate(Sender: TObject);
var
i: Integer;
posPulsante: Integer;
begin
posPulsante := 30;
for i := 1 to 3 do
begin
SwitchButton(posPulsante, i {For example, add the tag as extra parameter});
posPulsante := posPulsante + 50;
end;
end;
And then set that tag in the button, here using the added parameter indexPulsante.
procedure TFMain.SwitchButton(posPulsante: Integer; indexPulsante: Integer);
var
i: Integer;
posDescrizionePulsante: Integer;
strDescrizione: String;
begin
...
LevaPulsante.Tag := indexPulsante;
end;
And then, in the event handler (which I think you called SwitchState), you'll have the Sender, which is the control that was clicked (the panel, in your case).
procedure TFMain.SwitchState(Sender: TObject);
begin
case (Sender as TComponent).Tag of
1: ShowMessage('You clicked the first. Do something.')
2: ShowMessage('Do something else.')
else
ShowMessage('You clicked another button than 1 or 2');
end;
end;
NB: Tag is introduced in TComponent and therefore also available in TPanel. In the code above I only typecast to TComponent, because it doesn't matter that it's a panel to get the tag, but if you want to use other properties, a more specific cast may be needed. I like to keep the cast generic, to make it easier to make changes like switching to another type than TPanel, for instance when you actually gonna make a component out of this (inherited from TCustomControl?), or use a third party component.

Delphi: Menu Hint bug

I got the code below from the website ThoughtCo. (Zarko Gajic) - it presents the hint near the mouse pointer when it is in the menu item:
However, it has a bug: when the menu is opened by the keyboard the tooltip appears next to the mouse pointer, regardless of the location on the screen where the mouse pointer is:
I tried to fix the bug by adding the lines that are commented. Now the error is that the hint always appears regardless of whether you click the menu item quickly or not.
How to fix this problem?
procedure TfrmPrincipal.WMMenuSelect(var Msg: TWMMenuSelect);
var
menuItem : TMenuItem;
hSubMenu : HMENU;
hPopupWnd: HWND; // Added
R: TRect; // Added
Pt: TPoint; // Added
begin
inherited;
menuItem := nil;
if (Msg.MenuFlag <> $FFFF) or (Msg.IDItem <> 0) then
begin
if Msg.MenuFlag and MF_POPUP = MF_POPUP then
begin
hSubMenu := GetSubMenu(Msg.Menu, Msg.IDItem);
menuItem := Self.Menu.FindItem(hSubMenu, fkHandle);
end
else
begin
menuItem := Self.Menu.FindItem(Msg.IDItem, fkCommand);
end;
end;
hPopupWnd := FindWindow('#32768', nil); // Added
if hPopupWnd = 0 then Exit; // Added
GetWindowRect(hPopupWnd, R); // Added
GetCursorPos(Pt); // Added
if PtInRect(R, Pt) then // Added
miHint.DoActivateHint(menuItem)
else // Added
miHint.DoActivateHint(nil); // Added
end;
constructor TMenuItemHint.Create(AOwner: TComponent);
begin
inherited;
showTimer := TTimer.Create(self);
showTimer.Interval := Application.HintPause;
hideTimer := TTimer.Create(self);
hideTimer.Interval := Application.HintHidePause;
end;
destructor TMenuItemHint.Destroy;
begin
hideTimer.OnTimer := nil;
showTimer.OnTimer := nil;
self.ReleaseHandle;
inherited;
end;
procedure TMenuItemHint.DoActivateHint(menuItem: TMenuItem);
begin
hideTime(self);
if (menuItem = nil) or (menuItem.Hint = '') then
begin
activeMenuItem := nil;
Exit;
end;
activeMenuItem := menuItem;
showTimer.OnTimer := ShowTime;
hideTimer.OnTimer := HideTime;
end;
procedure TMenuItemHint.HideTime(Sender: TObject);
begin
self.ReleaseHandle;
hideTimer.OnTimer := nil;
end;
procedure TMenuItemHint.ShowTime(Sender: TObject);
var
r : TRect;
wdth : integer;
hght : integer;
begin
if activeMenuItem <> nil then
begin
wdth := Canvas.TextWidth(activeMenuItem.Hint);
hght := Canvas.TextHeight(activeMenuItem.Hint);
r.Left := Mouse.CursorPos.X + 16;
r.Top := Mouse.CursorPos.Y + 16;
r.Right := r.Left + wdth + 6;
r.Bottom := r.Top + hght + 4;
ActivateHint(r,activeMenuItem.Hint);
end;
showTimer.OnTimer := nil;
end;
WM_MENUSELECT tells you whether the menu item is being selected by mouse or keyboard.
If the MF_MOUSESELECT flag is present, use the mouse coordinates provided by GetCursorPos() (or the VCL's TMouse.CursorPos wrapper), or GetMessagePos().
If the flag is not present, use GetMenuItemRect() to get the screen coordinates of the bounding rectangle of the specified menu item, and then use whatever coordinates you want that are within that rectangle (centered, bottom edge, etc).
You should NOT be trying to work with the menu window directly at all, so get rid of your calls to FindWindow(), GetWindowRect(), and PtInRect().

Draw TPanel and TSplitter at runtime results in wrong component order

If I create multiple TPanel and TSplitter components at runtime into a TScrollBox, the order of the components is wrong. If I call drawInput() 3 times, the scrollbox contains 3 panels followed by 3 splitters instead of 1 panel followed by 1 splitter (repeated).
How can I force the correct order?
Here is a screenshot
function drawInput(owner: TWinControl): TWinControl;
var
panel: TPanel;
edit: TEdit;
splitter: TSplitter;
begin
panel := TPanel.Create(owner);
panel.Parent := owner;
panel.Align := alTop;
panel.AlignWithMargins := True;
panel.BorderWidth := 0;
panel.Color := clRed;
panel.BorderStyle := bsNone;
panel.ParentBackground := False;
panel.Ctl3D := False;
edit := TEdit.Create(panel);
edit.Parent := panel;
edit.Align := alTop;
edit.AlignWithMargins := True;
edit.Text := 'foo';
edit.Margins.Left := 5;
edit.Margins.top := 5;
edit.Margins.Bottom := 5;
edit.Margins.Right := 5;
splitter := TSplitter.Create(owner);
splitter.Parent := owner;
splitter.Align := alTop;
splitter.Beveled := True;
splitter.Height := 3;
end;
EDIT:
Here is how I call this function:
procedure TForm2.Button1Click(Sender: TObject);
var
form: TForm;
sb: TScrollBox;
begin
form := TForm.Create(Application);
sb := TScrollBox.Create(form);
sb.Parent := form;
sb.Align := alClient;
sb.Color := clBlack;
drawInput(sb);
drawInput(sb);
drawInput(sb);
drawInput(sb);
form.Width := 300;
form.Height := 700;
form.ShowModal;
end;
Position your panel + splitter then set the alignment
You can position you panel below all other components by aligning it to the client
function drawInput(owner: TWinControl): TWinControl;
var
panel: TPanel;
edit: TEdit;
splitter: TSplitter;
begin
panel := TPanel.Create(owner);
panel.Parent := owner;
///
panel.Align := alclient;
///
panel.Align := alTop;
panel.AlignWithMargins := True;
panel.BorderWidth := 0;
panel.Color := clRed;
panel.BorderStyle := bsNone;
panel.ParentBackground := False;
panel.Ctl3D := False;
splitter := TSplitter.Create(owner);
splitter.Parent := owner;
//
splitter.top := panel.top+panel.height;
//
splitter.Align := alTop;
splitter.Beveled := True;
splitter.Height := 3;
end;
Delphi's alignment logic can be hard at times. But the following works. Note the line splitter.Top := -1;
function drawInput(owner: TWinControl): TWinControl;
var
panel: TPanel;
edit: TEdit;
splitter: TSplitter;
begin
splitter := TSplitter.Create(owner);
splitter.Parent := owner;
splitter.Align := alTop;
splitter.Beveled := True;
splitter.Height := 3;
splitter.Top := -1;
panel := TPanel.Create(owner);
panel.Parent := owner;
panel.Align := alTop;
panel.AlignWithMargins := True;
panel.BorderWidth := 0;
panel.Color := clRed;
panel.BorderStyle := bsNone;
panel.ParentBackground := False;
panel.Ctl3D := False;
edit := TEdit.Create(panel);
edit.Parent := panel;
edit.Align := alTop;
edit.AlignWithMargins := True;
edit.Text := 'foo';
edit.Margins.Left := 5;
edit.Margins.top := 5;
edit.Margins.Bottom := 5;
edit.Margins.Right := 5;
end;
Here's the code that works for me on XE5. I still have to solve my problem but at least I fixed yours :)
procedure drawInput(owner: TWinControl; var t: integer);
var
panel: TPanel;
edit: TEdit;
splitter: TSplitter;
begin
panel := TPanel.Create(owner);
panel.Parent := owner;
panel.Align := alTop;
panel.AlignWithMargins := True;
panel.BorderWidth := 0;
panel.Color := clRed;
panel.BorderStyle := bsNone;
panel.ParentBackground := False;
panel.Ctl3D := False;
panel.Top := t;
t := panel.Top + panel.Height + 1;
edit := TEdit.Create(panel);
edit.Parent := panel;
edit.Align := alTop;
edit.AlignWithMargins := True;
edit.Text := 'foo';
edit.Margins.Left := 5;
edit.Margins.top := 5;
edit.Margins.Bottom := 5;
edit.Margins.Right := 5;
splitter := TSplitter.Create(owner);
splitter.Parent := owner;
splitter.Align := alTop;
splitter.Beveled := True;
splitter.Height := 3;
splitter.Top := t;
t := splitter.Top + splitter.Height + 1;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
form: TForm;
sb: TScrollBox;
t: integer;
begin
form := TForm.Create(Application);
sb := TScrollBox.Create(form);
sb.Parent := form;
sb.Align := alClient;
sb.Color := clBlack;
t := 0;
drawInput(sb, t);
drawInput(sb, t);
drawInput(sb, t);
drawInput(sb, t);
form.Width := 300;
form.Height := 700;
form.ShowModal;
end;
In one of my applications, I have a function that creates a TImage and follows it with a TSplitter with the parent and containing control being a TScrollbox (sbScroller). The function is either called by the end user (tied to a TButton OnClick event) when they select an image or when the program starts it loads a previously loaded set of images each divided by a TSplitter.
It works when run alone by itself (creating one TImage + TSplitter pairing) or when run in a continuous loop to create multiple pairings. The key element in getting it to work seems to the positioning of the TSplitter.Top property as the previous answer says:
procedure AddImage(AFilename: string);
var
Image: TImage;
begin
Image := TImage.Create(sbScroller);
with Image do
begin
Image.Parent := sbScroller;
Left := 0;
Top := 0;
Width := 150;
Height := 150;
Constraints.MinHeight := 128;
Align := alTop;
Anchors := [akLeft, akTop, akRight];
Proportional := True;
Stretch := True;
Visible := True;
end;
if sbScroller.ControlCount > 0 then
with TSplitter.Create(sbScroller) do
begin
Parent := sbScroller;
Top := Image.Top;
Align := alTop;
Color := clGray;
end;
end;

Delphi XE6 firemonkey component alignment problems when added at runtime

i wanto dynamic add 5 TLable in my iOS app.
like this
Procedure Form1.FormCreate(Sender: TObject)
var
I: Integer;
begin
for I := 1 to 5 do
begin
with TLabel.Create(Self) do
begin
Parent := self;
Align := TAlignLayout.Top;
Height := 50;
Text := IntToStr(I);
end;
end;
end;
i think the order is 12345, but I get 15432.
What can I do to get the desired results?
You must give a chance to the aligning algorithm to do what you want.
procedure TForm1.FormCreate(Sender: TObject);
var
I: Integer;
begin
for I := 1 to 5 do
begin
with TLabel.Create(Self) do
begin
Parent := self;
Align := TAlignLayout.alTop;
Height := 50;
Position.Y := I*Height; //add this line
Text := IntToStr(I);
end;
end;
end;

How to show dialog box with two buttons ( Continue / Close ) in Delphi

I want to create a warning dialog box which asks the users if the information typed during signup was correct, and asks him wether he want to continue or close that dialog and correct his information.
var
td: TTaskDialog;
tb: TTaskDialogBaseButtonItem;
begin
td := TTaskDialog.Create(nil);
try
td.Caption := 'Warning';
td.Text := 'Continue or Close?';
td.MainIcon := tdiWarning;
td.CommonButtons := [];
tb := td.Buttons.Add;
tb.Caption := 'Continue';
tb.ModalResult := 100;
tb := td.Buttons.Add;
tb.Caption := 'Close';
tb.ModalResult := 101;
td.Execute;
if td.ModalResult = 100 then
ShowMessage('Continue')
else if td.ModalResult = 101 then
ShowMessage('Close');
finally
td.Free;
end;
end;
Note: This will only work on Windows Vista or later.
if delphi then
if mrYes=MessageDlg('Continue?',mtwarning,[mbYes, mbNo],0) then
begin
//do somthing
end
else
exit; //go out
var
AMsgDialog: TForm;
abutton: TButton;
bbutton: TButton;
begin
AMsgDialog := CreateMessageDialog('This is a test message.', mtWarning,[]);
abutton := TButton.Create(AMsgDialog);
bbutton := TButton.Create(AMsgDialog);
with AMsgDialog do
try
Caption := 'Dialog Title' ;
Height := 140;
AMsgDialog.Width := 260 ;
with abutton do
begin
Parent := AMsgDialog;
Caption := 'Continue';
Top := 67;
Left := 60;
// OnClick :tnotyfievent ;
end;
with bbutton do
begin
Parent := AMsgDialog;
Caption := 'Close';
Top := 67;
Left := 140;
//OnClick :tnotyfievent ;
end;
ShowModal ;
finally
abutton.Free;
bbutton.Free;
Free;
end;
Based on this:
procedure HookResourceString(rs: PResStringRec; newStr: PChar);
var
oldprotect: DWORD;
begin
VirtualProtect(rs, SizeOf(rs^), PAGE_EXECUTE_READWRITE, #oldProtect);
rs^.Identifier := Integer(newStr);
VirtualProtect(rs, SizeOf(rs^), oldProtect, #oldProtect);
end;
const
SContinue = 'Continue';
SClose = 'Close';
procedure TForm1.Button1Click(Sender: TObject);
begin
HookResourceString(#SMsgDlgOK, SContinue);
HookResourceString(#SMsgDlgCancel, SClose);
if MessageDlg('My Message', mtConfirmation, [mbOK, mbCancel], 0) = mrOK then
begin
// OK...
end;
end;

Resources