一、樹的簡介
Developer 6.0以上版本提供了hierarchy tree(層次樹)的概念,htree控件非常方便,只需要少量的編程即可實現顯示層次結構的目的。
樹的特有屬性中如下幾個較為重要:
l 多項選擇(Multi-Selection):是否答應一次選中樹的多個節點。假如不答應,那么 選中第二個節點時,第一個被選中的節點會取消選擇。
l 記錄組(Record Group):指定生成樹的記錄組的名字。
簡單介紹一下跟樹相關的觸發子(Built-in):
l FUNCTION GET_TREE_NODE_PROPERTY (item_name VARCHAR2, node NODE, property NUMBER);
功能:取得樹節點的屬性
其中property有如下幾種:
NODE_STATE:EXPANDED_NODE(擴展節點)
COLLAPSED_NODE(收縮節點)
LEAF_NODE(葉節點)--注:不能展開或收縮
NODE_DEPTH:既節點在樹中的層級。
NODE_LABEL:節點的顯示文本
NODE_ICON:節點的圖標
NODE_VALUE:節點的值。
例子:
DECLARE
htree ITEM;
node_value VARCHAR2(100);
BEGIN
-- 得到樹
htree := Find_Item('tree_block.htree3');
-- 得到當前選中節點的值
node_value := Ftree.Get_Tree_Node_Property(htree, :SYSTEM.TRIGGER_NODE, Ftree.NODE_VALUE);
...
END;
注釋:其中: SYSTEM.TRIGGER_NODE指當前選中的樹節點。
l FUNCTION GET_TREE_PROPERTY (item_name VARCHAR2,property NUMBER);
功能:取得樹的屬性
其中property有如下幾種:
DATASOURCE
RECORD_GROUP
QUERY_TEXT
NODE_COUNT:返回樹中節點的個數。
SELECTION_COUNT
ALLOW_EMPTY_BRANCHES
ALLOW_MULTI-SELECT
l PROCEDURE SET_TREE_NODE_PROPERTY (item_name VARCHAR2,node FTREE.NODE,property NUMBER,value VARCHAR2);
功能:設置樹節點的屬性
l PROCEDURE SET_TREE_PROPERTY (item_name VARCHAR2,property NUMBER, value VARCHAR2);
PROCEDURE SET_TREE_PROPERTY (item_name VARCHAR2,property NUMBER, value RECORDGROUP);
功能:設置樹的屬性
l PROCEDURE POPULATE_TREE (item_name VARCHAR2);
功能:清空樹中已有數據,并根據記錄組或數據查詢重新生成樹。
l PROCEDURE ADD_TREE_DATA (item_name VARCHAR2,node FTREE.NODE, offset_type NUMBER,offset NUMBER,data_source NUMBER,data VARCHAR2);
功能:在指定節點下添加樹中數據
注:使用比較麻煩。
l FUNCTION FIND_TREE_NODE(item_name VARCHAR2,earch_string VARCHAR2, search_type NUMBER,search_by NUMBER,search_root NODE,start_point NODE);
功能:找到顯示文本或值符合search_string的節點。
參數:
search_type:FIND_NEXT
FIND_NEXT_CHILD
Search_by:NODE_LABEL
NODE_VALUE
Search_root:查詢的根節點,一般是Ftree.ROOT_NODE
Start_point:查找的開始節點,一般是Ftree.ROOT_NODE
l FUNCTION ADD_TREE_NODE(item_name VARCHAR2,node FTREE.NODE, offset_type NUMBER,offset NUMBER,state NUMBER,label VARCHAR2, icon VARCHAR2,value VARCHAR2);
功能:添加樹節點。
Offset_type:指定節點的分支類型,PARENT_OFFSET和SIBLING_OFFSET
Offset:指定新節點的位置,
PARENT_OFFSET:1..N
LAST_CHILD
SIBLING_OFFSET:NEXT_NODE
PREVIOUS_NODE
State:EXPANDED_NODE(擴展節點)
COLLAPSED_NODE(收縮節點)
LEAF_NODE(葉節點)
l PROCEDURE DELETE_TREE_NODE(item_name VARCHAR2,node NODE);
功能:刪除樹節點
l FUNCTION GET_TREE_NODE_PARENT(item_name VARCHAR2,node NODE);
功能:得到指定節點的父節點。
l FUNCTION GET_TREE_SELECTION(item_name VARCHAR2,selection NUMBER);
功能:得到處于選中狀態的節點。
l PROCEDURE SET_TREE_SELECTION(item_name VARCHAR2,node NODE, selection_type NUMBER);
功能:指定單個節點的選中狀態
參數:
selection_type:SELECT_ON
SELECT_OFF
SELECT_TOGGLE
FORM運行態時有關的觸發器:
l When-Tree-Node-Activated:用戶雙擊節點或在節點選中時按[ENTER]鍵時觸發。
l When-Tree-Node-Expanded:節點展開或收縮時觸發
l When-Tree-Node-Selected:當節點選中或取消選擇時觸發
二、生成樹的方式
樹控件一般單獨放在一個控制塊中(注:不能放在數據塊中),在畫布(CANVAS)上放置樹很輕易,并且,如無必要,樹的屬性也不需要設置。
生成樹的方式有幾種:
l 運行前通過設置記錄組或數據查詢屬性來生成
l 通過ADD_TREE_DATA觸發子來實現
l 運行態,通過ADD_TREE_NODE等觸發子來實現
l 運行態,通過添加或刪除記錄組的數據元素來實現
分析:
一、 對樹直接操作
描述:Find_Tree_Node找到指定節點,Add_Tree_Node來添加其下級節點。
缺點:編程較為復雜,操作不靈活,而且易出錯。
優點:可以對添加節點等過程進行控制,實現一些非凡要求。
例子:
--dept_cur為取單位的CURSOR,emp_cur為取雇員的CURSOR
htree := Find_Item('tree_view.tree_emp');
open dept_cur;
loop
fetch dept_cur into aa;
exit when dept_cur%notfound;
del_node := Ftree.Find_Tree_Node ( htree,aa.kjmc, Ftree.FIND_NEXT, Ftree.NODE_LABEL, Ftree.ROOT_NODE, Ftree.ROOT_NODE);
-- 刪除單位節點及其子節點
IF NOT Ftree.ID_NULL(del_node) then
Ftree.Delete_Tree_Node(htree, del_node);
END IF;
end loop;
close dept_cur;
-- 根據用CURSOR取得的單位生成樹的第一層節點
open dept_cur;
loop
fetch dept_cur into aa;
exit when dept_cur%notfound;
new_node := Ftree.Add_Tree_Node(htree, Ftree.ROOT_NODE, Ftree.parent_OFFSET, Ftree.LAST_CHILD, Ftree.EXPANDED_NODE, aa.dname, '', aa.deptno);
end loop;
close dept_cur;
--根據雇員CURSOR生成樹的下層節點
open emp_cur;
loop
fetch emp_cur into bb;
exit when emp_cur%notfound;
find_node := Ftree.Find_Tree_Node(htree, bb.kjbh, Ftree.FIND_NEXT,
Ftree.NODE_value, Ftree.ROOT_node, Ftree.ROOT_NODE);
new_node := Ftree.Add_Tree_Node(htree, find_node, Ftree.parent_OFFSET, Ftree.LAST_CHILD, Ftree.EXPANDED_NODE, bb.ename, '', bb.empno);
end loop;
close emp_cur;
-- 得到樹的根節點
ss := Ftree.get_tree_property(htree,FTREE.NODE_COUNT);
-- 循環,直到樹的所有節點都展開
for j in 1..ss LOOP
exp_node := Ftree.Find_Tree_Node(htree, '');
state := Ftree.Get_Tree_Node_Property(htree, j, Ftree.NODE_STATE);
IF state = Ftree.COLLAPSED_NODE THEN
Ftree.Set_Tree_Node_Property(htree, j, Ftree.NODE_STATE, Ftree.EXPANDED_NODE);
END IF;
END LOOP;
二、 動態記錄組
層次樹所使用記錄組的數據格式:
+ — Car
- — Airplane
— Boeing
— Boeing
初始狀態
層數
顯示文本
圖標
值
-1(收縮節點)
1
‘Car’
''
‘car’
0(葉節點)
2
'Honda'
''
'civic'
1(展開節點)
1
'Airplane'
''
'plane'
0
2
'Boeing'
''
'747'
0
2
'Boeing'
''
'757'
生成記錄組的方式又分為兩種。
1、 從查詢生成記錄組
描述:利用樹的查詢語句(connect by…prior…start with…)生成記錄組,設置樹的屬性來生成。
優點:編程簡單,方便。
缺點:只適用于可以構造出樹狀查詢語句的情況下。
例子:
v_ignore number;
rg_emps recordgroup;
begin
rg_emps := find_group('EMPS');
--假如非空,則清空數據
if not id_null(rg_emps) then
delete_group(rg_emps);
end if;
--構造記錄組
rg_emps := create_group_from_query('EMPS',
'select 1, level, ename, NULL, to_char(empno) '
'from emp '
'connect by prior empno = mgr '
'start with job = ''PRESIDENT''');
v_ignore := populate_group(rg_emps);
ftree.set_tree_property('tree_view.tree_emp', ftree.record_group, rg_emps);
end;
2、 用行列數據直接構造記錄組
描述:記錄組一般為行列結構,以循環方式直接向記錄組中添加單元數據。
優點:可直接控制記錄組的樣式。
缺點:對多層結構,編程也較為復雜。
例子:
--單位CURSOR
cursor cursor_dept is
select dname, deptno from dept order by dname;
--雇員CURSOR
cursor cursor_emp(p_dno number) is
select ename, empno from emp where deptno = p_dno order by ename;
v_i number;
v_ignore number;
rg_emps recordgroup;
rg_depts recordgroup;
v_init_state groupcolumn;
v_level groupcolumn;
v_label groupcolumn;
v_icon groupcolumn;
v_value groupcolumn;
begin
rg_depts := find_group('DEPTS');
--如有數據,則清空記錄組
if not id_null(rg_depts) then
delete_group(rg_depts);
end if;
rg_depts := create_group('DEPTS');
--這里自定義你需要的記錄組中列的數據類型和長度
--初始狀態(指展開、收縮還是葉節點)
v_init_state := add_group_column(rg_depts, 'init_state', number_column);
--所在層數
v_level := add_group_column(rg_depts, 'level', number_column);
--顯示文本
v_label := add_group_column(rg_depts, 'label', char_column, 40);
--圖標
v_icon := add_group_column(rg_depts, 'icon', char_column, 20);
--值
v_value := add_group_column(rg_depts, 'value', char_column, 5);
v_i := 1;
for deptrec in cursor_dept loop
add_group_row(rg_depts, v_i);
set_group_number_cell(v_init_state, v_i, 1);
set_group_number_cell(v_level , v_i, 1);
set_group_char_cell (v_label , v_i, deptrec.dname);
set_group_char_cell (v_icon , v_i, NULL);
set_group_char_cell (v_value , v_i, to_char(deptrec.deptno));
v_i := v_i + 1;
for emprec in cursor_emp(deptrec.deptno) loop
add_group_row(rg_depts, v_i);
set_group_number_cell(v_init_state, v_i, 1);
set_group_number_cell(v_level , v_i, 2);
set_group_char_cell (v_label , v_i, emprec.ename);
set_group_char_cell (v_icon , v_i, NULL);
set_group_char_cell (v_value , v_i, to_char(emprec.empno));
v_i := v_i + 1;
end loop;
end loop;
ftree.set_tree_property('tree_view.tree_org', ftree.record_group, rg_depts);
end;
結論:進行數據庫設計時盡量把父子結構放在一張表,這樣,使用查詢生成記錄組再生成樹的方式最簡單實用。
假如不能實現,那么直接構造記錄組也可生成樹。如無非凡要求,一般不采取對樹直接操作的方式。
附注:作者一般將生成樹的程序放在Form Builder的“程序單元”中,在需要的地方調用來實時刷新樹。
參考:Developer 6.0自帶的PL/SQL程序庫:navigate.pll
三、遺留問題
由于developer簡化了樹的設計,那么一些對樹的轉移、拷貝等操作就不太輕易實現。這個課題還需要繼續研究。