While working on my dzMdbViewer I wondered why the mouse wheel didn’t seem to scroll the listbox containing the table names but did work in the dbtable. It turned out to be a misconception on my side:
The listbox contained only a few items so it didn’t have a vertical scroll bar. The default behaviour is to scroll only the view without changing the currently selected item. Of course this does not have any effect if all items are already visible.
But that wasn’t what I wanted the mouse wheel to do. I wanted it to have the same effect as on the dbtable, that is: Select the next/previous line.
Unfortunately a listbox does not have an OnMouseWheel event (a dbtable does have one), so I could not simply assign an event handler and be done with it. I resorted to a proven hack and created an interposer class that overrides TListBox.DoMouseWheel:
type
TListBox = class(StdCtrls.TListBox)
///<summary>
/// Overrides the default behaviour of the TListbox for mouse wheel scrolling:
/// Rather than scroll the view without affecting the selected item, select the
/// next/previous item as if the user pressed the up/down arrow key. </summary>
function DoMouseWheel(_Shift: TShiftState; _WheelDelta: Integer; _MousePos: TPoint): Boolean; override;
end;
type
Tfr_AccessDb = class(TFrame)
lb_Tables: TListBox;
private
public
end;
implementation
{$R *.dfm}
uses
Math,
u_dzVclUtils;
{ TListBox }
function TListBox.DoMouseWheel(_Shift: TShiftState; _WheelDelta: Integer; _MousePos: TPoint): Boolean;
var
Idx: Integer;
begin
// calculate the index of the item to select
Idx := ItemIndex - Sign(_WheelDelta);
if Idx >= Items.Count then
Idx := Items.Count
else if Idx < 0 then
Idx := 0;
// select it
ItemIndex := Idx;
// and simulate a mouse click on it so the selected table gets displayed
Self.Click;
// tell the caller that the event has been handled
Result := True;
end;
Simple, isn't it?
I found a more involved version on The Spirit of Delphi that overrides the WndProc method and changes the Message to a keyboard message. I like my solution better, it "feels" cleaner.
The other solution also creates a control rather than using an interposer class. That's cleaner than an interposer class but I'm not a great fan of writing my own components to solve shortcomings like this. It's just too much overhead.