unit XTreeView;{ ============== TXTreeView 1.0 (1999-07-11) ============== Enhaced TTreeView with 2- or 3-state checkboxes. Freeware. Copyright ?Roman Stedronsky 1999, [email protected] All rights reserved. You may use this software in an application without fee or royalty, provided this copyright notice remains intact. types ----- TCheckState defines 4 states for every node (No check, Unchecked, Checked, Grayed) public properties ----------------- CheckStates[Index: integer] set/get the state for given node (by index) published properties -------------------- CheckBoxes when true, shows checkboxes ThreeState when true, use 3-state cycle (un-checked-grayed) when false, use 2-state cycle (unchecked-checked) CheckBitmap defines visual appearance of checkboxes (Width: 64 /4x16/, height: 16. See default one.) events ------ OnStateClick occures after changing state via mouse (Not when changing CheckStates!) Note: Every new node is in the state csNone by default (checkbox is not visible). You must explicitly change it by CheckStates property. (You can also use node's StateIndex as shown below, but why?) StateIndex CheckState -1 csNone 1 csUnchecked 2 csChecked 3 csGrayed }interfaceuses Windows, Messages, Classes, Graphics, Controls, ComCtrls, Commctrl;type TCheckState = (csNone, csUnchecked, csChecked, csGrayed); TStateClickEvent = procedure(CheckState: TCheckState) of object; TXTreeView = class(TCustomTreeView) protected { internal variables } FBitmap: TBitmap; CheckStateImages: TImageList; { property variables } FCheckBoxes: boolean; FThreeState: boolean; FStateClickEvent: TStateClickEvent; { property manipulation methods } procedure FWriteCheckBoxes(Value: boolean); function FReadCheckState(Index: integer): TCheckState; procedure FWriteCheckState(Index: integer; Value: TCheckState); procedure FWriteCheckBitmap(Value: TBitmap); { internal methods } procedure ChangeCheckState(Node: TTreeNode); procedure SetChildCS(Node: TTreeNode); procedure SetParentCS(Node: TTreeNode); function GetAllChildCS(Node:TTreeNode):integer; procedure BitmapChanged(Sender: TObject); public { overrided methods } constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure CNNotify(var Message: TWMNotify); message CN_NOTIFY; property CheckStates[Index: integer]: TCheckState read FReadCheckState write FWriteCheckState; published property CheckBoxes: boolean read FCheckBoxes write FWriteCheckBoxes default true; property ThreeState: boolean read FThreeState write FThreeState default false; property CheckBitmap: TBitmap read FBitmap write FWriteCheckBitmap stored true default nil; property OnStateClick: TStateClickEvent read FStateClickEvent write FStateClickEvent; published { make TCustomTreeView propeties published (exclude StateImages) } property Align; property Anchors; property AutoExpand; property BiDiMode; property BorderStyle; property BorderWidth; property ChangeDelay; property Color; property Ctl3D; property Constraints; property DragKind; property DragCursor; property DragMode; property Enabled; property Font; property HideSelection; property HotTrack; property Images; property Indent; property Items; property ParentBiDiMode; property ParentColor default False; property ParentCtl3D; property ParentFont; property ParentShowHint; property PopupMenu; property ReadOnly; property RightClickSelect; property RowSelect; property ShowButtons; property ShowHint; property ShowLines; property ShowRoot; property SortType; property TabOrder; property TabStop default True; property ToolTips; property Visible; property OnChange; property OnChanging; property OnClick; property OnCollapsing; property OnCollapsed; property OnCompare; property OnCustomDraw; property OnCustomDrawItem; property OnDblClick; property OnDeletion; property OnDragDrop; property OnDragOver; property OnEdited; property OnEditing; property OnEndDock; property OnEndDrag; property OnEnter; property OnExit; property OnExpanding; property OnExpanded; property OnGetImageIndex; property OnGetSelectedIndex; property OnKeyDown; property OnKeyPress; property OnKeyUp; property OnMouseDown; property OnMouseMove; property OnMouseUp; property OnStartDock; property OnStartDrag; end;procedure Register;implementation{$R XTreeView.res}const cCheckStatesBitmap = 'CheckStatesBitmap';procedure Register; begin RegisterComponents('DETOOLS', [TXTreeView]); end;{ property manipulation methods }
procedure TXTreeView.FWriteCheckBoxes(Value: boolean); begin FCheckBoxes := Value; if FCheckBoxes then StateImages := CheckStateImages else StateImages := nil; end;function TXTreeView.FReadCheckState(Index: integer): TCheckState; begin if (Index > -1) and (Index < Items.Count) then if Items[Index].StateIndex = -1 then Result := csNone else Result := TCheckState(Items[Index].StateIndex) else Result := csNone; end;procedure TXTreeView.FWriteCheckState(Index: integer; Value: TCheckState); begin if (Index > -1) and (Index < Items.Count) then if Value = csNone then Items[Index].StateIndex := -1 else Items[Index].StateIndex := integer(Value); end;procedure TXTreeView.FWriteCheckBitmap(Value: TBitmap); begin if Value = nil then begin FBitmap.Handle := LoadBitmap(HInstance, cCheckStatesBitmap) end else FBitmap.Assign(Value); CheckStateImages.Clear; // Does Clear free memory or not? CheckStateImages.Add(FBitmap, nil); end;{ internal methods }procedure TXTreeView.BitmapChanged(Sender: TObject); begin CheckStateImages.Clear; CheckStateImages.Add(FBitmap, nil); end; //ret values is 1 全部选中,2 全部未选中 3 部分选中 function TXTreeView.GetAllChildCS(Node:TTreeNode):integer; var ret:integer; num,num1,num2,i:integer; begin num1:=0; num2:=0; num:=node.Count; for i:=0 to num-1 do begin case node.Item[i].StateIndex of 1:inc(num1); 2:inc(num2); end; end; if (num1=num)then ret:=1 else if (num2=num)then ret:=2 else ret:=3; result:=ret; end; procedure TXTreeView.SetChildCS(Node: TTreeNode); var tempnode:ttreenode; stateindex:integer; begin if (node<>nil) then begin stateindex:=node.StateIndex; tempnode:=node.getFirstChild; while(tempnode<>nil) do begin tempnode.StateIndex:=stateindex; if (tempnode<>nil)and(tempnode.HasChildren) then SetChildCS(tempnode); tempnode:=tempnode.getNextSibling; end; end; end; procedure TXTreeView.SetParentCS(Node: TTreeNode); var tempnode:ttreenode; begin if (node<>nil) then begin tempnode:=node.GetPrev; while(tempnode<>nil)do begin if (node.HasAsParent(tempnode))then begin //如果子节点当前状态为选中,那么搜索该父节点的所有子节点, //如果已经全部选中,那末就将该父节点设为选中 //否则设置该父节点为灰色 //如果子节点当前状态为未选中,那么搜索该父节点所有子节点, //如果全部未选中,那么设置该父节点为未选中 //否则设置为灰色 case node.StateIndex of 2: begin case GetAllChildCS(tempnode) of 1: tempnode.StateIndex:=2; 2: tempnode.StateIndex:=2; 3: tempnode.StateIndex:=3; end; end; 1: begin case GetAllChildCS(tempnode) of 1: tempnode.StateIndex:=1; 2,3: tempnode.StateIndex:=3; end; end; end; end; tempnode:=tempnode.GetPrev; end; end; end; procedure TXTreeView.ChangeCheckState(Node: TTreeNode); begin if CheckStates[Node.AbsoluteIndex] = csUnchecked then CheckStates[Node.AbsoluteIndex] := csChecked else if CheckStates[Node.AbsoluteIndex] = csChecked then begin CheckStates[Node.AbsoluteIndex] := csUnchecked { if FThreeState then CheckStates[Node.AbsoluteIndex] := csGrayed else CheckStates[Node.AbsoluteIndex] := csUnchecked } end else CheckStates[Node.AbsoluteIndex] := csUnchecked; SetChildCS(node); SetParentCS(Node); end; { overrided methods }constructor TXTreeView.Create(AOwner: TComponent); begin inherited Create(AOwner); CheckStateImages := TImageList.Create(Self); FBitmap := TBitmap.Create; FBitmap.OnChange := BitmapChanged; FBitmap.Handle := LoadBitmap(HInstance, cCheckStatesBitmap); StateImages := CheckStateImages; FThreeState := false; CheckBoxes := true; ParentColor := False; TabStop := True; end;destructor TXTreeView.Destroy; begin FBitmap.Free; CheckStateImages.Free; inherited Destroy; end;procedure TXTreeView.CNNotify(var Message: TWMNotify); var Node: TTreeNode; Point: TPoint; Position: DWORD; begin case message.nmhdr.code of NM_CLICK: begin Position := GetMessagePos; Point.x := LoWord(Position); Point.y := HiWord(Position); Point := ScreenToClient(Point); Node := GetNodeAt(Point.x, Point.y); if (Node <> nil) then begin if htOnStateIcon in GetHitTestInfoAt(Point.x, Point.y) then begin ChangeCheckState(Node); if Assigned(FStateClickEvent) then FStateClickEvent(CheckStates[Node.AbsoluteIndex]); end; end; end; end; inherited; end;end.
==============
TXTreeView 1.0 (1999-07-11)
============== Enhaced TTreeView with 2- or 3-state checkboxes. Freeware. Copyright ?Roman Stedronsky 1999, [email protected] All rights reserved. You may use this software in an application
without fee or royalty, provided this copyright notice remains intact. types
-----
TCheckState defines 4 states for every node (No check,
Unchecked, Checked, Grayed)
public properties
-----------------
CheckStates[Index: integer] set/get the state for given node (by index)
published properties
--------------------
CheckBoxes when true, shows checkboxes
ThreeState when true, use 3-state cycle (un-checked-grayed)
when false, use 2-state cycle (unchecked-checked)
CheckBitmap defines visual appearance of checkboxes
(Width: 64 /4x16/, height: 16. See default one.)
events
------
OnStateClick occures after changing state via mouse
(Not when changing CheckStates!)
Note:
Every new node is in the state csNone by default (checkbox is not visible).
You must explicitly change it by CheckStates property. (You can also use
node's StateIndex as shown below, but why?) StateIndex CheckState
-1 csNone
1 csUnchecked
2 csChecked
3 csGrayed
}interfaceuses
Windows, Messages, Classes, Graphics, Controls, ComCtrls, Commctrl;type
TCheckState = (csNone, csUnchecked, csChecked, csGrayed);
TStateClickEvent = procedure(CheckState: TCheckState) of object; TXTreeView = class(TCustomTreeView)
protected
{ internal variables }
FBitmap: TBitmap;
CheckStateImages: TImageList;
{ property variables }
FCheckBoxes: boolean;
FThreeState: boolean;
FStateClickEvent: TStateClickEvent;
{ property manipulation methods }
procedure FWriteCheckBoxes(Value: boolean);
function FReadCheckState(Index: integer): TCheckState;
procedure FWriteCheckState(Index: integer; Value: TCheckState);
procedure FWriteCheckBitmap(Value: TBitmap);
{ internal methods }
procedure ChangeCheckState(Node: TTreeNode);
procedure SetChildCS(Node: TTreeNode);
procedure SetParentCS(Node: TTreeNode);
function GetAllChildCS(Node:TTreeNode):integer;
procedure BitmapChanged(Sender: TObject);
public
{ overrided methods }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure CNNotify(var Message: TWMNotify); message CN_NOTIFY;
property CheckStates[Index: integer]: TCheckState read FReadCheckState write FWriteCheckState;
published
property CheckBoxes: boolean read FCheckBoxes write FWriteCheckBoxes default true;
property ThreeState: boolean read FThreeState write FThreeState default false;
property CheckBitmap: TBitmap read FBitmap write FWriteCheckBitmap stored true default nil;
property OnStateClick: TStateClickEvent read FStateClickEvent write FStateClickEvent;
published
{ make TCustomTreeView propeties published (exclude StateImages) }
property Align;
property Anchors;
property AutoExpand;
property BiDiMode;
property BorderStyle;
property BorderWidth;
property ChangeDelay;
property Color;
property Ctl3D;
property Constraints;
property DragKind;
property DragCursor;
property DragMode;
property Enabled;
property Font;
property HideSelection;
property HotTrack;
property Images;
property Indent;
property Items;
property ParentBiDiMode;
property ParentColor default False;
property ParentCtl3D;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ReadOnly;
property RightClickSelect;
property RowSelect;
property ShowButtons;
property ShowHint;
property ShowLines;
property ShowRoot;
property SortType;
property TabOrder;
property TabStop default True;
property ToolTips;
property Visible;
property OnChange;
property OnChanging;
property OnClick;
property OnCollapsing;
property OnCollapsed;
property OnCompare;
property OnCustomDraw;
property OnCustomDrawItem;
property OnDblClick;
property OnDeletion;
property OnDragDrop;
property OnDragOver;
property OnEdited;
property OnEditing;
property OnEndDock;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnExpanding;
property OnExpanded;
property OnGetImageIndex;
property OnGetSelectedIndex;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnStartDock;
property OnStartDrag;
end;procedure Register;implementation{$R XTreeView.res}const
cCheckStatesBitmap = 'CheckStatesBitmap';procedure Register;
begin
RegisterComponents('DETOOLS', [TXTreeView]);
end;{ property manipulation methods }
begin
FCheckBoxes := Value;
if FCheckBoxes then
StateImages := CheckStateImages
else
StateImages := nil;
end;function TXTreeView.FReadCheckState(Index: integer): TCheckState;
begin
if (Index > -1) and (Index < Items.Count) then
if Items[Index].StateIndex = -1 then
Result := csNone
else
Result := TCheckState(Items[Index].StateIndex)
else
Result := csNone;
end;procedure TXTreeView.FWriteCheckState(Index: integer; Value: TCheckState);
begin
if (Index > -1) and (Index < Items.Count) then
if Value = csNone then
Items[Index].StateIndex := -1
else
Items[Index].StateIndex := integer(Value);
end;procedure TXTreeView.FWriteCheckBitmap(Value: TBitmap);
begin
if Value = nil then
begin
FBitmap.Handle := LoadBitmap(HInstance, cCheckStatesBitmap)
end
else
FBitmap.Assign(Value);
CheckStateImages.Clear; // Does Clear free memory or not?
CheckStateImages.Add(FBitmap, nil);
end;{ internal methods }procedure TXTreeView.BitmapChanged(Sender: TObject);
begin
CheckStateImages.Clear;
CheckStateImages.Add(FBitmap, nil);
end;
//ret values is 1 全部选中,2 全部未选中 3 部分选中
function TXTreeView.GetAllChildCS(Node:TTreeNode):integer;
var
ret:integer;
num,num1,num2,i:integer;
begin
num1:=0;
num2:=0;
num:=node.Count;
for i:=0 to num-1 do
begin
case node.Item[i].StateIndex of
1:inc(num1);
2:inc(num2);
end;
end;
if (num1=num)then
ret:=1
else
if (num2=num)then
ret:=2
else
ret:=3;
result:=ret;
end;
procedure TXTreeView.SetChildCS(Node: TTreeNode);
var
tempnode:ttreenode;
stateindex:integer;
begin
if (node<>nil) then
begin
stateindex:=node.StateIndex;
tempnode:=node.getFirstChild;
while(tempnode<>nil) do
begin
tempnode.StateIndex:=stateindex;
if (tempnode<>nil)and(tempnode.HasChildren) then
SetChildCS(tempnode);
tempnode:=tempnode.getNextSibling;
end;
end;
end;
procedure TXTreeView.SetParentCS(Node: TTreeNode);
var
tempnode:ttreenode;
begin
if (node<>nil) then
begin
tempnode:=node.GetPrev;
while(tempnode<>nil)do
begin
if (node.HasAsParent(tempnode))then
begin
//如果子节点当前状态为选中,那么搜索该父节点的所有子节点,
//如果已经全部选中,那末就将该父节点设为选中
//否则设置该父节点为灰色
//如果子节点当前状态为未选中,那么搜索该父节点所有子节点,
//如果全部未选中,那么设置该父节点为未选中
//否则设置为灰色
case node.StateIndex of
2:
begin
case GetAllChildCS(tempnode) of
1:
tempnode.StateIndex:=2;
2:
tempnode.StateIndex:=2;
3:
tempnode.StateIndex:=3;
end;
end;
1:
begin
case GetAllChildCS(tempnode) of
1:
tempnode.StateIndex:=1;
2,3:
tempnode.StateIndex:=3;
end;
end;
end;
end;
tempnode:=tempnode.GetPrev;
end;
end;
end;
procedure TXTreeView.ChangeCheckState(Node: TTreeNode);
begin
if CheckStates[Node.AbsoluteIndex] = csUnchecked then
CheckStates[Node.AbsoluteIndex] := csChecked
else
if CheckStates[Node.AbsoluteIndex] = csChecked then
begin
CheckStates[Node.AbsoluteIndex] := csUnchecked
{
if FThreeState then
CheckStates[Node.AbsoluteIndex] := csGrayed
else
CheckStates[Node.AbsoluteIndex] := csUnchecked
}
end
else
CheckStates[Node.AbsoluteIndex] := csUnchecked;
SetChildCS(node);
SetParentCS(Node);
end;
{ overrided methods }constructor TXTreeView.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
CheckStateImages := TImageList.Create(Self);
FBitmap := TBitmap.Create;
FBitmap.OnChange := BitmapChanged;
FBitmap.Handle := LoadBitmap(HInstance, cCheckStatesBitmap);
StateImages := CheckStateImages;
FThreeState := false;
CheckBoxes := true;
ParentColor := False;
TabStop := True;
end;destructor TXTreeView.Destroy;
begin
FBitmap.Free;
CheckStateImages.Free;
inherited Destroy;
end;procedure TXTreeView.CNNotify(var Message: TWMNotify);
var
Node: TTreeNode;
Point: TPoint;
Position: DWORD;
begin
case message.nmhdr.code of
NM_CLICK:
begin
Position := GetMessagePos;
Point.x := LoWord(Position);
Point.y := HiWord(Position);
Point := ScreenToClient(Point);
Node := GetNodeAt(Point.x, Point.y);
if (Node <> nil) then
begin
if htOnStateIcon in GetHitTestInfoAt(Point.x, Point.y) then
begin
ChangeCheckState(Node);
if Assigned(FStateClickEvent) then
FStateClickEvent(CheckStates[Node.AbsoluteIndex]);
end;
end;
end;
end;
inherited;
end;end.