大家好!前面看了万一老师的博客,用
SetWindowLong(H, GWL_STYLE, GetWindowLong(H, GWL_STYLE) or TVS_CHECKBOXES)
实现在treeview前加选择框。试了一下,可以,不过我有一个疑问,如何用代码控制将某个节点置为“选中”或“未选中”状态呢?我的目的是想,在某个节点状态改变(选中或未选中)时,自动改变父节点和子节点的状态。
谢谢!

解决方案 »

  1.   

    把treeview和imagelist结合起来使用,很方便的就可以达到楼主想要的效果。
      

  2.   

    就是修改TreeView的StateIndex来改变选中未选中,ImageList中添加2个图标,一个是不打勾的,一个打勾的,然后改StateIndex就可以
      

  3.   

    1stClass 里面有这样的组件
      

  4.   

    曾经写过的一个控件unit CheckedTreeView;interfaceuses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
      ComCtrls, Commctrl;type
      TTVCheckChangedEvent = procedure(Sender: TObject; Node: TTreeNode; Checked: boolean) of object;
      TCheckedTreeView = class(TTreeView)
      private
        { Private declarations }  protected
        { Protected declarations }
        F_OnNodeCheckChanged: TTVCheckChangedEvent;  //当一个Node的Checked属性发生变化后触发
        F_CheckedNodeList: TList;               //纪录所有被Checked的节点
        F_CheckBoxes: Boolean;                  //是否显示CheckBox
        //某个Node被选中或反选后触发
        procedure Do_NodeChecked(node: TTreeNode; checked: Boolean);virtual;
        procedure CNNotify(var Message: TWMNotify); message CN_NOTIFY;
        procedure SetCheckBoxes(value: Boolean);
        function GetNodeFromItem(const Item: TTVItem): TTreeNode;
        procedure Delete(Node: TTreeNode); override;
      public
        { Public declarations }
        constructor Create(AOwner: TComponent); override;
        destructor Destroy; override;
        function NodeChecked(node: TTreeNode): boolean;
        procedure CheckNode(Node :TTreeNode; Checked :Boolean);
      published
        { Published declarations }
        property OnNodeCheckedChanged: TTVCheckChangedEvent read F_OnNodeCheckChanged write F_OnNodeCheckChanged;
        property CheckBoxes: boolean read F_CheckBoxes write SetCheckBoxes;
      end;function IsNodeChecked(node: TTreeNode): boolean;
    procedure SetNodeChecked(Node :TTreeNode; Checked :Boolean);
    procedure Register;implementationconst
          TVIS_CHECKED = $2000;
          TVIS_UNCHECKED = $1000;
          TVS_CHECKBOXES = $0100;procedure Register;
    begin
      RegisterComponents('Samples', [TCheckedTreeView]);
    end;
    procedure SetComCtlStyle(Ctl: TWinControl; Value: Integer; UseStyle: Boolean);
    var
      Style: Integer;
    begin
        if Ctl.HandleAllocated  then
        begin
            Style := GetWindowLong(Ctl.Handle, GWL_STYLE);
            if   not   UseStyle   then
                Style := Style and (not Value)
            else
                Style := Style or Value;
            SetWindowLong(Ctl.Handle, GWL_STYLE, Style);
        end;
    end;function IsNodeChecked(node: TTreeNode): boolean;
    var
        TvItem:TTVItem;
    begin
        TvItem.Mask := TVIF_STATE;
        TvItem.hItem := Node.ItemId;
        TreeView_GetItem(Node.TreeView.Handle, TvItem);
        Result := (TvItem.State and TVIS_CHECKED) = TVIS_CHECKED;
    end;procedure SetNodeChecked(Node :TTreeNode; Checked :Boolean);
    var
        TvItem:TTVItem;
    begin
        FillChar(TvItem,   SizeOf(TvItem),   0);
        with TvItem do
        begin
            hItem :=Node.ItemId;
            Mask := TVIF_STATE;
            StateMask := TVIS_STATEIMAGEMASK;
            if Checked  then
                TvItem.State:= (TvItem.State and (not TVIS_UNCHECKED)) or TVIS_CHECKED
            else
                TvItem.State:=(TvItem.State and (not TVIS_CHECKED)) or TVIS_UNCHECKED;
            TreeView_SetItem(Node.TreeView.Handle, TvItem);
        end;
    end;{ TCheckedTreeView }procedure TCheckedTreeView.CNNotify(var Message: TWMNotify);
    var
        bOriginalChecked: Boolean;
        bCurrentChecked: Boolean;
        nIndex: Integer;
        node: TTreeNode;
    begin
        inherited;
        With Message do
        begin
            case NMHdr^.code of
                TVN_GETDISPINFO:
                begin
                    with PTVDispInfo(NMHdr)^ do
                    begin
                        Node := GetNodeFromItem(item);
                        bCurrentChecked:= IsNodeChecked(node);
                        nIndex:= F_CheckedNodeList.IndexOf(node);
                        if nIndex >= 0 then
                            bOriginalChecked:= true
                        else
                            bOriginalChecked:= false;                    if bOriginalChecked <> bCurrentChecked then
                        begin
                            if bCurrentChecked then
                                F_CheckedNodeList.Add(node)
                            else
                                F_CheckedNodeList.Delete(nIndex);
                            Do_NodeChecked(node, bCurrentChecked);
                        end ;
                    end ;
                end;
            end ; //case
        end;
    end ;
    constructor TCheckedTreeView.Create(AOwner: TComponent);
    begin
      inherited;
      F_CheckBoxes:= false;
      F_CheckedNodeList:= TList.Create;
    end;destructor TCheckedTreeView.Destroy;
    begin
      F_CheckedNodeList.Clear;
      F_CheckedNodeList.Free;
      inherited;end;function TCheckedTreeView.NodeChecked(node: TTreeNode): boolean;
    begin
        Result:= IsNodeChecked(node);
    end ;procedure TCheckedTreeView.CheckNode(Node :TTreeNode; Checked :Boolean);
    var
        nIndex: Integer;
    begin
        SetNodeChecked(node, checked);
        nIndex:= F_CheckedNodeList.IndexOf(node);
        if checked and (nIndex< 0) then
        begin
            F_CheckedNodeList.Add(Node);
            Do_NodeChecked(node, checked);
        end
        else if (not checked) and (nIndex>=0 ) then
        begin
            F_CheckedNodeList.Delete(nIndex);
            Do_NodeChecked(node, checked);
        end ;
    end ;procedure TCheckedTreeView.Do_NodeChecked(node: TTreeNode;
      checked: Boolean);
    begin
        if  F_CheckBoxes then
            if Assigned(F_OnNodeCheckChanged) then
                F_OnNodeCheckChanged(self,node, checked);
    end;function TCheckedTreeView.GetNodeFromItem(const Item: TTVItem): TTreeNode;
    begin
      Result := nil;
      if Items <> nil then
        with Item do
          if (state and TVIF_PARAM) <> 0 then Result := Pointer(lParam)
          else Result := Items.GetNode(hItem);
    end;procedure TCheckedTreeView.SetCheckBoxes(value: Boolean);
    var
        nIndex: Integer;
        TvItem:TTVItem;
    begin
        if Value<> F_CheckBoxes then
        begin
            F_CheckBoxes:= value;
            if value then
                SetComCtlStyle(Self,TVS_CHECKBOXES,True)
            else
            begin
                SetComCtlStyle(Self,TVS_CHECKBOXES,False);
                F_CheckedNodeList.Clear ;
                //Update all nodes' state to remove the boxes
                for nIndex := 0 to Items.Count -1 do
                begin
                    FillChar(TvItem, SizeOf(TvItem),   0);
                    with TvItem do
                    begin
                        hItem :=Items.Item[nIndex].ItemId;
                        Mask := TVIF_STATE;
                        StateMask := TVIS_STATEIMAGEMASK;
                        TreeView_GetItem(Handle, TvItem);
                        TVItem.State:= TVItem.state and (Not TVIS_UNCHECKED) and (Not TVIS_CHECKED);
                        TreeView_SetItem(Handle, TvItem);
                    end;            end ;
            end ;    end ;
    end ;procedure TCheckedTreeView.Delete(Node: TTreeNode);
    var
        nIndex: Integer;
    begin
        nIndex:= F_CheckedNodeList.IndexOf(node);
        if nIndex >= 0 then
            F_CheckedNodeList.Delete(nIndex);
        inherited;
    end;end.