Delphi, how to close TComboBox when mouse leaves?

2.7k Views Asked by At

I'm trying to implement the following functionality:

  1. when mouse comes over the combobox, it opens automatically.
  2. when mouse leaves the combobox area (not only the combo, but dropdown list too), it closes automatically.

First point was quite easy:

procedure TForm1.ComboTimeUnitsMouseEnter(Sender: TObject);
begin
  ComboTimeUnits.DroppedDown := True;
end;

The second point, though, I cannot do it. I tried:

procedure TForm1.ComboTimeUnitsMouseLeave(Sender: TObject);
begin
  ComboTimeUnits.DroppedDown := False;
end;

But when the mouse is over the combobox, it acts very strange, appearing and disappearing, becoming unusable.

I tried AutoCloseUp property, with no result. Now I'm out of ideas and google couldn't help.

Can someone point me in the right direction?

1

There are 1 best solutions below

0
On

There is no simple solution to your Combo Box (CB) request. I recall that the drop down List of a Windows CB is child to the screen and not the CB. The reason for this is to be able to display the drop down list outside of the client window as illustrated below. Pretty good stuff if you ask me.

enter image description here

Suggested solution

Here's a go at trying to use the existing TComboBox. TLama's "ugly code" is more elegant than mine because he uses an interceptor class. My suggestion below does however solve an additional case, namely the listbox does not roll up when the mouse moves up and crosses the boundry between the ListBox back to the Combobox.

unit main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.AppEvnts;
type

  TFormMain = class(TForm)
    ComboBox1: TComboBox;
    Label1: TLabel;
    Label2: TLabel;
    procedure ComboBox1MouseEnter(Sender: TObject);
    procedure ComboBox1CloseUp(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    FActiveCb : TComboBox;  //Stores a reference to the currently active CB. If nil then no CB is in use
    FActiveCbInfo : TComboBoxInfo; //stores relevant Handles used by the currently active CB

    procedure ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);
  public
    { Public declarations }
  end;

var
  FormMain: TFormMain;

implementation

{$R *.dfm}
procedure TFormMain.FormCreate(Sender: TObject);
begin
  FActiveCb := nil;
  FActiveCbInfo.cbSize := sizeof(TComboBoxInfo);
  Application.OnIdle := Self.ApplicationEvents1Idle;
end;

procedure TFormMain.ComboBox1CloseUp(Sender: TObject);
begin
  FActiveCb := nil;
end;

procedure TFormMain.ComboBox1MouseEnter(Sender: TObject);
begin
  FActiveCb := TComboBox(Sender);
  FActiveCb.DroppedDown := true;
  GetComboBoxInfo(FActiveCb.Handle, FActiveCbInfo); //Get CB's handles
end;

procedure TFormMain.ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);
var w : THandle;
begin
  //Check if the mouse cursor is within the CB, it's Edit Box or it's List Box
  w := WindowFromPoint(Mouse.CursorPos);

  with FActiveCbInfo do
    if Assigned(FActiveCb) and (w <> hwndList) and (w <> hwndCombo) and (w <> hwndItem) then
      FActiveCb.DroppedDown := false;
end;
end.

How to add additional CBs

  1. Drop a new combobox on the form.
  2. Assign ComboBox1MouseEnter proc to the OnMouseEnter event
  3. Assign ComboBox1CloseUp proc to the OnCloseUp event

Issues

There are however certain issues that remain to be solved:

  1. ListBox dissapears when user clicks
  2. Text in the CB cannot be selected using the mouse
  3. For sure more issues...