4

Checkbox handling in version 5.0.0 of VirtualTrees.pas appears broken when toThemeAware is enabled. Nodes that are csUncheckedNormal are drawn as checked + hot.

To correctly paint an unchecked, themed checkbox using DrawElement, the Details record must be : Element = teButton, Part = 3, and State = 5. However, VirtualTrees.pas ends up calling DrawElement with State = 1 when a node is set to csUncheckedNormal.

There seems to be a good deal of indirection and extra constants declared in VirtualTrees, so I'm not sure how best to fix this. Ideas welcomed...

(Even the minimal code to get a TVirtualStringTree on screen and filled with some data is a bit lengthy to post here. Aside from the basics, all that's needed to reproduce this is to enable toCheckSupport in TreeOptions.MiscOptions, and set Node.CheckType := ctTriStateCheckBox in the InitNode callback.)

TLama
  • 75,147
  • 17
  • 214
  • 392
DaveS_Lifeway
  • 375
  • 3
  • 9
  • I did the following right now; put the VirtualStringTree v.5.0.0 on a form, keep the toThemeAware in PaintOptions, included the toCheckSupport into the MiscOptions, added the following line of code into a certain event handler `VirtualStringTree1.AddChild(nil).CheckType := ctTriStateCheckBox;`, run the application from my Delphi 2009 on Windows 7 and looked at the check boxes closely; they were not in a hot state. So I went back here to write this comment, ask you if you have VCL styles enabled (before to look into the code), upvoted your question and pressed ENTER key :-) – TLama Apr 18 '12 at 19:38
  • Yes, sorry, this is in an app with styles enabled, under Delphi XE2. – DaveS_Lifeway Apr 18 '12 at 20:04
  • @TLama: if you see it painting correctly, I'd be really interested in what you see at a couple spots in the code. Namely, the value of ImageInfo.Index in TBaseVirtualTree.PaintCheckImage. I have 9, which hits the 2nd case, so State := 9 - 8, which is wrong. That Index of 9 comes from TBaseVirtualTree.GetCheckImage, and the const ckCheckUncheckedNormal, which is a const of 9, on line 122 of VirtualTrees.pas. *something* has to be different if you're seeing it paint correctly. Thanks! – DaveS_Lifeway Apr 19 '12 at 07:10
  • The difference is that I've had only Delphi 2009 available at the time I did the test and they have no VCL styles. One common thing for both versions, the tri-state check box is not tri-state (but this might be some settings). Now I checked that on Delphi XE2 and without VCL styles enabled this works as expected (except tri-state). If you use VCL style, the `Node.CheckState` is `csUncheckedNormal` though but it renders me the checked hot state too, what is surely wrong. I'll take a look on that, but it takes me some time and I have to work on something now. – TLama Apr 19 '12 at 07:43

1 Answers1

6

Well, since I think the VirtualTreeView does not count with the VCL styles when porting to delphi XE2, this might light up to solve your problem. You have to get element details before you draw it, otherwise you'll get something like this (it's the simulation of how the VirtualTreeView paint check box states). Notice the different order and the artifacts; it's the result of the same code once with VCL styles disabled, second time enabled:

enter image description here

Quite strange I know, but I can't answer you why is this happening. I can just tell you that you should call the TThemeServices.GetElementDetails or optionally calculate the state index by your own to get the element rendering to work properly. You may try to use the following fix:

procedure TBaseVirtualTree.PaintCheckImage(Canvas: TCanvas; 
  const ImageInfo: TVTImageInfo; Selected: Boolean);
var
  // add a new variable for calculating TThemedButton state from the input
  // ImageInfo.Index; I hope ImageInfo.Index has the proper state order
  State: Integer;
begin
...
  case Index of
    0..8: // radio buttons
    begin
      // get the low index of the first radio button state and increment it by 
      // the ImageInfo.Index and get details of the button element
      State := Ord(TThemedButton(tbRadioButtonUncheckedNormal)) + Index - 1;
      Details := StyleServices.GetElementDetails(TThemedButton(State));
    end;
    9..20: // check boxes
    begin
      // get the low index of the first check box state and increment it by 
      // the ImageInfo.Index and get details of the button element
      State := Ord(TThemedButton(tbCheckBoxUncheckedNormal)) + Index - 9;
      Details := StyleServices.GetElementDetails(TThemedButton(State));
    end;
    21..24: // buttons
    begin
      // get the low index of the first push button state and increment it by 
      // the ImageInfo.Index and get details of the button element
      State := Ord(TThemedButton(tbPushButtonNormal)) + Index - 21;
      Details := StyleServices.GetElementDetails(TThemedButton(State));
    end;
  else
    Details.Part := 0;
    Details.State := 0;
  end;
...
end;

I've tested this for all check types and it works for me.

TLama
  • 75,147
  • 17
  • 214
  • 392
  • Yes, this does indeed fix the issue. I wonder though, if it's the same approach the treeview authors would take. Specifically, I would think there had to be a reason for the whole CheckStateToCheckImage array thing in GetCheckImage(), though I don't know what it is, and it seems to me to be another level of consts added on top of the already existing consts. In any event, this does work, thanks! – DaveS_Lifeway Apr 19 '12 at 17:20