Changing
the text color and text background color of a TreeView Nodes (also changing
individual text styles)
The TTreeView control by default will always
display the text background color as clWindow. This is annoying since
it is real easy to change the background color of the TreeView by changing
the Color property. Fortunately, with a little bit of work, we can
change the text background color, the text color, and the text styles of
the TreeNodes. Below I describe two methods. The first, simpler,
method, doesn't require subclassing a TreeView or trapping any messages.
The second, more robust method requires deriving a new TreeView from TTreeView
and trapping the WM_PAINT message.
KEYWORDS: WM_PAINT, TControlCanvas
METHOD 1: The goal is to create a new TControlCanvas
and set it's Control property to the TreeView. This will give
you a Canvas to draw on. Then every time the TreeView is painted,
you have to update the TreeView by painting on this
Canvas. To do this, you'll have to trap the WM_PAINT message
sent to the Form.
|
//in
header...
TControlCanvas *TVCanvas;
void
__fastcall PaintHandler(TMessage &Msg);
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_PAINT,
TMessage, PaintHandler)
END_MESSAGE_MAP(TForm)
|
//---------------------------------------------------------------------------
//in
source...
__fastcall TForm1::TForm1(TComponent*
Owner)
: TForm(Owner)
{
TVCanvas
= new TControlCanvas();
TVCanvas->Control
= TreeView1;
TVCanvas->Font
= TreeView1->Font;
//Draw
text transparently!
TVCanvas->Brush->Style
= bsClear;
}
void
__fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
delete
TVCanvas;
}
//Implement
painting the TreeView text background
void __fastcall
TForm1::PaintHandler(TMessage &Msg)
{
//let
everything else draw first
TForm::Dispatch(&Msg);
//manage
drawing the TreeView
for
(int index = 0; index < TreeView1->Items->Count; index++)
{
if (index != TreeView1->Selected->Index)
{
TRect NodeRect = TreeView1->Items->Item[index]->DisplayRect(true);
//FillRect the text background
//You can even fill in a different color for each node
TVCanvas->Brush->Color = TreeView1->Color;
TVCanvas->FillRect(NodeRect);
//Change the font color of individual items
switch(index)
{
case 0: TVCanvas->Font->Color = clRed;
break;
case 1: TVCanvas->Font->Color = clGreen;
break;
case 2: TVCanvas->Font->Color = clBlue;
break;
default: TVCanvas->Font->Color = clBlack;
}
TVCanvas->TextOut(NodeRect.Left + 2, NodeRect.Top + 1,
TreeView1->Items->Item[index]->Text);
}
}
}
void __fastcall
TForm1::TreeView1Change(TObject *Sender, TTreeNode *Node)
{
Refresh();
}
void __fastcall
TForm1::FormPaint(TObject *Sender)
{
TreeView1->Refresh();
}
|
METHOD 2: Ok, that's one
way to do it. The other, more robust way is to derive your own TreeView
from TTreeView, and trap the WM_PAINT message in your derived class...
|
//in
derived MyTreeView header...
private:
TControlCanvas
*TVCanvas;
public:
__fastcall
TMyTreeView(TComponent* Owner);
__fastcall
TMyTreeView::~TMyTreeView();
virtual
void __fastcall PaintIt(TMessage &Message);
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_PAINT,
TMessage, PaintIt);
END_MESSAGE_MAP(TTreeView)
|
//---------------------------------------------------------------------------
//in
derived MyTreeView source...
__fastcall TMyTreeView::TMyTreeView(TComponent*
Owner)
: TTreeView(Owner)
{
TVCanvas
= new TControlCanvas();
TVCanvas->Control
= this;
TVCanvas->Brush->Style
= bsClear;
}
__fastcall TMyTreeView::~TMyTreeView()
{
delete
TVCanvas;
}
//Manage
drawing the TreeView background and text
void __fastcall
TMyTreeView::PaintIt(TMessage &Msg)
{
TTreeView::Dispatch(&Msg);
for
(int index = 0; index < Items->Count; index++)
{
if (index != Selected->Index)
{
TRect NodeRect = Items->Item[index]->DisplayRect(true);
//FillRect the text background
//You can even fill in a different color for each node
TVCanvas->Brush->Color = Color;
TVCanvas->FillRect(NodeRect);
//Change the font color of individual items
switch(index)
{
case 0: TVCanvas->Font->Color = clRed;
break;
case 1: TVCanvas->Font->Color = clGreen;
break;
case 2: TVCanvas->Font->Color = clBlue;
break;
default: TVCanvas->Font->Color = clBlack;
}
TVCanvas->TextOut(NodeRect.Left + 2, NodeRect.Top + 1, Items->Item[index]->Text);
}
}
} |
There are some things to note, specifically in the line...
TRect NodeRect = Items->Item[index]->DisplayRect(true);
If you set the parameter to the DisplayRect method to false, then NodeRect
will be the entire line of the TreeNode,
including the buttons and images (if present). This is in fact
one way to draw your own TreeView buttons, and indeed, adds
a greater degree of control. The problem is that it will be difficult
to tell how it is connected. It can be done by testing it's
previous node and next node, but it is somewhat of a hassle.
Also, if you do have an imagelist set to MyTreeView, you'll
have to offset the text appropriately (if DisplayRect(false)
is specified). Either way, it can be done without
CustomDraw, and the second method allows you nearly complete control
over the look of your TreeView.
|