hakanfa
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.TreeView1CustomDraw Item(Sende r: 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.RecordCou nt)+')';
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(No de.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
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.TreeView1CustomDraw
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.RecordCou
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(No
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
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
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
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
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
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
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',t rue).Handl e, @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.TreeView1CustomDraw Item(Sende r: 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 = {
01040000002B00000000000000 00000000FF FFFFFFFFFF FFFF000000 00000000
0009540065007300740069006E 0067002000 31002B0000 0000000000 00000000
FFFFFFFFFFFFFFFF0000000000 0000000954 0065007300 740069006E 00670020
0032002B000000000000000000 0000FFFFFF FFFFFFFFFF 0000000000 00000009
540065007300740069006E0067 0020003300 2B00000000 0000000000 0000FFFF
FFFFFFFFFFFF00000000000000 0009540065 0073007400 69006E0067 00200034
00}
end
object TreeView2: TTreeView
Left = 176
Top = 8
Width = 145
Height = 345
DragMode = dmAutomatic
Indent = 19
TabOrder = 1
OnCustomDrawItem = TreeView1CustomDrawItem
Items.NodeData = {
01040000002B00000000000000 00000000FF FFFFFFFFFF FFFF000000 00000000
0009540065007300740069006E 0067002000 31002B0000 0000000000 00000000
FFFFFFFFFFFFFFFF0000000000 0000000954 0065007300 740069006E 00670020
0032002B000000000000000000 0000FFFFFF FFFFFFFFFF 0000000000 00000009
540065007300740069006E0067 0020003300 2B00000000 0000000000 0000FFFF
FFFFFFFFFFFF00000000000000 0009540065 0073007400 69006E0067 00200034
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(Se nder: TCustomTreeView; Node: TTreeNode;
State: TCustomDrawState; var DefaultDraw: Boolean);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.TreeView1CustomDraw Item(Sende r: TCustomTreeView;
Node: TTreeNode; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
Sender.Canvas.Font.Style := [fsBold];
DefaultDraw:=True;
end;
procedure TForm1.TreeView1DragOver(S ender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
if Sender.ClassName = Source.ClassName then
Accept:=True;
end;
end.
Any ideas?
-Hokki
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
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.TreeView1CustomDraw
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 = {
01040000002B00000000000000
0009540065007300740069006E
FFFFFFFFFFFFFFFF0000000000
0032002B000000000000000000
540065007300740069006E0067
FFFFFFFFFFFF00000000000000
00}
end
object TreeView2: TTreeView
Left = 176
Top = 8
Width = 145
Height = 345
DragMode = dmAutomatic
Indent = 19
TabOrder = 1
OnCustomDrawItem = TreeView1CustomDrawItem
Items.NodeData = {
01040000002B00000000000000
0009540065007300740069006E
FFFFFFFFFFFFFFFF0000000000
0032002B000000000000000000
540065007300740069006E0067
FFFFFFFFFFFF00000000000000
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(Se
State: TCustomDrawState; var DefaultDraw: Boolean);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.TreeView1CustomDraw
Node: TTreeNode; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
Sender.Canvas.Font.Style := [fsBold];
DefaultDraw:=True;
end;
procedure TForm1.TreeView1DragOver(S
State: TDragState; var Accept: Boolean);
begin
if Sender.ClassName = Source.ClassName then
Accept:=True;
end;
end.
Any ideas?
-Hokki
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
treeview1.repaint;
at the end