2

I need to display files in a directory to a TVirtualStringTree. So, I use SHGetFileInfo to get files' icons. But seems I can only get "normal" icons (Left side on following screen shot). If so, can TVirtualStringTree draw icons as "disabled"? Just like you disabled the a node. Please see a screen shot:

enter image description here

UPDATED

There is a similar thread in Soft Gems forum. I can get the rect of the icon then draw the icon myself. I'm using TcxImageList and it can draw the "disabled" icon easily. I firstly assigned a non-exist image index in GetImageIndex event so I have a room to draw the icon. Then using following code to draw.

procedure TfrmMain.tvSharesAfterItemPaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode; ItemRect: TRect); 
var 
  rImage: TRect; 
  OffsetLeft: Integer; 
begin 
    rImage  := ItemRect; 

    with TVirtualStringTree(Sender) do begin 
      if (toShowRoot in TreeOptions.PaintOptions) then 
        OffsetLeft := Indent * (GetNodeLevel(Node) + 1) 
      else 
        OffsetLeft := Indent * GetNodeLevel(Node); 

      Inc(rImage.Left, Margin + OffsetLeft); 
      Inc(rImage.Top, (NodeHeight[Node] - Images.Height) div 2); 
      rImage.Right  := rImage.Left + Images.Width; 
      rImage.Bottom := rImage.Top + Images.Height; 
    end; 

    // draw the "normal" or "disabled" icon here
    imageList.Draw(TargetCanvas, rImage.left, rImage.Top, ...);
  end; 
end;
TLama
  • 75,147
  • 17
  • 214
  • 392
trudger
  • 917
  • 2
  • 12
  • 20
  • Could these nodes be disabled or you need only to look them as disabled ? Anyway, what version of Delphi are you using ? Could you add it to the tags to your question ? To disable a particular node in tree is enough to use `VirtualTree.IsDisabled[Node] := True`. – TLama Aug 30 '12 at 03:30
  • I don't want to disable the node, I only want the "grey" icon. – trudger Aug 30 '12 at 05:57
  • I've suggested to [`make a new event for custom image drawing`](http://code.google.com/p/virtual-treeview/issues/detail?id=282), so don't forget to star it if you like that. – TLama Aug 31 '12 at 10:13

2 Answers2

5

There's no direct way to draw disabled image state. I would prefer to create the event for custom drawing of the images (now I've suggested this as a new feature for virtual tree view, due to this lack). Here is the example with interposed class for virtual string tree.

For custom drawing of grayscale images, it uses the code from this post. Surely, it's not a solution for permanent use, you would have to keep the PaintImage method code synchronized with the actual code since the method is completely overriden.

Parameter count of the OnCustomDrawImage event points to that parameters deserve to wrap to some structure, but it's just a showcase how would it look like. This new event is fired twice; first time for image draw (the DrawOverlay is False) and second time for overlay (the DrawOverlay parameter is True):

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, DateUtils, StdCtrls, VirtualTrees, ImgList, CommCtrl;

type
  TCustomDrawImageEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode;
    ImageList: TCustomImageList; ImageIndex: Integer; TargetCanvas: TCanvas;
    X, Y: Integer; Style: Cardinal; DrawEnabled: Boolean; DrawOverlay: Boolean;
    var CustomDraw: Boolean) of object;
  TVirtualStringTree = class(VirtualTrees.TVirtualStringTree)
  private
    FOnCustomDrawImage: TCustomDrawImageEvent;
  protected
    function DoCustomDrawImage(Node: PVirtualNode; ImageList: TCustomImageList;
      ImageIndex: Integer; TargetCanvas: TCanvas; X, Y: Integer; Style: Cardinal;
      DrawEnabled: Boolean; DrawOverlay: Boolean): Boolean; virtual;
    procedure PaintImage(var PaintInfo: TVTPaintInfo; ImageInfoIndex: TVTImageInfoIndex;
      DoOverlay: Boolean); override;
  published
    property OnCustomDrawImage: TCustomDrawImageEvent read FOnCustomDrawImage write FOnCustomDrawImage;
  end;

type
  TForm1 = class(TForm)
    VirtualStringTree1: TVirtualStringTree;
    ImageList1: TImageList;
    procedure FormCreate(Sender: TObject);
    procedure VirtualStringTree1GetImageIndex(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
      var Ghosted: Boolean; var ImageIndex: Integer);
  private
    procedure VirtualTreeCustomDrawImage(Sender: TBaseVirtualTree; Node: PVirtualNode;
      ImageList: TCustomImageList; ImageIndex: Integer; TargetCanvas: TCanvas;
      X, Y: Integer; Style: Cardinal; DrawEnabled: Boolean; DrawOverlay: Boolean;
      var CustomDraw: Boolean);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TVirtualStringTree }

type
  TCustomImageListCast = class(TCustomImageList);

function TVirtualStringTree.DoCustomDrawImage(Node: PVirtualNode;
  ImageList: TCustomImageList; ImageIndex: Integer; TargetCanvas: TCanvas; X,
  Y: Integer; Style: Cardinal; DrawEnabled: Boolean; DrawOverlay: Boolean): Boolean;
begin
  Result := False;
  if Assigned(FOnCustomDrawImage) then
    FOnCustomDrawImage(Self, Node, ImageList, ImageIndex, TargetCanvas, X, Y,
      Style, DrawEnabled, DrawOverlay, Result);
end;

procedure TVirtualStringTree.PaintImage(var PaintInfo: TVTPaintInfo;
  ImageInfoIndex: TVTImageInfoIndex; DoOverlay: Boolean);
var
  CutNode: Boolean;
  ExtraStyle: Cardinal;
  DrawEnabled: Boolean;
  PaintFocused: Boolean;
const
  Style: array[TImageType] of Cardinal = (0, ILD_MASK);
begin
  with PaintInfo do
  begin
    CutNode := (vsCutOrCopy in Node.States) and (tsCutPending in TreeStates);
    PaintFocused := Focused or (toGhostedIfUnfocused in TreeOptions.PaintOptions);
    if DoOverlay then
      GetImageIndex(PaintInfo, ikOverlay, iiOverlay, Images)
    else
      PaintInfo.ImageInfo[iiOverlay].Index := -1;
    DrawEnabled := not (vsDisabled in Node.States) and Enabled;
    with ImageInfo[ImageInfoIndex] do
    begin
      if (vsSelected in Node.States) and not (Ghosted or CutNode) then
      begin
        if PaintFocused or (toPopupMode in TreeOptions.PaintOptions) then
          Images.BlendColor := Colors.FocusedSelectionColor
        else
          Images.BlendColor := Colors.UnfocusedSelectionColor;
      end
      else
        Images.BlendColor := Color;
      if (ImageInfo[iiOverlay].Index > -1) and (ImageInfo[iiOverlay].Index < 15) then
        ExtraStyle := ILD_TRANSPARENT or ILD_OVERLAYMASK and
          IndexToOverlayMask(ImageInfo[iiOverlay].Index + 1)
      else
        ExtraStyle := ILD_TRANSPARENT;
      if (toUseBlendedImages in TreeOptions.PaintOptions) and PaintFocused
        and (Ghosted or ((vsSelected in Node.States) and
        not (toFullRowSelect in TreeOptions.SelectionOptions) and
        not (toGridExtensions in TreeOptions.MiscOptions)) or CutNode)
      then
        ExtraStyle := ExtraStyle or ILD_BLEND50;
      if (vsSelected in Node.States) and not Ghosted then
        Images.BlendColor := clDefault;

      // in this modified part of code, the new event OnCustomDrawImage
      // is fired once before the image is actually drawn and once when
      // the overlay is to be drawn; when you keep its CustomDraw param
      // in False value (what is, by default), the default drawing will
      // be done otherwise you need to take care of drawing by yourself

      // draw image default way when the CustomDraw parameter of the new
      // OnCustomDrawImage event remains False (what is, by default)
      if not DoCustomDrawImage(Node, Images, Index, Canvas, XPos, YPos,
        Style[Images.ImageType] or ExtraStyle, DrawEnabled, False)
      then
        TCustomImageListCast(Images).DoDraw(Index, Canvas, XPos, YPos,
          Style[Images.ImageType] or ExtraStyle, DrawEnabled);
      // draw overlay default way when the CustomDraw parameter of the new
      // OnCustomDrawImage event remains False (what is, by default)
      if PaintInfo.ImageInfo[iiOverlay].Index >= 15 then
      begin
        if not DoCustomDrawImage(Node, ImageInfo[iiOverlay].Images,
          ImageInfo[iiOverlay].Index, Canvas, XPos, YPos,
          Style[ImageInfo[iiOverlay].Images.ImageType] or ExtraStyle,
          DrawEnabled, True)
        then
          TCustomImageListCast(ImageInfo[iiOverlay].Images).DoDraw(
            ImageInfo[iiOverlay].Index, Canvas, XPos, YPos,
            Style[ImageInfo[iiOverlay].Images.ImageType] or ExtraStyle,
            DrawEnabled);
      end;
    end;
  end;
end;

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  VirtualStringTree1.OnCustomDrawImage := VirtualTreeCustomDrawImage;
end;

type
  TImageListDrawParams = record
    cbSize: DWORD;
    himl: HIMAGELIST;
    i: Integer;
    hdcDst: HDC;
    x: Integer;
    y: Integer;
    cx: Integer;
    cy: Integer;
    xBitmap: Integer;
    yBitmap: Integer;
    rgbBk: COLORREF;
    rgbFg: COLORREF;
    fStyle: UINT;
    dwRop: DWORD;
    fState: DWORD;
    Frame: DWORD;
    crEffect: COLORREF;
  end;

procedure DrawDisabledImage(DC: HDC; ImageList: TCustomImageList; Index, X,
  Y: Integer);
var
  Options: TImageListDrawParams;
begin
  FillChar(Options, SizeOf(Options), 0);
  Options.cbSize := SizeOf(Options);
  Options.himl := ImageList.Handle;
  Options.i := Index;
  Options.hdcDst := DC;
  Options.x := X;
  Options.y := Y;
  Options.fState := ILS_SATURATE;
  ImageList_DrawIndirect(@Options);
end;

procedure TForm1.VirtualStringTree1GetImageIndex(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
  var Ghosted: Boolean; var ImageIndex: Integer);
begin
  ImageIndex := 0;
end;

procedure TForm1.VirtualTreeCustomDrawImage(Sender: TBaseVirtualTree;
  Node: PVirtualNode; ImageList: TCustomImageList; ImageIndex: Integer;
  TargetCanvas: TCanvas; X, Y: Integer; Style: Cardinal; DrawEnabled: Boolean;
  DrawOverlay: Boolean; var CustomDraw: Boolean);
begin
  CustomDraw := True;
  if not DrawOverlay then
    DrawDisabledImage(TargetCanvas.Handle, ImageList, ImageIndex, X, Y);
end;

end.

And the result (I have to say it would be fine to blend it yet):

enter image description here

Community
  • 1
  • 1
TLama
  • 75,147
  • 17
  • 214
  • 392
3

No, TVirtualStringTree does not have an explicit DisabledIcon property or any thing similiar.

However, you can achieve your desired result by an appropriate handler for the GetImageIndex event. In the event handler for this event, determine if the node is disabled or not, and use this test as a discriminator for computing the image index.

In your imagelist, you will need to have normal versions of glyphs and disabled versions. VirtualTree will not magically create the disabled versions for you, however it is a trivial matter to clone a glyph and grey wash it.

Let me know if you need demo code, either for the GetImageIndex event handler, or for grey washing.

Sean B. Durkin
  • 12,659
  • 1
  • 36
  • 65
  • I get these icons by using SHGetFileInfo, I'm not sure how can I get the grey version. I'm appreciated if you can show me the demo code. – trudger Aug 30 '12 at 05:59
  • For disabled icons, Windows has APIs for that: http://stackoverflow.com/questions/6003018/make-disabled-menu-and-toolbar-images-look-better#answer-6003256 – David Heffernan Aug 30 '12 at 07:20
  • @David, have you tried to play with the [`IMAGELISTSTATEFLAGS`](http://msdn.microsoft.com/en-us/library/windows/desktop/bb775231(v=vs.85).aspx) to get grayscaled alpha blended image ? I've tried to use the `ILS_ALPHA` but it seems it takes precedence before `ILS_SATURATE`, so the result is alpha blended but not grayscaled. The images rendered by the method you've showed there looks to me quite dark. – TLama Aug 30 '12 at 13:06
  • @TLama I don't know what you mean. That code, using just `ILS_SATURATE` will produce grey scale for a 32bpp image with alpha channel. I don't understand why you'd want to add `ILS_ALPHA`. – David Heffernan Aug 30 '12 at 13:10
  • @David, I was hoping for alpha blended grayscaled rendering - the combination of `ILS_SATURATE or ILS_ALPHA`, but it seems those flags cannot be combined. – TLama Aug 30 '12 at 15:31