Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/VCL/Standard Controls/TPanel    [ Add a report in this area ]  
Report #:  4304   Status: Closed
Controls on a TPanel will not show focus rectangles under XP
Project:  Delphi Build #:  4.453
Version:    7.0 Submitted By:   Jordan Russell
Report Type:  Basic functionality failure Date Reported:  5/4/2003 10:36:53 AM
Severity:    Critical / Show Stopper Last Updated: 3/20/2012 2:24:39 AM
Platform:    All versions Internal Tracking #:   169373
Resolution: Fixed (Resolution Comments) Resolved in Build: : 9.0.1761.24408
Duplicate of:  None
Voting and Rating
Overall Rating: (4 Total Ratings)
4.75 out of 5
Total Votes: None
Description
This is a show-stopper issue for keyboard users:

Under Windows XP with TXPManifest, controls such as TCheckBox placed on a TPanel will not show a focus rectangle when focused, regardless of the "Hide underlines" setting in Display Properties, and regardless of whether the last input came from the keyboard.

TCheckBoxes placed inside a TGroupBox instead do not have this problem. So it seems to be an issue with TPanel.
Steps to Reproduce:
- Start Delphi 7 on Windows XP running the Windows XP theme (not the Classic theme)
- Drop a TXPManifest component
- Drop a TCheckBox component ("CheckBox1")
- Drop a TPanel component below
- Inside the TPanel, drop a TCheckBox component ("CheckBox2")
- Run

- Hold down Tab

- Notice that CheckBox1 gets a focus rectangle when focused, but CheckBox2 never does.
Workarounds
The responsibility for focus rectangle damages lays on an author of WM_PRINTCLIENT handler ;)

When checkbox processes CTLCOLORSTATIC message is requests its parent to draw background (DrawThemeParentBackground API). It calls Panel's WM_PRINTCLIENT, it calls TCustomPanel.Paint. In this handler Brush.Style is set to bsClear, this brush style is set to the DC passed inside (i.e. checkbox DC), and later when CTLCOLORSTATIC returns, focus rectangle is drawn using invalid brush resulting in no focus rectangle drawing :(

There are many possible workarounds in different places:

1) Ensure button.paint procedure to restore brush correctly. But it seems to be incorrect solution, because we should be allowed to do anything in .Paint handler ;) and also we can't guarantee any other 3rd-party panel-like control to do the same.

2) Save brush (and other DC state) before DrawThemeBackground and restore after it. It requires us to put this code in any location calling DrawThemeBackground. This is not elegant but will protect our DC from modification caused by any parent control (i.e. non-Delphi), that will contain ours.

3) Make sure that WM_PRINTCLIENT does not modify a state of the DC passed in.

The 3rd workaround is the following:

We have to add lines marked with (**) to the WMPrintClient handler.

procedure TWinControl.WMPrintClient(var Message: TWMPrintClient);
(**)
var
    SaveIndex: Integer;
(**)
begin
  with Message do
    if Result <> 1 then
      if ((Flags and PRF_CHECKVISIBLE) = 0) or Visible then
    (**)
      begin
        SaveIndex := SaveDC(Message.DC);
    (**)
        PaintHandler(TWMPaint(Message));
    (**)
        RestoreDC(Message.DC,SaveIndex);
      end
    (**)
      else
        inherited
    else
      inherited;
end;

Now the focus rectangle is drawing fine ;) Also do not forget to add ';' after PaintHandler call

---------

The responsibility for focus rectangle damages lays on an author of WM_PRINTCLIENT handler ;)

When checkbox processes CTLCOLORSTATIC message is requests its parent to draw background (DrawThemeParentBackground API). It calls Panel's WM_PRINTCLIENT, it calls TCustomPanel.Paint. In this handler Brush.Style is set to bsClear, this brush style is set to the DC passed inside (i.e. checkbox DC), and later when CTLCOLORSTATIC returns, focus rectangle is drawn using invalid brush resulting in no focus rectangle drawing :(

There are many possible workarounds in different places:

1) Ensure button.paint procedure to restore brush correctly. But it seems to be incorrect solution, because we should be allowed to do anything in .Paint handler ;) and also we can't guarantee any other 3rd-party panel-like control to do the same.

2) Save brush (and other DC state) before DrawThemeBackground and restore after it. It requires us to put this code in any location calling DrawThemeBackground. This is not elegant but will protect our DC from modification caused by any parent control (i.e. non-Delphi), that will contain ours.

3) Make sure that WM_PRINTCLIENT does not modify a state of the DC passed in.

The 3rd workaround is the following:

We have to add lines marked with (**) to the WMPrintClient handler.

procedure TWinControl.WMPrintClient(var Message: TWMPrintClient);
(**)
var
    SaveIndex: Integer;
(**)
begin
  with Message do
    if Result <> 1 then
      if ((Flags and PRF_CHECKVISIBLE) = 0) or Visible then
    (**)
      begin
        SaveIndex := SaveDC(Message.DC);
    (**)
        PaintHandler(TWMPaint(Message));
    (**)
        RestoreDC(Message.DC,SaveIndex);
      end
    (**)
      else
        inherited
    else
      inherited;
end;

Now the focus rectangle is drawing fine ;) Also do not forget to add ';' after PaintHandler call
Attachment
None
Comments

None

Server Response from: ETNACODE01