Link to home
Start Free TrialLog in
Avatar of hakanfa
hakanfaFlag for Finland

asked on

TTreeView, OnCustomDrawItem how to proceed....

Hello everyone,
I'm trying to develop an application that handles diffrent records in a database(n-tire). The database have a field named "folder" which indicates the named place for the record. The application have a TreeView placed on the left side of the form, and the nodes are named after the "folders" in the database. (like March 2006, April 2006). All records can have different "status" (like open/closed) and I would like to display the count of records having the status "open" for each folder in the treview, with bolded text and the count of open records in blue. (jus like MS Outlook). I have a timer on the application checking against the server for new records continously, so this feature can not be a part of the actual treeviews events (like OnCustomDrawItem, I've tried that one but couldn't manage to forece the repainting of each node without messing with the treeview). Below You can find the code that I've tried so far, but as I mentioned before, it works as long as You play with the TreeView not the background functionality Im looking for.

procedure TForm1.TreeView1CustomDrawItem(Sender: TCustomTreeView;
  Node: TTreeNode; State: TCustomDrawState; var DefaultDraw: Boolean);
var
  bmp: TBitMap;
  s: string;
  pt: TPoint;
  r: TRect;
  DC: HWND;
begin
  if Node.Text = 'Inbox' then
  begin
    s := '('+IntToStr(CDS.RecordCount)+')';
    Sender.Canvas.Font.Style := [fsBold];
    r := Node.DisplayRect(true);
    bmp := TBitmap.Create;
    bmp.Width := Canvas.TextWidth(s);
    bmp.Height := Canvas.TextHeight(S);
    bmp.Canvas.Font.Color := clBlue;
    bmp.Canvas.Brush.Style := bsClear;
    bmp.Canvas.TextOut(0, 0, s);
    Pt.x := r.Left + sender.Canvas.TextWidth(Node.Text)+8;
    Pt.y := r.Top + 2;
    DC := GetDC(sender.Handle);
    BitBlt(DC, pt.x, pt.y, BMP.Width, BMP.Height, BMP.Canvas.Handle, 0, 0, SRCCOPY);
    bmp.Free;
    ReleaseDC(sender.Handle, DC);
    DefaultDraw := True;
  end;
end;

The solution would be to manage the forced repainting and/or develop a totally new feature I think. Any suggestions?

Kind regards,
-Hokki
Avatar of TheRealLoki
TheRealLoki
Flag of New Zealand image

in the timer that changes the counts etc, you could call
treeview1.repaint;
at the end
ah wait, i remember the problems now
because of the treeview canvas problem (where it creates a new "unrelated" font if you access the canvas, thus corrupting subsequent nodes displayed in the tree)
You will have to draw everything manually (link lines, selected region box etc)
if you wish to use different font properties (in your case fsBold also) for different nodes.
It is even worse if you wish to use different fonts on the same node (which you are doing)
What you are doing with the TBitmap is ok, but you may have to extend it to draw the node.text also, and the aforementioned link-lines, etc)
http://www.delphipages.com/news/detaildocs.cfm?ID=159
shows how to do the display, similar to what you do)
but I do not have an example of doing the link lines.
The easy "trick" to do this, is to draw the dotted lines in a 16x16 image, and load them into a TImageList.
You can then just use the "StateImage" property to display the appropriate one.
(Treeview1.StateImages := imagelist1; Node.StateIndex := 2;)
This is also handy if you want to have different coloured link lines, or something else

Another solution I have used, when TTreeview does not allow me to do formatting how I like, is the VirtualTreeview
http://www.delphi-gems.com/VirtualTreeview/VT.php

or TreeNT may be able to do this correctly, but i haven't tried it, and it is discontinued (same author i think, so probably replaced by virtual treeview)
http://www.delphi-gems.com/TreeNT.php

Avatar of hakanfa

ASKER

Hello, and thank you for a quick reply,
Actually the code that i mentioned works fine, the thing is that i would like to do the painting with an external procedure not an event from the
TreeView. The TreeView.Repaint or TreeView.Refresh doesn't trigger the OnDrawItem for some reasons, so the only way for the moment
is a)calling LockWindowsUpdate or b) reload the TreeView.(neither of these are acceptable). I actually think that I have the solution, but have to try it a few more times before I can be sure. I'll revert with details later.All idea's/thoughts are welcome.

-Hokki
 
hmm, the repaint works here
I set up  :-
s := '('+IntToStr(somecount)+')';
in the OnCustomDrawItem event
...
and in the ontimer event, I have
somecount := somecount + 1;
treeview1.repaint;
occurring every second

and I am getting the "Inbox" node repainting fine, I am still able to open/close branches etc while this is working
Avatar of hakanfa

ASKER

Hello again.. still working on the project and found a nice solution for my problem. The following code works fine, though another problem occurred.
This works fine:

function GetNodeByText
(ATree : TTreeView; AValue:String;
 AVisible: Boolean): TTreeNode;
var
    Node: TTreeNode;
begin
  Result := nil;
  if ATree.Items.Count = 0 then Exit;
  Node := ATree.Items[0];
  while Node <> nil do
  begin
    if Node.Text = AValue then
    begin
      Result := Node;
      if AVisible then
        Result.MakeVisible;
      Break;
    end;
    Node := Node.GetNext;
  end;
end;


then...
In the treeviews OnCustomDrawItem the code I mentioned above..

then..
For example Button click..
RedrawWindow(GetNodeByText(TreeView1,'Inbox',true).Handle, @R, 0, RDW_FRAME or RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOCHILDREN);

Put the above code in some event/procedure when You need to update the specific node.. Then You do not need the Timer...

The problem that occurred is: When You drag items to the TreeView (like when you move items in Outlook) the painting of the specific node doesn’t work (the focus drawing when You drag items over a specific node). I've tried to make a simple demo just painting each node with bold text and this problem occurs even then.

procedure TForm1.TreeView1CustomDrawItem(Sender: TCustomTreeView;
  Node: TTreeNode; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
  Sender.Canvas.Font.Style := [fsBold];
  DefaultDraw:=True;
end;

Here’s the dfm

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 361
  ClientWidth = 341
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object TreeView1: TTreeView
    Left = 8
    Top = 8
    Width = 145
    Height = 345
    DragMode = dmAutomatic
    Indent = 19
    TabOrder = 0
    OnCustomDrawItem = TreeView1CustomDrawItem
    OnDragOver = TreeView1DragOver
    Items.NodeData = {
      01040000002B0000000000000000000000FFFFFFFFFFFFFFFF00000000000000
      0009540065007300740069006E006700200031002B0000000000000000000000
      FFFFFFFFFFFFFFFF000000000000000009540065007300740069006E00670020
      0032002B0000000000000000000000FFFFFFFFFFFFFFFF000000000000000009
      540065007300740069006E006700200033002B0000000000000000000000FFFF
      FFFFFFFFFFFF000000000000000009540065007300740069006E006700200034
      00}
  end
  object TreeView2: TTreeView
    Left = 176
    Top = 8
    Width = 145
    Height = 345
    DragMode = dmAutomatic
    Indent = 19
    TabOrder = 1
    OnCustomDrawItem = TreeView1CustomDrawItem
    Items.NodeData = {
      01040000002B0000000000000000000000FFFFFFFFFFFFFFFF00000000000000
      0009540065007300740069006E006700200031002B0000000000000000000000
      FFFFFFFFFFFFFFFF000000000000000009540065007300740069006E00670020
      0032002B0000000000000000000000FFFFFFFFFFFFFFFF000000000000000009
      540065007300740069006E006700200033002B0000000000000000000000FFFF
      FFFFFFFFFFFF000000000000000009540065007300740069006E006700200034
      00}
  end
end

and the Unit

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls;

type
  TForm1 = class(TForm)
    TreeView1: TTreeView;
    TreeView2: TTreeView;
    procedure TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
    procedure TreeView1CustomDrawItem(Sender: TCustomTreeView; Node: TTreeNode;
      State: TCustomDrawState; var DefaultDraw: Boolean);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.TreeView1CustomDrawItem(Sender: TCustomTreeView;
  Node: TTreeNode; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
  Sender.Canvas.Font.Style := [fsBold];
  DefaultDraw:=True;
end;


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

Any ideas?

-Hokki

ASKER CERTIFIED SOLUTION
Avatar of Computer101
Computer101
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial