Solved

Controls do not recieve middle mouse button events occurring outside the client area.

Posted on 2011-02-18
9
653 Views
Last Modified: 2012-05-11
I have a TImage-derived component to which I'm attempting to add middle-click panning functionality.  It seems that middle mouse events do not get delivered to my component when the mouse cursor is outside of it's client area, like left and right mouse events do.

I've tried intercepting the WM_NCMBUTTONUP message using a message handler, and even overriding the WndProc function, and I still never see this message come through.

The component also does not seem to receive Mouse Move messages when the cursor is outside the client area and the middle button is down.  It does receive the non-client Mouse Moves when the left or right button is down.

I can provide a small demo project if what I'm asking isn't clear enough.
0
Comment
Question by:ShawnCurry
9 Comments
 
LVL 24

Expert Comment

by:jimyX
ID: 34930436
Could you provide a demo or show some code please?
0
 
LVL 3

Author Comment

by:ShawnCurry
ID: 34931445
I've attached a demo that I've created exploring 2 alternate methods for receiving the missing events.  All the results are essentially the same.

To Test:
Place the mouse cursor inside the client area of the component.
Click and release each mouse button individually.
 - Should receive MouseUp notification
For each mouse button, place cursor in client area, click and drag outside client area, release.
 - Should receive MouseUp notification
 * Only receive MouseUp notification for left mouse button.

MiddleMouse.zip
0
 
LVL 24

Expert Comment

by:jimyX
ID: 34931855
That seems not catching. I can think of work around solution:
I used a Panel but you can apply this to the descendant components as well.
MiddleMouseUpdated.zip
0
 
LVL 24

Expert Comment

by:jimyX
ID: 34931872
Here is a complete demo for the three Mouse Buttons:
MiddleMouseUpdated-2.zip
0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
LVL 24

Expert Comment

by:jimyX
ID: 34931903
Just noticed that will not work outside the client area.
Then you can try the global mouse hook which catches all the mouse events:
http://delphigeek.blogspot.com/2007/02/global-mouse-hook-in-delphi.html
0
 
LVL 32

Expert Comment

by:ewangoya
ID: 34934269

Why would you expect the component to receive messages that do not belong to it. If you click on the form, thats a mouse click on the form and not on your component.

Same as when you have a button on the form, you don't expect the button to get a mousedown event when you click on the form, imagine what kind of problems we would run into.
0
 
LVL 3

Author Comment

by:ShawnCurry
ID: 34934504
The Mouse Hook method does look promising; but I wish I didn't have to put it into a DLL.  Do you have any idea how to figure out what thread I need to attach it to in order to allow it to live in my application?

I am not interested in listening for every single mouse event that occurs.  I'm really only interested in a very narrow set of them that I think I should be receiving now in order to make dragging behavior consistent for all mouse buttons.

Normally, a component wouldn't receive Mouse Move events when the cursor is outside it's client area.  However, they do if the left mouse button is down.  For example, try highlighting some text on this page, and drag the mouse cursor outside of the browser window.  The text selection still responds - or at least that seems normal for Windows applications.  Likewise, I still get the Mouse Up event (for the left button) so long as a Mouse Down event occurred inside the client area.

So, I'm only interested in certain events, and only under certain conditions:

Precondition: Mouse Down inside client area.
Deliver the missing non-client move events.
Deliver the missing non-client mouse up event if it occurs.
0
 
LVL 21

Accepted Solution

by:
developmentguru earned 500 total points
ID: 34935500
Here is a small project that demonstrates capturing all mouse events to a single control, without the use of a DLL.  Place a single panel on your main form in a new project.  Tie in the events as shown in the code.  You will notice that if you move the mouse over the panel that it's caption shows the mouse coordinates for mouse move messages that happen while the mouse is over the control.  Now, hold down the middle mouse button while over the panel and move the mouse off the panel while holding down the middle mouse button.  You will see that you now get mouse move messages even when you are not on the control (until you let go of the middle mouse button).

Let me know if you need more.
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    procedure Panel1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure Panel1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure Panel1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Panel1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  //start capture of mouse messages if the middle button just got pressed.
  if Button = mbMiddle then
    SetCaptureControl(Panel1);
end;

procedure TForm1.Panel1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  Panel1.Caption := IntToStr(X) + ' ' + IntToStr(Y);
end;

procedure TForm1.Panel1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  //end capture of mouse messages if middle button has been let go.
  if Button = mbMiddle then
    SetCaptureControl(nil);
end;

end.

Open in new window

0
 
LVL 3

Author Closing Comment

by:ShawnCurry
ID: 34937246
Perfect.  Exactly what I was looking for.  Short and sweet.
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

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

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…
With Secure Portal Encryption, the recipient is sent a link to their email address directing them to the email laundry delivery page. From there, the recipient will be required to enter a user name and password to enter the page. Once the recipient …

948 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

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now