透過 SetWindowLong 加入 TVS_CHECKBOXES 樣式,TreeView 即顯示核取方塊。再封裝取得/設定節點勾選狀態函數,於滑鼠事件中實現點選父節點連帶勾選所有子孫節點。
void __fastcall TForm1::FormCreate(TObject *Sender)
{
SetWindowLong(TreeView1->Handle, GWL_STYLE,
GetWindowLong(TreeView1->Handle, GWL_STYLE) | TVS_CHECKBOXES);
}
// 取得節點勾選狀態
bool GetNodeChecked(TTreeNode *ANode)
{
TVITEM tvItem;
tvItem.mask = TVIF_HANDLE | TVIF_STATE;
tvItem.hItem = ANode->ItemId;
tvItem.stateMask = TVIS_STATEIMAGEMASK;
TreeView_GetItem(ANode->TreeView->Handle, &tvItem);
return ((BOOL)(tvItem.state >> 12) - 1); // 狀態映像索引 1=未勾選, 2=勾選
}
// 設定節點勾選狀態
void SetNodeChecked(TTreeNode *ANode, bool IsSetToChecked)
{
TVITEM tvItem;
tvItem.mask = TVIF_HANDLE | TVIF_STATE;
tvItem.hItem = ANode->ItemId;
tvItem.stateMask = TVIS_STATEIMAGEMASK;
tvItem.state = INDEXTOSTATEIMAGEMASK(IsSetToChecked ? 2 : 1);
TreeView_SetItem(ANode->TreeView->Handle, &tvItem);
}
// 遞歸設定子孫節點
void SetChildChecked(TTreeNode *ANode, bool yn)
{
SetNodeChecked(ANode, yn);
for (int i = 0; i < ANode->Count; i++)
SetChildChecked(ANode->Item[i], yn);
}
TTreeNode *ClickNode; // 滑鼠移動時暫存節點
bool NodeChecked; // 暫存該節點原本勾選狀態
void __fastcall TForm1::TreeView1MouseMove(TObject *Sender,
TShiftState Shift, int X, int Y)
{
ClickNode = TreeView1->GetNodeAt(X, Y);
if (ClickNode != NULL)
NodeChecked = GetNodeChecked(ClickNode); // 記錄點擊前狀態
}
void __fastcall TForm1::TreeView1MouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
if (ClickNode != NULL) {
// 若狀態改變 (使用者點擊核取方塊),同步子孫節點
if (GetNodeChecked(ClickNode) != NodeChecked) {
NodeChecked = !NodeChecked;
SetChildChecked(ClickNode, NodeChecked);
}
}
}
TVIS_STATEIMAGEMASK 狀態位元第 12-15 位存放狀態映像索引 (1-based)。INDEXTOSTATEIMAGEMASK(1) = 未勾選,(2) = 勾選。MouseMove 記錄點擊前狀態,MouseDown 比較點擊後狀態,若不同表示使用者點擊了核取方塊,遞歸設定所有子孫節點相同狀態。