Discussion:
Change appearance
(too old to reply)
Stark
2012-04-29 13:43:29 UTC
Permalink
I am creating a few buttons at runtime and I wanted to change appearance of
the button the user is hovering over with the mouse. I don't want to use the
font, which I already use for other purposes. I had the idea to surround the
button with a TBevel, which I could make visible when the mouse was over the
button.
The solution doesn't work. It looks like my code does not apply the property
(visible) to the Bevel, but rather to the Button. Here is my code:

I first create the components at Runtime (in the form OnShow):

for i := 0 to 10 do
begin
aButton := TButton.Create(Self);
aBevel := TBevel.Create(Self);

with aButton do
begin
Name := Format('Button%d', [i]);
Parent := Self;
Left := 16;
Top := 40 + i*25;
Width := 145;
Height := 20;
Caption := Format('Button %d', [i]);
OnMouseEnter := AnyBtnMouseEnter;
OnMouseLeave := AnyBtnMouseLeave;
end;

with aBevel do
begin
Parent := Self;
Left := aButton.Left-2;
Top := aButton.Top-2;
Width := aButton.Width+4;
Height := aButton.Height+4;
Shape:= bsFrame;
Style:= bsRaised;
visible:= false;
end;
end;

The following procedures fail their mission to show and hide the bevel when
the mouse is over the button: The OnMouseLeave proc makes the button not
visible !
procedure Tform1.AnyBtnMouseEnter(Sender: TObject);
begin
If Sender is TButton then
Tbevel(Sender).Visible := true;
end;

procedure Tform1.AnyBtnMouseLeave(Sender: TObject);
begin
If Sender is TButton then
Tbevel(Sender).Visible := false;
end;
Ph. B.
2012-04-30 09:04:09 UTC
Permalink
Post by Stark
I am creating a few buttons at runtime and I wanted to change appearance
of the button the user is hovering over with the mouse. I don't want to
use the font, which I already use for other purposes. I had the idea to
surround the button with a TBevel, which I could make visible when the
mouse was over the button.
The solution doesn't work. It looks like my code does not apply the
for i := 0 to 10 do
begin
aButton := TButton.Create(Self);
aBevel := TBevel.Create(Self);
with aButton do
begin
Name := Format('Button%d', [i]);
Parent := Self;
Left := 16;
Top := 40 + i*25;
Width := 145;
Height := 20;
Caption := Format('Button %d', [i]);
OnMouseEnter := AnyBtnMouseEnter;
OnMouseLeave := AnyBtnMouseLeave;
end;
with aBevel do
begin
Parent := Self;
Left := aButton.Left-2;
Top := aButton.Top-2;
Width := aButton.Width+4;
Height := aButton.Height+4;
Shape:= bsFrame;
Style:= bsRaised;
visible:= false;
end;
end;
The following procedures fail their mission to show and hide the bevel
when the mouse is over the button: The OnMouseLeave proc makes the
button not visible !
procedure Tform1.AnyBtnMouseEnter(Sender: TObject);
begin
If Sender is TButton then
Tbevel(Sender).Visible := true;
end;
procedure Tform1.AnyBtnMouseLeave(Sender: TObject);
begin
If Sender is TButton then
Tbevel(Sender).Visible := false;
end;
Hi,

If Sender is a TButton, cast it in a TBevel changes nothing...
You must identify which TButton raises the event, the TBevel which it is
attached and finally set its Visible property to True.
--
Philippe.
a***@aol.com
2012-05-02 21:29:48 UTC
Permalink
Post by Stark
I am creating a few buttons at runtime and I wanted to change appearance of
the button the user is hovering over with the mouse. I don't want to use the
font, which I already use for other purposes. I had the idea to surround the
button with a TBevel, which I could make visible when the mouse was over the
button.
The solution doesn't work. It looks like my code does not apply the property
  for i := 0 to 10 do
  begin
     aButton := TButton.Create(Self);
     aBevel := TBevel.Create(Self);
     with aButton do
     begin
       Name := Format('Button%d', [i]);
       Parent := Self;
       Left := 16;
       Top := 40 + i*25;
       Width := 145;
       Height := 20;
       Caption := Format('Button %d', [i]);
       OnMouseEnter := AnyBtnMouseEnter;
       OnMouseLeave := AnyBtnMouseLeave;
     end;
     with aBevel do
     begin
       Parent := Self;
       Left := aButton.Left-2;
       Top := aButton.Top-2;
       Width := aButton.Width+4;
       Height := aButton.Height+4;
       Shape:= bsFrame;
       Style:= bsRaised;
       visible:= false;
     end;
  end;
The following procedures fail their mission to show and hide the bevel when
the mouse is over the button: The OnMouseLeave proc makes the button not
visible !
procedure Tform1.AnyBtnMouseEnter(Sender: TObject);
begin
  If Sender is TButton then
     Tbevel(Sender).Visible := true;
end;
procedure Tform1.AnyBtnMouseLeave(Sender: TObject);
begin
  If Sender is TButton then
     Tbevel(Sender).Visible := false;
end;
The Sender will always be the button, so you have to get the bevel for
the particular button. For this we can use the Tag property of a
TComponent (and its descendants). Tag is a four-byte integer value
which can be used to store any four-byte value (ie a DWord). But we
must typecast any non-integer to an integer when we store the value.
And typecast the integer to the type of what we have stored when we
access it.

Then we must trigger "Mouse in button" and "Mouse not on a button"
events. For this you can use the MouseMove button event of the button,
and the MouseMove event of the form. We need only one ButtonMouseMove
event handler because we can use the Sender parameter to get the
particular button, then get its Tag, typecast to a TBevel, and set
Visible to false. ButtonMouseMove is called only when the mouse moves
over the button. It is not called when the mouse is off the button. So
we must use the FormMouseMove to set bevels to not visible, and we do
this for all the bevels,, because we don't know which button the mouse
has just come off. So we check all the entries in the form's
Components array, if it is a TButton, we check the Tag for a value >
0, if so we tyoe-cast it to a Bevel and set the Bevel.Visible to
false. A TComponent has a Tag property so we don't need to typecast
the Components[i] to a TButton.

To prevent a lot of unnecessary messages to set or clear Bevel.Visible
as the mouse moves, we check for the correct Bevel.Visible and only
change it if necessary.

procedure TForm1.FormCreate(Sender: TObject);
begin
{do this for all buttons with a bevel}
Button1.Tag := integer(Bevel1);
Bevel1.Visible := false;
end;

procedure TForm1.ButtonMouseMove(Sender: TObject; Shift: TShiftState;
X,
Y: Integer);
var
BtnBevel : TBevel;
begin
BtnBevel := TBevel(TButton(Sender).Tag);
BtnBevel.Visible := true;
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var
i : integer;
BtnBevel : TBevel;
begin
for i := 0 to Self.ComponentCount - 1 do
if (Self.Components[i] is TButton) then
if (Components[i].Tag > 0) then begin
BtnBevel := TBevel(Components[i].Tag);
if BtnBevel.Visible then
BtnBevel.Visible := false;
end; {if (Components[i].Tag > 0)}
{end; if (Components[i] is TButton)}
{end; for i := 0 to ComponentsCount - 1}
end;

You must select the ButtonMouseMove event handler yourself in each
button's events list. (after, of course changing the event handler's
name from the auto-entered Button??MouseMove to ButtonMouseMove as it
will be used for all appropriate buttons).

One Problem with using a TBevel is that (on my D3) the bevel width is
fixed. If you used a TPanel you could set the bevel width wider, &
hence more visible.

Storing object references in the Tag property is a powerful tool for
dealing with some issues of identity, or code reduction (ie only one
event handler for a number of controls instead of one for each
control).

Alan Lloyd
a***@aol.com
2012-05-03 06:59:09 UTC
Permalink
Post by Stark
I am creating a few buttons at runtime and I wanted to change appearance of
the button the user is hovering over with the mouse. I don't want to use the
font, which I already use for other purposes. I had the idea to surround the
button with a TBevel, which I could make visible when the mouse was over the
button.
The solution doesn't work. It looks like my code does not apply the property
  for i := 0 to 10 do
  begin
     aButton := TButton.Create(Self);
     aBevel := TBevel.Create(Self);
     with aButton do
     begin
       Name := Format('Button%d', [i]);
       Parent := Self;
       Left := 16;
       Top := 40 + i*25;
       Width := 145;
       Height := 20;
       Caption := Format('Button %d', [i]);
       OnMouseEnter := AnyBtnMouseEnter;
       OnMouseLeave := AnyBtnMouseLeave;
     end;
     with aBevel do
     begin
       Parent := Self;
       Left := aButton.Left-2;
       Top := aButton.Top-2;
       Width := aButton.Width+4;
       Height := aButton.Height+4;
       Shape:= bsFrame;
       Style:= bsRaised;
       visible:= false;
     end;
  end;
The following procedures fail their mission to show and hide the bevel when
the mouse is over the button: The OnMouseLeave proc makes the button not
visible !
procedure Tform1.AnyBtnMouseEnter(Sender: TObject);
begin
  If Sender is TButton then
     Tbevel(Sender).Visible := true;
end;
procedure Tform1.AnyBtnMouseLeave(Sender: TObject);
begin
  If Sender is TButton then
     Tbevel(Sender).Visible := false;
end;
There is a slightly better way to hide the bevel only once; and if you
use a TPanel there is another issue.

Firstly when the bevel (or panel) is shown set the form's OnMouseMove,
& once the bevel is hidden "lift" the form's OnMouseMove by setting it
to nil. It will then not be called when the mouse moves.

If you use a TPanel the IDE places the button on the TPanel and sets
the TPanel as the button's Parent. Hence when the panel is hidden, so
will the button be. You must change the button's Parent to the form
(or whatever the panel & button is on) and you must also set the
buttons Top & Left to be relative to the Form. This is readily done by
using the TPanel's ClientToScreen and the TForm's ScreenToClient
methods.

Here is my test code for both the TBevel (on Button1) and the TPanel
(on Button3) with comments :

procedure TForm1.FormCreate(Sender: TObject);
var
Btn3Point : TPoint;
begin
{bevel around Button1}
Button1.Tag := integer(Bevel1);
Bevel1.Visible := false;
{panel under Button3
... get coordinates for button relative to the form
instead of relative to panel}
Btn3Point := Panel1.ClientToScreen(point(Button3.Left,
Button3.Top));
Btn3Point := Form1.ScreenToClient(Btn3Point);
{... reference panel in button's tag}
Button3.Tag := integer(Panel1);
Button3.SetBounds(Btn3Point.X, Btn3Point.Y, Button3.Width,
Button3.Height);
{... now change button's Parent}
Button3.Parent := Self;
{... init panel's visibility}
Panel1.Visible := false;
{... lift form's OnMouseMove}
Self.OnMouseMove := nil;
end;

// using a bevel
procedure TForm1.ButtonMouseMoveA(Sender: TObject; Shift: TShiftState;
X,
Y: Integer);
begin
BevelShown := TBevel(TButton(Sender).Tag);
if not BevelShown.Visible then begin
BevelShown.Visible := true;
{set form's OnMouseMove}
Self.OnMouseMove := FormMouseMove;
end; {if not BevelShown.Visible}
end;

// using a panel
procedure TForm1.ButtonMouseMoveB(Sender: TObject; Shift: TShiftState;
X,
Y: Integer);
begin
PanelShown := TPanel(TButton(Sender).Tag);
if not PanelShown.Visible then begin
PanelShown.Visible := true;
{set form's OnMouseMove}
Self.OnMouseMove := FormMouseMove;
end; {if not PanelShown.Visible}
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
{for bevel around button}
if Assigned(BevelShown) then
BevelShown.Visible := false;
{for panel under button}
if Assigned(PanelShown) then
PanelShown.Visible := false;
{prevent further OnMouseMove calls}
Self.OnMouseMove := nil;
end;

Alan Lloyd
a***@aol.com
2012-05-03 07:03:10 UTC
Permalink
Post by a***@aol.com
There is a slightly better way to hide the bevel only once; and if you
use a TPanel there is another issue.
Drat & Double Drat !

I forgot to include two global variables to my code text - before the
FormCreate ...

var
BevelShown : TBevel;
PanelShown : TPanel;

Alan Lloyd
Stark
2012-05-05 18:44:34 UTC
Permalink
Thanks for the answer. I will read your suggestion with attention. In the
meantime someone suggested the following trick:

Replace my original declaration:
aBevel := TBevel.Create(Self);
aButton := TButton.Create(Self);
with:
aBevel := TBevel.Create(Self);
aButton := TButton.Create(aBevel);
so to make the Bevel the owner of each Button. My procedure must then be
changed form:

procedure Tform1.AnyBtnMouseEnter(Sender: TObject);
begin
If Sender is TButton then
Tbevel(Sender).Visible := true;
end;

to:

procedure Tform1.AnyBtnMouseEnter(Sender: TObject);
begin
If Sender is TButton then
TBevel(TButton(Sender).Owner).Visible := true;
end;
----------------------------------
I tried and this works. Moving the mouse over the buttons, the bevel appears
on the focused button.
Unfortunately, I am disappointed by poor appearance of what I was trying
achieve with the bevel. It doesn't look as I expected ..
a***@aol.com
2012-05-07 06:17:13 UTC
Permalink
Post by Stark
Unfortunately, I am disappointed by poor appearance of what I was trying
achieve with the bevel. It doesn't look as I expected ..
That's why I suggested using a TPanel. You can then set the width of
the bevel.

Alan Lloyd
Stark
2012-05-08 14:13:40 UTC
Permalink
In the attempt of using a TPanel, I tried just replacing it in my code to
the TBevel but it doesn't work; when moving the mouse over he button, it
starts flashing (you can only see a flickering border). Clicking the mouse
on it (without releasing it) reveals the panel but not the button and
finally releasing the mouse button it shows the button again. Can you
understand why ?
In the meantime I tried your solution and it works. Anyway, here is my code
:

procedure TForm1.FormShow(Sender: TObject);

var
aButton: TButton;
aPanel: TPanel;
i: integer;
begin
for i := 0 to 10 do
begin
aPanel := TPanel.Create(Self);
aButton := TButton.Create(aPanel);
with aButton do
begin
Name := Format('Button%d', [i]);
Parent := Self;
Left := 16;
aButton.Top := 40 + i*25;
Width := 145;
Height := 20;
Font.Style := Font.Style+[fsBold];
OnClick := AnyButtonClick;
OnMouseEnter := AnyBtnMouseEnter;
OnMouseLeave := AnyBtnMouseLeave;
end;
with aPanel do
begin
Name := Format('Panel%d', [i]);
Parent := Self;
Left := aButton.Left-2;
Top := aButton.Top-2;
Width := aButton.Width+4;
Height := aButton.Height+4;
BevelWidth := 4;
BorderStyle := bsSingle;
visible:= false;
end;
end;
end;
end;
end;

procedure Tform1.AnyBtnMouseEnter(Sender: TObject);
begin
If Sender is TButton then
TPanel(TButton(Sender).Owner).Visible := true;
end;

procedure Tform1.AnyBtnMouseLeave(Sender: TObject);
begin
If Sender is TButton then
TPanel(TButton(Sender).Owner).Visible := false;
end;
Stark
2012-05-09 17:40:32 UTC
Permalink
Post by Stark
Unfortunately, I am disappointed by poor appearance of what I was trying
achieve with the bevel. It doesn't look as I expected ..
That's why I suggested using a TPanel. You can then set the width of
the bevel.

Alan, can you explain why you used MouseMove rather then MouseEnter and
Leave ?

Loading...