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.
|