?
Solved

How to get CM_MOUSEENTER and CM_MOUSELEAVE to work with any control without modifying the components

Posted on 2005-02-26
17
Medium Priority
?
2,334 Views
Last Modified: 2012-05-05
Hello Experts,

I want to control the display of certain elements when the mouse cursor moves on to, and off,  a control such as a TBitBtn, TImage, etc. I have found very useful examples in the EE solutions repository. The code I use for accomplishing what I need is as follows:

In the form's unit interface:

.
private
    procedure CMMouseEnter(var Msg: TMessage); message CM_MOUSEENTER;
    procedure CMMouseLeave(var Msg: TMessage); message CM_MOUSELEAVE;
.
.  
and in the form's implementation section:
.
procedure TForm1.CMMouseEnter(var Msg: TMessage);
begin
  if (Msg.LParam = Integer(BitBtn1)) then
  begin
    Image1.Visible := True;
  end;
end;

procedure TForm1.CMMouseLeave(var Msg: TMessage);
begin
  if (Msg.LParam = Integer(BitBtn1)) then
  begin
    Image1.Visible := False;
  end;
end;

This works quite well BUT ONLY IF BITBTN1 IS DIRECTLY PLACED ON FORM1. If I place BitBtn1 on a Panel1, which in turn is placed on Form1, the above code I use does not work. I have a strong feeling this has something to do with the parent-child relationship and handling of CM_MOUSEENTER and CM_MOUSELEAVE at the parent.
How can I get this to work with controls not directly placed on the Form1, but on panels and sub-panels of panels on a form? I DO NOT WANT TO MODIFY ANY COMPONENTS.

Can anyone help? - I need some example code urgently

Thanks,
JDJVR
0
Comment
Question by:JDJVR
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 7
  • 4
  • 3
  • +1
17 Comments
 
LVL 11

Expert Comment

by:calinutz
ID: 13411182
I think that this is what you need....


...
  private
    FFocusControl: TControl;
    procedure ApplicationIdle(Sender: TObject; var Done: Boolean);
    { Private declarations }
  public
    procedure OnEnter(Sender: TObject);
    procedure OnExit(Sender: TObject);
    { Public declarations }


procedure TForm1.ApplicationIdle(Sender: TObject; var Done: Boolean);
var
  CurControl: TControl;
  P: TPoint;
begin
  GetCursorPos(P);
  CurControl := FindDragTarget(P, True);
  if FFocusControl <> CurControl then
  begin
    if FFocusControl <> nil then
      OnExit(FFocusControl);
    FFocusControl := CurControl;
    if FFocusControl <> nil then
      OnEnter(FFocusControl);
  end;
end;

procedure TForm1.OnEnter(Sender: TObject);
begin
  //OnEnter code
  if (sender = Image2)or(sender=Label14) then
  begin
  Image2.Left:=Image2.Left+1;
  Image2.Picture.LoadFromFile('C:\save1.bmp');
  Label14.Font.Style:=[fsBold];
  end;
end;

procedure TForm1.OnExit(Sender: TObject);
begin
  //OnExit code
  if (sender = Image2)or(sender=Label14) then
  begin
  Image2.Left:=Image2.Left-1;
  Image2.Picture.LoadFromFile('C:\save2.bmp');
  Label14.Font.Style:=[];
  end;
end;
0
 
LVL 15

Assisted Solution

by:mikelittlewood
mikelittlewood earned 600 total points
ID: 13411303
You could alter the code slightly and use an imagelist instead to hold all the different images you want to swap between.
You also need to assign the Application.Idle function to the one you have written I think.

procedure TForm1.OnEnter(Sender: TObject);
begin
  //OnEnter code
  if (sender = Image1) then
  begin
    ImageList1.GetBitmap(1, Image1.Picture.Bitmap);
    Image1.Invalidate;
  end;
end;

procedure TForm1.OnExit(Sender: TObject);
begin
  //OnExit code
  if (sender = Image1) then
  begin
    ImageList1.GetBitmap(0, Image1.Picture.Bitmap);
    Image1.Invalidate;
  end;
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
  Application.OnIdle := ApplicationIdle;
end;
0
 
LVL 15

Expert Comment

by:mikelittlewood
ID: 13411339
also you should put in inherited in the idle event too I think.

procedure TForm1.ApplicationIdle(Sender: TObject; var Done: Boolean);
var
  CurControl: TControl;
  P: TPoint;
begin
  GetCursorPos(P);
  CurControl := FindDragTarget(P, True);
  if FFocusControl <> CurControl then
  begin
    if FFocusControl <> nil then
      OnExit(FFocusControl);
    FFocusControl := CurControl;
    if FFocusControl <> nil then
      OnEnter(FFocusControl);
  end;
  inherited;
end;
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
LVL 15

Expert Comment

by:mikelittlewood
ID: 13413530
In fact you could even make it more generic if you know for example you want the same action to happen to all TBitBtn's pictures on the form for example.
From reading your original code you don't want an image on the button when the mouse is off the control and you do when you hover the mouse over it.
You could use the tag property of the TBitBtn to hold to visible image index reference of the image you want to show and clear the image after it leaves the control.

procedure TForm1.OnEnter(Sender: TObject);
begin
  //OnEnter code
  if (sender is TBitBtn) then
  begin
    ImageList1.GetBitmap((Sender as TBitBtn).Tag, (Sender as TBitBtn).Glyph);
    (Sender as TBitBtn).Invalidate;
  end;
end;

procedure TForm1.OnExit(Sender: TObject);
begin
  //OnExit code
  if (sender is TBitBtn) then
  begin
    (Sender as TBitBtn).Glyph := nil
  end;
end;
0
 

Author Comment

by:JDJVR
ID: 13418088
Hi guys,
Tried your suggestions but could not get the code to work. I think we are getting lost in the details of what happens in the OnExit and OnEnter procedures. That is not my problem at this stage and therefore not important. If you take a look at my original code which worked well for any control placed directly on Form1 (Form1 is therefore the parent) there is no problem executing. But if a control (say BitBtn2) is located on a child of Form1 such as a Panel1, the code would work for Panel1, but not for any children of  Panel1. (If I moved the mouse cursor over Panel1 and had code in place to be executed, it would work - but not for BitBtn2 or any other child components of Panel1.) My original code therefore only works for ANY IMMEDIATE CHILD COMPONENTS OF FORM1.

To give more background, I need help text to appear when moving the mouse cursor on ANY control in the application (Buttons, ListBoxes, etc) and on ALL forms in the application. The help text is just a simple background image with a label which appears when the mouse is over the control, and disappears when the mouse moves off the control. I have made peace with the fact that I would in all probability have to duplicate whatever solution is appropriate, for every form in the application. The ideal is to have just one block of code to control this for the entire application, but I somehow feel this will in all probability not be possible. Maybe I'm wrong. You can now see why I do not want to modify components because it would mean I would have to modify every single component in the application only for displaying a pop-up help text graphic associated with it - which is ludicrous. Borland could have saved us a lot of hassles by publishing OnMouseEnter and OnMouseLeave events for most of their components.

Is there any way that MY ORIGINAL CODE can be altered to overcome the problem? We just need to implement some code that takes care of the parent-child issue - it should not be too difficult.

Thanks,
JDJVR
0
 

Author Comment

by:JDJVR
ID: 13418104
I am increasing the points to 300 - The difficulty grade may not be that high (I don't know) but I need a solution urgently.
0
 
LVL 26

Accepted Solution

by:
EddieShipman earned 600 total points
ID: 13420280
OK, you can add the CMMouseleave/enter to all bitbtns like this:

type
  TBitBtn = class(Buttons.TBitBtn)
  private
    procedure CMMouseEnter(var Msg: TMessage); message CM_MOUSEENTER;
    procedure CMMouseLeave(var Msg: TMessage); message CM_MOUSELEAVE;
  end;

  TForm1 = class(TForm)
    BitBtn1: TBitBtn;
    Panel1: TPanel;
    BitBtn2: TBitBtn;
    Image1: TImage;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation



{$R *.dfm}

{ TMouseControl }

procedure TBitBtn.CMMouseEnter(var Msg: TMessage);
begin
  Form1.Image1.Visible := True;
end;

procedure TBitBtn.CMMouseLeave(var Msg: TMessage);
begin
  Form1.Image1.Visible := False;
end;


Works for any control.
0
 

Author Comment

by:JDJVR
ID: 13421498
Hello EddieShipman!
I knew if I tried long enough I would somehow coax you to give some input. The code you supplied works fine if the same action is required for all BitBtns on the form. This means that the same graphic is displayed if the mouse cursor moves over ANY BitBtn on the form. But the problem is that each BitBtn has its own specific associated graphic, so I'm afraid the solution you gave does not work for me. In some way or another each BitBtn (or any other control, for that matter) must be identified by a code section such as:
if (Msg.LParam = Integer(BitBtn1)) then
  begin
    Image1.Visible := True;
  end;
in order to get the correct image to appear. As I have explained before, the above code works for all controls directly parented by Form1 as per my original question, but not for subsequent child controls lower in the object hierarchy. Any ideas?

JDJVR
0
 

Author Comment

by:JDJVR
ID: 13421516
I have tried incorporating the above code into procedure CMMMouseEnter/Leave but it does not work - forgot to mention this.
0
 
LVL 26

Expert Comment

by:EddieShipman
ID: 13422102
Then just check the sender's name in the CMMouseEnter/Leave, duh!!!

 if TBitBtn(Sender).Name = 'BitBtn1' then
 begin
  // do whatever for BitBtn1
 end;

 if TBitBtn(Sender).Name = 'BitBtn2' then
 begin
  // do whatever for BitBtn2
 end;

Or,use the tag property and use a case statement:

  case  TBitBtn(Sender).Tag of
  0:
    // do BitBtn1's stuff here
  1:
    // do BitBtn2's stuff here
  2:
    // do BitBtn3's stuff here
  end;
0
 

Author Comment

by:JDJVR
ID: 13423940
Yeah, I realized it a while after typing the last comment - but its late over here and I'm not thinking straight anymore. I also got the code supplied by mikelittlewood to work, so the problem's solved. I'll  be splitting the points between EddieShipman and mikelittlewood. Going to catch some shut-eye now.

Thanks,
JDJVR
0
 
LVL 11

Expert Comment

by:calinutz
ID: 13428399
Your question was:
" I want to control the display of certain elements when the mouse cursor moves on to, and off,  a control such as a TBitBtn, TImage, etc. I have found very useful examples in the EE solutions repository."

This was your question... and my answer... was first and it did what you asked for in your question... right?
 Wasn't I supposed to get at least a part of the points?
Curious...
:(
No offense << mikelittlewood >> but your answer was taken from mine and modified.
I do not need those points... It just seemed unfair, that's all.
I guess I will not be answering << JDJVR >> any more questions then. I am quite busy most of the time.
I do not like WASTING my time.
0
 
LVL 15

Expert Comment

by:mikelittlewood
ID: 13428730
Hi Calinutz,

Yes I did just modify your answer, but I was just trying to give some other alternatives for storing his images more than anything.
I didn't see any need to modify much of your original code as I believe your answer was the method I would have used.

No offense taken and I hope the above message vindicates your answer.

Cheers
Mike
0
 

Author Comment

by:JDJVR
ID: 13431929
Hello calinutz and mikelittlewood,

I am sorry if I offended anyone through the points awarding, but let me explain my motivation:

I am also in the middle of a hectic schedule, trying to fix a hundred things simultaneously, mostly late at night. When calinutz offered a solution, I was at that stage already overworked and could not get his example code to work. calinutz did not point out the ApplicationIdle procedure link with the Application.OnIdle event, which caused me an entire late night trying to get it working, WASTING MY TIME, which I also do not enjoy.

I am not a Delphi guru, this is why I depend on the experts to provide solutions as clearly as possible. The experts unfortunately assume us nitwits to know more than we actually do. So calinutz offered a partial solution but the "little extra" offered by mikelittlewood got me to get the thing working very quickly and I therefore decided to award him half the points. EddieShipman was the only expert to actually address my original question, but the other solution proved equally suitable.

In retrospect, I probably should have given calinutz his share of the points but it was late at night, I was tired, frustrated and not thinking straight (you can see that from my last communication with EddieShipman). So, calinutz, please accept my apologies - next time I will leave points awarding for the next morning when my brain works properly. Hope you understand.

Regards
JDJVR

0
 
LVL 26

Expert Comment

by:EddieShipman
ID: 13433195
You can re-open the question and split the points diferently if you want.
Go to Community Support and ask to reopen for split.
0
 

Author Comment

by:JDJVR
ID: 13433613
Nah, what's done is done. Spent too much time on this issue already. Hope to do better next time.
JDJVR
0
 
LVL 11

Expert Comment

by:calinutz
ID: 13437601
As I said before : I do not need those points... It just seemed unfair at that time, that's all.
I think I know what it is like to work late ( I usually wake up at 7 in the morning , go to work ,and go to bed at 4-5 in the morning... and meanwhile I work... so I know how it is like to be tired).
Appologies accepted
0

Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
Add bar graphs to Access queries using Unicode block characters. Graphs appear on every record in the color you want. Give life to numbers. Hopes this gives you ideas on visualizing your data in new ways ~ Create a calculated field in a query: …
In this video, Percona Solution Engineer Dimitri Vanoverbeke discusses why you want to use at least three nodes in a database cluster. To discuss how Percona Consulting can help with your design and architecture needs for your database and infras…
Suggested Courses

777 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question