Ask the Delphi Pro 10-Minute Solutions

Make a List Box Track the Mouse
By Brendan Delumpa

Recently, a user asked me if there was a way to make a list box track the mouse (have the currently selected item be the one under the pointer) as it moves over the items much like a combo box does when you drop down its list. I responded that if he really needed a quick solution for his project, he could drop a TListBox component onto his form and write an event handler for the OnMouseMove event. The code would be fairly simple—just a couple of lines like this:


procedure TForm1.ListBox1MouseMove(Sender: TObject; Shift: TShiftState; 
                                   X, Y: Integer);
var
  P : TPoint;
begin
  {Grab the position of the mouse and put it into a TPoint}
  P.X := X;
  P.Y := Y;
  {Set the ItemIndex of the list box to the Item under the current mouse
   coordinates}
  ListBox1.ItemIndex := ListBox1.ItemAtPos(P, True);
end;

The ItemAtPos method of TListBox makes all of this work. As the mouse passes over the list box, the event handler picks up the coordinates. ItemAtPos then passes back the index of the list under the current mouse position. Doing the assignment to the ItemIndex property changes the current selection in the list box—pretty neat.

Well, that was the quick and dirty solution, but there's more to it. It is possible that you could use this functionality in more than one project. In that case, you'd have to create a new component. Now while the solution is simple, and in fact, uses pretty much the same code as before, finding the right method to override in the ancestral tree isn't as readily apparent as you might think. This is because the handler code for a mouse move isn't defined at the TListBox level. It's actually defined about four parent levels up at the TControl level. So visibility is a bit of an issue.

Furthermore, for those of you who have written event handlers for components, the natural tendency (and usually the correct one) would be to write a Windows message handler for WM_MOUSEMOVE. Well, you can, but in order to get the coordinates, you need to get the high- and low-order bits from the lParam parameter of the message record to resolve the coordinate values. It's just a bit more coding.

As I mentioned earlier, you can use code similar to the previous listing for the new descendant class. And you can do this with TControl's dynamic method MouseMove. MouseMove's declaration is almost exactly like the OnMouseMove event handler. It's just missing the Sender parameter. The other parameters Shift, X, and Y are all there. How convenient! So with MouseMove, you've found the method to override.

Before I give you the code listing, you might wonder why I chose to descend from TCustomListBox, and not TListBox. The reason for this is simple: TListBox surfaces a property called MultiSelect that allows a user to select multiple items. Within the scope of this solution, that property is meaningless. Why? Because as the mouse moves over the list, the ItemIndex property constantly changes. You just can't select multiple items in this case. With that in mind, descending from TCustomListBox makes more sense because you can leave out the MultiSelect property yet retain all the other functionality of TListBox. Here's the code listing:


unit EnhListBox;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TEnhListBox = class(TCustomListBox)
  private
    { Private declarations }
  protected
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
  public
    { Public declarations }
  published
    property Align;
    property Anchors;
    property BiDiMode;
    property BorderStyle;
    property Color;
    property Columns;
    property Constraints;
    property Ctl3D;
    property DragCursor;
    property DragKind;
    property DragMode;
    property Enabled;
    property ExtendedSelect;
    property Font;
    property ImeMode;
    property ImeName;
    property IntegralHeight;
    property ItemHeight;
    property Items;
    property ParentBiDiMode;
    property ParentColor;
    property ParentCtl3D;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ShowHint;
    property Sorted;
    property Style;
    property TabOrder;
    property TabStop;
    property TabWidth;
    property Visible;
    property OnClick;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnDrawItem;
    property OnEndDock;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMeasureItem;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property OnStartDock;
    property OnStartDrag;
  end;

procedure Register;

implementation

{ TEnhListBox }

procedure TEnhListBox.MouseMove(Shift: TShiftState; X, Y: Integer);
var
  P : TPoint;
begin
  //Make sure to call the inherited method first!
  inherited MouseMove(Shift, X, Y);
  //Grab the current mouse coordinates
  P.X := X;
  P.Y := Y;
  //Set the index
  Self.ItemIndex := Self.ItemAtPos(P, True);
end;

procedure Register;
begin
  RegisterComponents('BD', [TEnhListBox]);
end;

end.
 
Other 10-Minute Solutions
 Trapping Messages Sent to an Application
 Getting the Number of Records From a Fixed-Length ASCII File
 Performing Incremental Searches with a TListbox
 Resizing the Drop-down List of a TComboBo
 Disabling the System Keys from Your Application
 Making A Secondary Form Independent of the Main Form
 Creating a System Tray Application
 Opening and Closing a CD Tray
 Hiding an Application from Windows
 Make a List Box Track the Mouse
 Running a Program at Startup
 Creating A Skin Component
 Dynamically Load Components From Packages at Run Time
 Obtain All Values from Multivalue Input Fields in WebBroker Applications
 Migrate Your BDE Applications to Linux with dbExpress
 Create a Multiline Button Component
 Update and Maintain dbExpress's Unidirectional, Read-Only Datasets
 Use ActionBands to Enable End Users to Customize Your Delphi Applications
 Produce HTML Reporting Output with WebBroker Components
  Convert XML Documents into Different Formats with the XSL Template Language


Ask the Delphi Pro | Who is the Pro? | Usage Policies | Ask a Question | Search | Feedback


Sponsored Links


Advertising Info  |   Member Services  |   Contact Us  |   Help  |   Feedback  |   Site Map
Jupiterweb networks

internet.comearthweb.comDevx.comClickZ

Search Jupiterweb:

Jupitermedia Corporation has four divisions:
JupiterWeb, JupiterResearch, JupiterEvents, and JupiterImages

Copyright 2004 Jupitermedia Corporation All Rights Reserved.
Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

Jupitermedia Corporate Info | Newsletters | Tech Jobs | E-mail Offers