delphicanvaslistboxownerdrawn

How to remove the dotted focus rectangle from TListBox?


I am using ListBox.Style := lbOwnerDrawFixed with OnDrawItem:

procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
  ARect: TRect; State: TOwnerDrawState);
begin
  with ListBox1.Canvas do begin
    Brush.Color := clWhite;
    Pen.Color := clWhite;
    FillRect(ARect);
  end;
end;

It should give an empty space to draw anything I want, but it doesn't. When the item is focued I still can see a dotted rectangle around it.

How to remove the rectangle?


Solution

  • The focus rectangle is drawn by the VCL (*) in TCustomListBox.CNDrawItem after the OnDrawItem event handler returns.

    procedure TCustomListBox.CNDrawItem(var Message: TWMDrawItem); 
    
    ...
    
      ..
        if Integer(itemID) >= 0 then
          DrawItem(itemID, rcItem, State)      //-> calls message handler
        else
          FCanvas.FillRect(rcItem);
        if (odFocused in State) and not TStyleManager.IsCustomStyleActive then
          DrawFocusRect(hDC, rcItem);          //-> draws focus rectangle
      ..
    ..
    

    Whatever you draw in the event handler will later get focused by the VCL.


    But notice that VCL use DrawFocusRect to draw the focus rectangle:

    Because DrawFocusRect is an XOR function, calling it a second time with the same rectangle removes the rectangle from the screen.

    So just call DrawFocusRect yourself and let VCL's call erase it:

    procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
      ARect: TRect; State: TOwnerDrawState);
    begin
      with ListBox1.Canvas do begin
        Brush.Color := clWhite;
        Pen.Color := clWhite;
        FillRect(ARect);
        if odFocused in State then  // also check for styles if there's a possibility of using ..
          DrawFocusRect(ARect);
      end;
    end;
    


    (*) Normally default window procedure draws the focus rectangle for an owner drawn list box item in response to a WM_DRAWITEM message. But the default window procedure is not called for a list box in VCL's handling of the message.