TREEVIEW联接数据库问题:(50分)

  • 主题发起人 主题发起人 lyplay2
  • 开始时间 开始时间
L

lyplay2

Unregistered / Unconfirmed
GUEST, unregistred user!
表名dw
id name pid
01 分公司一 1
0101 厂部 2
010101 办公室 3
01010101 财务处 4
01010102 后勤处 4
010102 人事科 3
010103 供销科 3
0102 维修部 2
02 分公司二 1
0201 厂部 2
0202 维修部 2

其中PID为所在级数:
1=01,02
2=0101,0102。。。
3=010101,010102。。。
4=01010101,01010102。。。
有什么好的办法让它和TREEVIEW联接起来吗?
请贴出代码,我查了以前的贴子,还是似懂非懂的。
谢谢。

 
呵呵, 我刚做完, 只要用TreeView的AddObject或AddChildObject就行了,
在OnExpanding中添加下一节点, 这样的话不致于开始的时候程序很慢,
 
能详细些吗?
 
我所应用的树,希望对你有帮助
代码:
{*******************************************************}
{                                                       }
{              XXX公司公共组件库                        }
{                                                       }
{       版权所有 (C) 2001-2002 XXX公司                  }
{                                                       }
{*******************************************************}

unit aTreeView;


{*******************************************************************************
项目:XXX公司公共组件库
模块:用树状视图显示层次结构的数据表控件TaTreeView
描述:适用的表结构
        KeyId:integer
        DisplayName:string
        ParentId:integer (自连接到KeyId)
        RootId:integer (如果FRootId=-1 就从数据库计算所有的节点,
                        否则从FRootID开始计算节点 )


     注意问题:
        1.必须全部指定数据:Connection,TableName,KeyField,DisplayField,ParentField
          才能执行Execute
        2.如果使用多选功能,必须指定StateImages属性、CheckIndex,UnCheckIndex,GrayedIndex


     本控件用法
        1.设定Connection:TADOConnection,
              TableName/KeyField/DisplayField/ParentField
        2.运行Execute即可
        3.如果查指定的公司,指定RootId就可以了


     实现原理
        1.运行Execute 就生成根的节点,在其下建一个临时节点tempNodeName
          (作用是在父节点上显示一个+号)
        2.点击一个节点时删除临时节点,并生成其下所有的子节点
        这样做的好处是不用一次过求所有的节点,节约了时间


     公开属性、方法:
        1.  Execute
            生成Node //输出的过程

        2.  ExecuteEx(ADataSet:TDataSet)
            根据ADataSet一次生成所有Node,不需要填写Connection,TableName

        3.  FindId:integer
            用作后台查找SQL用。当指定了FindId的时候,返回的SQL将是FindId节点下的
            Sql字符串。如果StateImages有值,FindId不起作用。

        4.  KeyValue:string
            如果FindId是-1的情况下,返回的是当前选择(OnFocus)节点的关键信息否则返
            回的是FindId的关键信息

        5.  SQL:string
            包含所有节点关键字的信息,如果StateImages指定了值得情况下,则是所有
            选中节点的信息。例如: '1,2,3,4,5'

        6.  CheckIndex:integer
            StateImages 中的小图标,表示选中状态

        7.  Connection:TADOConnection
            TableName的ADOConnection

        8.  DisplayField:string
            TableName 中显示在node.Text的字段名

        9.  GrayedIndex:integer
            StateImages 中的小图标,表示半选中状态

        10. ImageIndex:integer
            所有节点的ImgIndex

        11. KeyField:string
            TableName 中关键字的字段名

        12. ParentField:string
            TableName 中父关键字的字段名

        13. RootId:integer
            树状视图的根节点位置。如果FRootId=-1 就从数据库计算所有的节点,否则
            从FRootID开始计算节点

        14. TableName:string
            获取树状视图的表名

        15. UnCheckIndex:integer     StateImages 中的小图标,表示未选中状态


     未实现:
        1.没有将Check,UnCheck,Grayed图片包含到资源包中。所以当需要使用多选功能时
          必须指定StateImages

        2.没有定义设计期的编辑器

        3.没有定义从那个节点开始的方法。所以不能在执行完Execute方法后焦点落到指
          定的Node上面,虽然可以通过调用Expand,然后查找得出需要,但是可能会出现
          闪烁的情况


版本:1.5
日期:?
作者:刘秋华 Delphi1999@21cn.com
更新:2002-9-16 第6次更新 加入了ExecuteEx方法,支持一次过载入全部的节点
     2002-3-20 第5次更新 支持多选操作;加入大量注释
     2001-10-30 第4次更新
TODO:
********************************************************************************}


interface


uses
  Windows, SysUtils, Classes,ComCtrls, ADODB,Messages,Controls,ImgList,DB;


type
  TaTreeView = class(TTreeView)
  private


    FCheckIndex: integer;


    FConnection: TADOConnection;


    FDisplayField: string;


    FFindId: integer;


    FGrayedIndex: integer;


    FImgIndex: integer;


    FKeyField: string;


    FList:TStringList;


    FParentField: string;


    FRootId: integer;


    FSQL: string;


    FTableName: string;


    FUnCheckIndex: integer;


    function GetKeyValue: string;


    function GetSQL: string;


  protected


    FOldOnExpanding: TTVExpandingEvent;//记录用户定义的OnExpanding


    FQuery: TADOQuery;//用于从数据表中获取最上层节点和各层节点


  { 根据Node判断它的子节点是否为空 }
    function DeleteTempNode(Node: TTreeNode): Boolean;//Node:需要监测的节点


    { 根据aFindId查找TTreeNode }
    function FindNode(aFindId:integer):TTreeNode;


    { 在Node下加入Item }
    procedure AddItem(Node:TTreeNode);


    { 响应原来的OnExpanding事件,并生成子节点并执行用户的OnExpanding }
    procedure DoExpanding(Sender: TObject; Node: TTreeNode;
      var AllowExpansion: Boolean);


    procedure Loaded; override;


    { 当StateImage存在的时候,处理Check,UnCheck的显示问题 }
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;


    { 根据Node将SQL写入FSQL,如果StateImages存在则根据全部选中的节点得出SQL }
    procedure SetSqlStr(Node: TTreeNode);


  public


    { 生成Node //输出的过程 }
    procedure Execute;


    { 一次过生成所有节点 //输出过程 }
    procedure ExecuteEx(ADataSet:TDataSet);


    { 用作后台查找SQL用。当指定了FindId的时候,返回的SQL将是FindId节点下的Sql
      字符串。如果StateImages有值,FindId不起作用。}
    property FindId:integer read FFindId write FFindId default -1;


    { 如果FindId是-1的情况下,返回的是当前选择(OnFocus)节点的关键信息
      否则返回的是FindId的关键信息 }
    property KeyValue: string read GetKeyValue;


    { 包含所有节点关键字的信息,如果StateImages指定了值得情况下,则是所有选中
      节点的信息。例如: '1,2,3,4,5' }
    property SQL: string read GetSQL;


    constructor Create(AOwner: TComponent); override;


    destructor Destroy; override;


  published


    { StateImages 中的小图标,表示选中状态 }
    property CheckIndex:integer read FCheckIndex write FCheckIndex;


    { TableName的ADOConnection }
    property Connection: TADOConnection read FConnection write FConnection;


    { TableName 中显示在node.Text的字段名 }
    property DisplayField: string read FDisplayField write FDisplayField;


    { StateImages 中的小图标,表示半选中状态 }
    property GrayedIndex:integer read FGrayedIndex write FGrayedIndex;


    { 所有节点的ImgIndex }
    property ImgIndex:integer read FImgIndex write FImgIndex;


    { TableName 中关键字的字段名 }
    property KeyField: string read FKeyField write FKeyField;


    { TableName 中父关键字的字段名 }
    property ParentField: string read FParentField write FParentField;


    { 树状视图的根节点位置。如果FRootId=-1 就从数据库计算所有的节点,
      否则从FRootID开始计算节点 }
    property RootID:integer read FRootId write FRootId default -1;


    { 获取树状视图的表名 }
    property TableName: string read FTableName write FTableName;


    { StateImages 中的小图标,表示未选中状态 }
    property UnCheckIndex:integer read FUnCheckIndex write FUnCheckIndex;


  end;


implementation


const
  tempNodeName: string = 'DbTreeTempNode';//临时节点名称


{ TaTreeView }


function TaTreeView.DeleteTempNode(Node: TTreeNode): Boolean;
var tempNode: TTreeNode;
begin
  Result := False;
  if (Node.Count = 1) then
  begin
    tempNode := Node.GetFirstChild;
    if (tempNode.Text = tempNodeName) then
    begin
      tempNode.Delete;
      Result := True;
    end;
  end;
end;


constructor TaTreeView.Create(AOwner: TComponent);
begin
  inherited;
  FQuery := TADOQuery.Create(Self);
  FList:=TStringList.Create;
  FCheckIndex:=-1;
  FFindId:=-1;
  FGrayedIndex:=-1;
  FImgIndex:=-1;
  FRootId:=-1;
  FUnCheckIndex:=-1;
end;


destructor TaTreeView.Destroy;
var i: Integer;
  Node: TTreeNode;
  pid: PInteger;
begin
  FQuery.Free;
  for i := 0 to Items.Count - 1 do
  begin
    Node := Items[i];
    if Node.Text <> tempNodeName then begin
      pid := Node.Data;
      Dispose(pid);
    end;
  end;
  FList.Free;
  inherited;
end;


procedure TaTreeView.DoExpanding(Sender: TObject; Node: TTreeNode;
  var AllowExpansion: Boolean);
begin
  AddItem(Node);
  if Assigned(FOldOnExpanding) then//inherited
    FOldOnExpanding(Sender, Node, AllowExpansion);
end;


procedure TaTreeView.Execute;
const
  RootSql: string = 'select %s from %s where %s=%s';//[KeyField + ',' + DisplayField, TableName, ParentField, KeyField]
var Node: TTreeNode;
  pid: PInteger;
begin
  if (FConnection = nil) or (FTableName = '') or (FKeyField = '') or
  (FDisplayField = '') or (FParentField = '') then
    raise Exception.Create('please enter Connection/TableName/KeyField/DisplayField/ParentField');
  Items.Clear;
  FList.Clear;

  { 如果FRootId=-1 就从数据库计算所有的节点,否则从FRootID开始计算节点 }
  if FRootId<>-1 then
  begin
    FQuery.Sql.Text:=Format(RootSql,[FKeyField+','+FDisplayField,FTableName,FKeyField,IntToStr(FRootId)]);
  end
  else
    FQuery.SQL.Text := Format(RootSql, [FKeyField + ',' + FDisplayField, FTableName, FParentField, FKeyField]);

  FQuery.Open;
  Node := Items.Add(nil, FQuery.FieldByName(FDisplayField).AsString);
  if FImgIndex<>-1 then
  begin
    Node.ImageIndex:=FImgIndex;
    Node.SelectedIndex:=FImgIndex;
    if Assigned(StateImages) then
      Node.StateIndex:=FUnCheckIndex;
  end;
  New(pid);
  pid^:=FQuery.FieldByName(FKeyField).AsInteger;
  Node.Data := pid;
  with Items.AddChild(Node, tempNodeName) do
    if Assigned(StateImages) then StateIndex:=FUnCheckIndex;
  FQuery.Close;
  Node.Expand(false);
  Node.Selected:=true;
end;


procedure TaTreeView.Loaded;
begin
  inherited;
  FOldOnExpanding := OnExpanding;
  OnExpanding := DoExpanding;
  
  FQuery.Connection := FConnection;
  if Assigned(Images) then
    if FImgIndex=-1 then FImgIndex:=0;
  if Assigned(StateImages) then
  begin
    if FCheckIndex=-1 then FCheckIndex:=1;
    if FUnCheckIndex=-1 then FUnCheckIndex:=2;
    if FGrayedIndex=-1 then FGrayedIndex:=3;
  end;
end;


function TaTreeView.GetKeyValue: string;
var SelNode: TTreeNode;
    pId: PInteger;
begin
  Result := '';
  if FFindId<>-1 then
    result := IntToStr(FFindId)
  else
  begin
    SelNode := Selected;
    pId := SelNode.Data;
    Result := IntToStr(pId^);
  end;
end;


function TaTreeView.GetSQL: string;
var tempNode: TTreeNode;
    i:integer;
    sKey:string;
begin
  // 根据不同的情况返回一个节点,然后从此节点开始计算SQL
  if FFindId=-1 then
    tempNode := Selected
  else
  begin
    tempNode := FindNode(FFindId);
  end;
  if Assigned(StateImages) then
    tempNode := Items.GetFirstNode;

  if not Assigned(tempNode) then
    raise Exception.Create('没有节点被选择!');

  // 非StateImages情况下,将采取将得出的SQL保存在一个列表中,当下次再查找
  // 同样情况的时候,直接返回
  i:=-1;
  FSQL := '';
  if not Assigned(StateImages) then
  begin
    sKey:=GetKeyValue;
    i:=FList.IndexOfName(sKey);
  end;

  // 真正计算SQL
  if i=-1 then
  begin
    AddItem(tempNode);//暗中展开tempNode
    SetSqlStr(tempNode);//计算tempNode的SQL
    if not Assigned(StateImages) then
      FList.Add(sKey+'='+FSQL);
  end
  else
  begin
    FSQL:=FList.Values[sKey];
  end;
  Result := FSQL;
end;


procedure TaTreeView.SetSqlStr(Node: TTreeNode);
var i: Integer;
  pid: PInteger;
begin
  // StateImages存在的情况下优先处理
  if Assigned(StateImages) then
  begin
    if Node.Count=0 then begin
      if Node.StateIndex=FCheckIndex then
      begin
        pid:=Node.Data;
        FSQL:=IntToStr(pid^);
      end;
    end
    else
      for i:=0 to Node.Count -1 do begin
        AddItem(Node.Item[i]);
        if Node.Item[i].Count=0 then begin
          if Node.Item[i].StateIndex=FCheckIndex then
          begin
            pid:=Node.Item[i].Data;
            if FSQL<>'' then FSQL:=FSQL+',';
            FSQL:=FSQL+IntToStr(pid^);
          end;
        end
        else
        begin
          SetSqlStr(Node.Item[i])
        end;
      end;

  end
  // 处理StateImages 不存在的情况
  else
  begin
    if Node.Count = 0 then begin
      pid := Node.Data;
      FSQL := IntToStr(pid^);
    end
    else
      for i := 0 to Node.Count - 1 do begin
        AddItem(Node.Item[i]);
        if Node.Item[i].Count = 0 then begin
          pid := Node.Item[i].Data;
          if FSQL <> '' then FSQL := FSQL + ',';
          FSQL := FSQL + IntToStr(pid^);
        end
        else
          SetSqlStr(Node.Item[i]);
      end;
  end;
end;


procedure TaTreeView.AddItem(Node: TTreeNode);
const
  ChildSql: string = 'select %s from %s where (%s=:icode) and (%s<>%s)';//[KeyField + ',' + DisplayField, TableName, ParentField, KeyField, ParentField]
var pId: PInteger;
  tempNode: TTreeNode;
begin
  if DeleteTempNode(Node) then begin
    pId := Node.Data;
    FQuery.SQL.Text := Format(ChildSql, [FKeyField + ',' + FDisplayField, FTableName, FParentField, FKeyField, FParentField]);
    FQuery.Parameters[0].Value := pId^;
    FQuery.Open;
    FQuery.DisableControls;
    //   DisableAlign;
    while not FQuery.EOF do begin
      New(pId);
      pId^:=FQuery.FieldByName(FKeyField).AsInteger;
      tempNode := Items.AddChild(Node, FQuery.FieldByName(FDisplayField).AsString);
      tempNode.Data := pId;
      if FImgIndex<>-1 then
      begin
        tempNode.ImageIndex:=FImgIndex;
        tempNode.SelectedIndex:=FImgIndex;
        tempNode.StateIndex:=Node.StateIndex;
      end;
      Items.AddChild(tempNode, tempNodeName);
      FQuery.Next;
    end;
    //  EnableAlign;
    FQuery.EnableControls;
    FQuery.Close;
  end;
end;


function TaTreeView.FindNode(aFindId: integer): TTreeNode;
var i:integer;
    Node:TTreeNode;
    pId:pInteger;
    { 暗中展开所有的节点 }
    procedure FullExpand(ANode:TTreeNode);
    var i:integer;
    begin
      AddItem(ANode);
      for i:=1 to ANode.Count do
        FullExpand(ANode.Item[i-1]);
    end;
begin
  result:=nil;

  // 展开所有节点
  Node:=Items.GetFirstNode;
  while Assigned(Node) do
  begin
    FullExpand(Node);
    Node:=Node.getNextSibling;
  end;

  // 查找需要的节点
  for i:=0 to Items.Count-1 do
  begin
    pId:=Items[i].Data;
    if pId^=aFindId then
    begin
      result:=Items[i];
      exit;
    end;
  end;

  if not assigned(result) then
    raise exception.Create('not found Node:'+IntToStr(aFindId));
end;


procedure TaTreeView.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
var
  MyHitTest : THitTests;
  ANode:TTreeNode;
  { 设置所有子节点显示状态 }
  procedure SetAllChildNode(ANode:TTreeNode;AValue:integer);
  var
    i:Integer;
  begin
    ANode.StateIndex:=AValue;
    if ANode.Count=0 then exit;
    for i:=0 to ANode.Count-1 do
    begin
      if ANode.Item[i].Count=0 then
        ANode.Item[i].StateIndex:=AValue
      else
        SetAllChildNode(ANode.Item[i],AValue);
    end;
  end;
  { 设置所有父节点显示状态 }
  procedure SetAllRootNode(ANode:TTreeNode);
  var
    RootNode:TTreeNode;
    RootValue:integer;
    i:integer;
  begin
    RootNode:=ANode.Parent;
    if not Assigned(RootNode) then exit;
    RootValue:=0;
    for i:=0 to RootNode.Count-1 do
    begin
      if RootValue=0 then
        RootValue:=RootNode.Item[i].StateIndex
      else if RootValue<>RootNode.Item[i].StateIndex then
        if RootValue<>FGrayedIndex then
          RootValue:=FGrayedIndex;
    end;
    RootNode.StateIndex:=RootValue;
    SetAllRootNode(RootNode);
  end;
begin
  inherited;
  if not Assigned(StateImages) then exit;
  MyHitTest := GetHitTestInfoAt(X,Y);
  if htOnStateIcon in MyHitTest then
  begin
    ANode:=GetNodeAt(x,y);
    if ANode.StateIndex<>FUnCheckIndex then
      ANode.StateIndex:=FUnCheckIndex
    else
      ANode.StateIndex:=FCheckIndex;
    SetAllChildNode(ANode,ANode.StateIndex);//设置所有子对象StateIndex
    SetAllRootNode(ANode);//设置所有父对象StateIndex
  end;
end;


procedure TaTreeView.ExecuteEx(ADataSet: TDataSet);//一次过生成所有节点
var
  LoadRec:boolean;//用来处理RootID<>-1时候过滤记录用
  function FindItem(AFindID:integer):TTreeNode;
  var i:Integer;
      pID:PInteger;
  begin
    result:=nil;
    for i:=0 to Items.Count-1 do
    begin
      pId:=Items[i].Data;
      if pId^=AFindID then
      begin
        result:=Items[i];
        exit;
      end;
    end;
  end;
  procedure AddAItem(ACap: string; AParent, AID: integer);
  var
    ANode, ParentNode: TTreeNode;
    APoint: PInteger;
  begin
    ParentNode := FindItem(AParent);
    New(APoint);
    APoint^ := AID;
    if assigned(ParentNode) then
      ANode := Items.AddChild(ParentNode, ACap)
    else
      ANode := Items.Add(ParentNode, ACap);
    if FImgIndex<>-1 then
    begin
      ANode.ImageIndex:=FImgIndex;
      ANode.SelectedIndex:=FImgIndex;
      if Assigned(StateImages) then
        ANode.StateIndex:=FUnCheckIndex;
    end;
    ANode.Data := APoint;
  end;
begin
  Items.Clear;
  FList.Clear;
  LoadRec:=false;

  ADataSet.Open;
  try
    while not ADataSet.Eof do
    begin
      { 如果RootID存在的话,先将数据指针移动到适当的记录 }
      if (FRootID<>-1) and (not LoadRec) and (ADataSet.FieldByName(FKeyField).AsInteger<>FRootID) then
      begin
        ADataSet.Next;
        Continue;
      end
      else
        LoadRec:=true;

      AddAItem(ADataSet.FieldByName(FDisplayField).AsString,
               ADataSet.FieldByName(FParentField).AsInteger,
               ADataSet.FieldByName(FKeyField).AsInteger);
      ADataSet.Next;
    end;
  finally
    ADataSet.Close;
  end;

  if assigned(Items[0]) then
    Items[0].Expand(false);
end;


end.
 
谢谢:autumn
不过好象太复杂了些。下来好好看看

 
下面是一段代码供参考

var Node1,Node2:ttreeNode
begin
Node1:= treeview1.AddChild(nil,'分公司一');
Node2:= treeview1.AddChild(node1,'厂部1');
treeview1.AddChild(node2,'办公室1');
treeview1.AddChild(node2,'人事科1');

Node1:= treeview1.AddChild(nil,'分公司二');
Node2:= treeview1.AddChild(node1,'厂部2');
treeview1.AddChild(node2,'办公室2');
treeview1.AddChild(node2,'人事科2');
end;

再按你的业务逻辑从数据库取出你的数据,多用几次循环。
如果有多层,可以写成递归过程。
如果每个节点还包含信息,你还可以改用addchildobject过程。




 
我是按照2差树的方法建立数据库,然后方在treeview中的.是用广度搜索.
 
看到飘摇客大侠的一段源码也许能解决我的问题。
作者:飘摇客 发表日期:2002-7-12 14:15:19 阅读数:783

作者主页:http://wolfsoft.nugoo.com



procedure CreateSubTree(FNodeName: string; Node: TTreeNode = nil);
var
mLocalName: string;
TreeNode: TTreeNode;
Ads_Tmp: TADODataSet;
begin
ADS_Tmp := TADODataSet.Create(Self);
ADS_Tmp.Connection := ADOConn;
with ADS_Tmp do
begin
Close;
CommandText := 'Select * from Type Where ParentID =' + FNodeName;
Open;
First;
while not Eof do
begin
mLocalName := FieldbyName('ID').Asstring;
TreeNode := TreeView.Items.AddChild(Node, FieldByName('Name').AsString);
CreateSubTree(mLocalName, TreeNode);
Next;
end;
end;
end;

但有几个问题不懂:
1,这段程序中的ID是否相当于我表中的ID
PARENTID是否相当于我表中的PID
2‘‘CommandText := 'Select * from Type Where ParentID =' + FNodeName;
fondename是何值,在程序中怎么没有看见赋值?


我是菜鸟,请不要见笑。



 
多人接受答案了。
 
后退
顶部