user7421317
user7421317

Reputation:

TreeView custom draw item

I want use a Win32 TreeView control with the feature to resort the items, like it is possible inside of Windows Explorer's Favorites / Quick Access:

image

I didn't find an option to enable the divider between two nodes, so I think I have to draw it by myself.

To do this, I think I have to use the NM_CUSTOMDRAW notification with the CDIS_DROPHILITED item state. But I can't catch this. Why?

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
  Unit2, Vcl.ComCtrls;

type
  TForm1 = class(TForm)
    TreeView1: TTreeView;
    procedure TreeView1AdvancedCustomDrawItem(Sender: TCustomTreeView;
      Node: TTreeNode; State: TCustomDrawState; Stage: TCustomDrawStage;
      var PaintImages, DefaultDraw: Boolean);
    procedure TreeView1Changing(Sender: TObject; Node: TTreeNode;
      var AllowChange: Boolean);
    procedure TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
  private
    procedure WMNotify(var Msg: TMessage); message WM_NOTIFY;
  end;

var
  Form1: TForm1;

implementation

uses
  CommCtrl;

{$R *.dfm}

procedure TForm1.TreeView1AdvancedCustomDrawItem(Sender: TCustomTreeView;
  Node: TTreeNode; State: TCustomDrawState; Stage: TCustomDrawStage;
  var PaintImages, DefaultDraw: Boolean);
begin
  if (cdsDropHilited in State) then
    Write;
end;

procedure TForm1.TreeView1Changing(Sender: TObject; Node: TTreeNode;
  var AllowChange: Boolean);
begin
  AllowChange := Node <> TreeView1.Items[1];
end;

procedure TForm1.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
  Accept := True;

  GetComCtlVersion();
end;

procedure TForm1.WMNotify(var Msg: TMessage);
begin
  case (PNMHdr(Msg.LParam)^.code) of
    NM_CUSTOMDRAW:
      if (PNMTVCustomDraw(Msg.LParam)^.nmcd.uItemState and CDIS_NEARHOT <> 0) then
        Write;
  end;

  inherited;
end;

end.

Neither TreeView1AdvancedCustomDrawItem() nor WMNotify() reach the Write statement.

Any suggestions? How can I draw the node dividers while implementing a drag & drop feature?

I'm using Windows 7.

Upvotes: 1

Views: 1567

Answers (2)

user7421317
user7421317

Reputation:

TreeView_SetInsertMark draws the divider like shown in the question.

Upvotes: 1

Remy Lebeau
Remy Lebeau

Reputation: 596287

If you look closely at how Windows Explorer draws its tree nodes, you will see that the divider you are interested in is only drawn when the mouse is over the top or lower edge of a node that is being dragged onto. Look at the positioning of the divider in relation to the target node's highlight selection when the mouse is not over its top/lower edge. They overlap.

You can accomplish the same thing in the TTreeView.OnAdvancedCustomDraw(Item) events. Check if the TreeView's DropTarget node (the node that will be dropped onto when the user releases the mouse) is the node currently being drawn, and if so then:

  • retrieve the current mouse position in client coordinates using TreeView.ScreenToClient(Mouse.CursorPos).

  • retrieve the DisplayRect of the DropTarget node.

  • check if the mouse coordinates are currently within the top or lower few pixels of that rectangle.

  • if so, draw your divider at those coordinates as needed.

Upvotes: 3

Related Questions