Search code examples
formsdelphigridcursorcontrols

setted cursor on form wont show up on controls


I use Delphi xe-8 and I use thread for queries and I want to show an hourglass cursor while my thread is working.

I'm setting my cursor to hourglass but when I hover on controls on the form, for example over a grid, the cursor appears as the default; the cursor only changes when I hover over the form itself. What I want to do is to show an hourglass cursor over just the form and any controls on the form, but not the rest of the application.

I tried to set Screen.Cursor to crHourGlass but then it is for the whole application, so it is an hourglass even if I'm hovering on another form.

Is it possible to do that? If it is how can i do that?


Solution

  • When the mouse is over a given TWinControl, the OS sends it a WM_SETCURSOR message requesting it to set the onscreen cursor icon. The VCL's default processing of that message goes like this:

    1. if the Screen.Cursor is not crDefault, that is the cursor used.

    2. otherwise, if the TWinControl has a TGraphicControl child underneath the mouse, and its Cursor is not crDefault, that is the cursor used.

    3. otherwise, if the TWinControl's own Cursor is not crDefault, that is the cursor used.

    4. otherwise, the message is passed to the parent window. Repeat steps 2-3.

    So, to do what you are asking for, try setting the Form's Cursor to the desired value, and make sure all child controls on the Form have their Cursor set to crDefault.

    If that is not an option for you, or if it doesn't work, you can instead have your Form class override its virtual WndProc() method (or use a message method) to handle the WM_SETCURSOR message directly. Call the inherited handler first, and if the message's Result is FALSE and the worker thread is running then call the Win32 API SetCursor() function to set your desired cursor icon, and set the message's Result to TRUE, eg:

    protected
      procedure WndProc(var Message: TMessage); override;
    
    ...
    
    procedure TMyForm.WndProc(var Message: TMessage);
    begin
      inherited;
      if (Message.Msg = WM_SETCURSOR) and (Message.Result = 0) and (Thread is Running) then
      begin
        Windows.SetCursor(Screen.Cursors[crHourGlass]);
        Message.Result := 1;
      end;
    end;
    

    Or

    private
      procedure WMSetCursor(var Message: TMessage); message WM_SETCURSOR;
    
    ...
    
    procedure TMyForm.WMSetCursor(var Message: TMessage);
    begin
      inherited;
      if (Message.Result = 0) and (Thread is Running) then
      begin
        Windows.SetCursor(Screen.Cursors[crHourGlass]);
        Message.Result := 1;
      end;
    end;
    

    This way, Screen.Cursor and individual TControl.Cursor properties still take priority, but if the cursor remains unchanged (ie, all of the properties are crDefault) then you can change the cursor for the entire Form as a whole, without affecting other Forms.