最新回應

這功能摸了很久,
主要是要針對上面交代下來的功能去做客製化,
弄到後面真的有一把鼻涕一把眼淚的感覺...
這邊只是做個紀錄,小弟功力火侯還不到家,
希望路過的各位高手多多指教。

 

在開始之前先說明一下客製化的需求:
1. 元件化
2. 自定義的節點圖示
3. 節點根據資料庫的資料產生
4. 根據網址帶的參數去展開樹狀圖
5. 節點要可以帶連結 

 

在撰寫的過程中,
最麻煩就是要根據資料的主從結構來定義父節點子節點之間的關係,
在我的 CASE 裡面,是這樣定義的:

父 節 點:1.1
子節點1:1.1.1
子節點2:1.1.2
子節點3:1.1.3 

 

不知道各位看出來了沒有,
簡單的說就是用 " . " 來區分上、下層結點,
而父節點 1.1 又隸屬在節點 1 之下, 整體架構如下圖:

在我的資料庫中,
我用一個欄位來記錄這樣的關係:

 

有一點要提醒,就是最上層的根目錄是另外放在一個資料表,暫且統稱資料表ROOT好了,
至於其他節點又會放在另一個資料表,統稱資料表NODE
而在網頁上,我會將這些這需參數放在網址,(例如:test.aspx?id=23)
讓程式在頁面載入可以去抓這些資訊:

01 void Page_Load(object sender, EventArgs e)
02     {
03         if (Request.QueryString["id"] == string.Empty)
04         {
05             Message.Text = "網址參數有誤";
06             TreeView1.Visible = false;
07         }

08         else
09         {
10             queryString = Request.QueryString["id"].Split('.'); //分割網址參數
11             //
12             if (!Page.IsPostBack)
13             {
14                 addService(); //新增父節點
15             }

16         }

17     }

 

 

新增父節點程式:
(資料來源是資料表ROOT)

01 void addService()
02     {
03         string nodeName = string.Empty;        
04         string nodeLink = "";
05         //
06         string QryString = "SELECT * FROM rootDB";
07         //        
08         SqlConn.Open();
09         SqlCmd = new SqlCommand(QryString, SqlConn);
10         DataReader = SqlCmd.ExecuteReader();
11         while (DataReader.Read())
12         {            
13             nodeName = DataReader["sevName"].ToString();            
14             nodeLink = DataReader["sevLink"].ToString();
15         }

16         DataReader.Close();
17         SqlConn.Close();
18         //      
19         if (nodeName == string.Empty || nodeLink == string.Empty)
20         {
21             Message.Text = "找不到服務";
22             TreeView1.Visible = false;
23         }

24         else
25         {
26             if (chkQueryString(Request.QueryString["id"]))
27             {
28                 TreeView1.Nodes.Clear();
29                 //          
30                 TreeNode MyNode = new TreeNode();
31                 MyNode.Expanded = false; //節點是否展開
32                 MyNode.Text = nodeName; //節點文字
33                 MyNode.Value = GetServName(Request.QueryString["id"]); //節點值
34                 MyNode.NavigateUrl = nodeLink; //目標連結
35                 MyNode.Target = "_self"; //連結開啟方式
36                 //  
37                 if (chkChildNodes(MyNode)) //如果有子節點才會有展開標籤
38                 {
39                     MyNode.PopulateOnDemand = true;
40                     doExpand(MyNode); //根據網址參數設定是否預先展開
41                 }

42                 else
43                 {
44                     MyNode.PopulateOnDemand = false;
45                 }

46                 //        
47                 TreeView1.Nodes.Add(MyNode);
48             }

49             else
50             {
51                 Message.Text = "參數錯誤";
52                 TreeView1.Visible = false;
53             }

54         }

55     }

前半段到17行的部分用途在從資料表ROOT中取得服務的名稱,
第二階段從26行開始首先檢查網址的參數是否合法(存在於資料庫中),
若合法,則進行根目錄的初始化(根目錄自己本身也算一個節點喔)以及雜項設定。(28~35行)

 

但是截至上述步驟,也只能確認在資料表ROOT確實有這個服務存在,
還不能確定在該服務之下有沒有其他節點,
因此從37行開始啟動一系列的檢查步驟,
首先將剛剛初始化的根目錄節點MyNode丟到副程式chkChildNodes裡面檢查有沒有其他的子節點,
如果有,就要將MyNode的PopulateOnDemand屬性設定為True,
這個環節相當重要,
因為PopulateOnDemand就是決定樹狀圖是否動態載入節點的關鍵。

 

40行的地方是決定樹狀圖要展開到哪一層。(根據網址參數)
最後,47行的地方,無論有沒有子節點,最後都要把根目錄塞到樹狀圖中。

 

檢查子節點副程式↓:
(資料來源是資料表NODE)

01 bool chkChildNodes(TreeNode theNode)
02     {
03         int Count = 0;
04         bool MyOutput = false;
05         //
06         string chkCmd = "SELECT COUNT(*) FROM nodeDB";
07         SqlConn.Open();
08         SqlCmd = new SqlCommand(chkCmd, SqlConn);
09         Count = (int)SqlCmd.ExecuteScalar();
10         SqlConn.Close();
11         //
12         if (Count == 0)
13         { }
14         else
15         {
16             MyOutput = true;
17         }

18         //
19         return MyOutput;
20     }

 

決定根目錄是否展開的副程式↓:

01 void doExpand(TreeNode _node)
02     {
03         if (Request.QueryString["id"] == GetServName(Request.QueryString["id"]))
04         {
05             _node.Collapse(); //網址參數與根目錄相同→收合
06         }

07         else
08         {
09             _node.Expand();  
10         }

11     }


 

 


以上呢...是DEMO根目錄的產生,
接下來才要開始子節點的製造過程,
至於為啥要這麼麻煩...只能說~老闆最大~....

 

進入這個階段之前呢,
先貼一下樹狀圖的 HTML CODE 好了:

1 <asp:TreeView ID="TreeView1" runat="server"  
2         EnableClientScript="False"
3         NodeWrap="true"  
4         OnTreeNodePopulate="PopulateNode">

5 </asp:TreeView>
6 <asp:Label ID="Message" runat="server" ></asp:Label>

 

為啥要先貼呢?
因為這裡面包含一個很重要的部分,
就是 OnTreeNodePopulate 屬性。
接下來會有點雜...要了解這屬性之前,先回頭看看上面 PopulateOnDemand 的定義,



很麻煩?
好吧...一起解說比較方便,
所謂的 PopulateOnDemand 就是設定樹狀圖是否動態載入節點,
若要動態填入節點,須先將節點的 PopulateOnDemand 屬性設為 true,然後定義 OnTreeNodePopulate 事件的處理方法,以程式的方式填入節點。

 

以上是改寫自 MSDN,
我用一個比較直覺的方式解說,
一般人在開啟檔案總管時,要是看到資料夾旁邊有個 [+] 的圖示,就知道裡面還有東西,
PopulateOnDemand 就是在控制這個符號的產生!
但是並不包含去顯示裡面的檔案!
要顯示裡面的檔案,就必須設定 OnTreeNodePopulate 的程式,用該程式來填入裡面的檔案(節點)。

 

所以這也是為何在產生根目錄的時候,
要先確定底下還有東西,才能把 PopulateOnDemand 設定為 True,
因為沒有設定好會有兩個後果:
1. 每個都設定為 True:即使父節點底下沒有任何子節點也會看到 [+] 的符號,
而且連最底層的節點也會有相同情形,必須一一點擊 [+] 符號才會消失。

2. 每個都設定為 False:永遠看不到父節點底下是否有子節點!

 

好啦...以上都了解之後,
接下來要說的是:當使用者點擊 [+] 之後,是如何產生子節點的?
這部分是藉由 TreeNodeEventArgs 產生的參數 e 傳入 OnTreeNodePopulate
 的副程式中:

1 void PopulateNode(Object sender, TreeNodeEventArgs e)
2     {
3         e.Node.ChildNodes.Clear();
4         PopulateProducts(e.Node);        
5     }

 

 

01 void PopulateProducts(TreeNode node)
02     {
03         DataSet ResultSet = RunQuery("SELECT * FROM nodeDB");
04         //
05         if (ResultSet.Tables.Count > 0)
06         {
07             foreach (DataRow row in ResultSet.Tables[0].Rows)
08             {
09                 // 建立新節點  
10                 //節點參數:(文字, 值, 節點圖片URL, 目標連結URL, 連結開啟方式)                
11                 TreeNode NewNode = new TreeNode(row["sevName"].ToString(), row["sevValue"].ToString(), "", row["serLink"].ToString(), "_self");
12                 //
13                 // 動態設定 PopulateOnDemand 值,
14                 // 在此節點底下仍有子節點時,才會產生展開符號                
15                 if (chkChildNodes(NewNode))
16                 {
17                     NewNode.PopulateOnDemand = true;
18                     //
19                     if (doChildNodeExpand(NewNode, queryString.Length)) //根據網址參數決定要展開的層數
20                     {
21                         NewNode.Expand(); //節點的位置在網址參數的路徑上
22                     }

23                     else
24                     {
25                         NewNode.Collapse();
26                     }

27                 }

28                 else
29                 {
30                     NewNode.PopulateOnDemand = false;
31                 }

32                 //
33                 NewNode.SelectAction = TreeNodeSelectAction.Expand;  // 設定點選節點時展開
34                 node.ChildNodes.Add(NewNode);    
35             }

36         }

37     }

分成兩段,
第一段是先清除所有節點,
第二段跟之前很類似,
首先要去資料庫找到其他節點填入,(3~11行)
之後根據該節點底下是否有其他節點決定 PopulateOnDemand 的值,(15, 17行)
至於要根據網址參數展開節點的副程式 doChildNodeExpand ,在這邊就先保留啦,
畢竟那是小弟的 CASE,對其他人來說也許不是很適用,
不過上面說的要是都有通...那樹狀圖也差不多會個七八成了,
我的方法比較粗淺而且大多都是由 MSDN 改寫的,
有問題歡迎指教。



DotBlogs Tags: C# TreeView

關連文章

回應

  • topcat 2009/8/10 下午 05:25 回覆

    # re: [C#]樹狀圖(TreeView)的用法

    小喵分享一下小喵的方式...大家互相研究一下

    http://www.dotblogs.com.tw/topcat/archive/2008/03/05/1234.aspx

    另外拖拉放的方式去調整TreeView的結構...可以參考這一篇

    http://www.dotblogs.com.tw/topcat/archive/2009/08/06/9901.aspx

    ^_^

  • willy0080 2009/8/11 上午 08:48 回覆

    # re: [C#]樹狀圖(TreeView)的用法

    小喵大您好:

    其實在開始研究樹狀圖之前,

    您的兩篇文章小弟都已經拜讀過了,

    只是我還是個新手,

    加上對VB不熟悉,因此才發這篇小技巧文,

    有錯誤的話還請您多多指教~^^"

  • topcat 2009/8/11 下午 02:14 回覆

    # re: [C#]樹狀圖(TreeView)的用法

    您客氣了

    結構上建議您參考小喵的NodeId,ParentId這樣的結構

    這樣在異動Tree的結構時,需要更改的部分只有ParentId而已

    搭配遞迴展開樹,就能有無限層、易維護的效果

    ^_^

  • 路人甲 2009/8/12 上午 10:09 回覆

    # re: [C#]樹狀圖(TreeView)的用法

    你目前設計的 Tree在 1.1下就是子節點(樹葉,如 1.1.1)
    ,ㄡ想問的是1.1下是否能建立一個分支點(像1.2一樣),
    在這分支點下,才是子節點(樹葉,如 1.1.1)?
    如果可以,能否提供建製的程式碼或想法? :)-

    感謝ㄋ的幫忙!

  • willy0080 2009/8/12 下午 01:06 回覆

    # re: [C#]樹狀圖(TreeView)的用法

    to 路人甲 :

    當然可以啊~當初結構會這樣定義就是要無限制的擴充下去~

    我記得圖片上面也有畫1.2吧?

    至於怎麼建置...老實說沒有捷徑,

    要做一堆字串處理...

  • 路人甲 2009/8/13 上午 09:29 回覆

    # re: [C#]樹狀圖(TreeView)的用法

    謝謝ㄋ的回答!

    >我記得圖片上面也有畫1.2吧?
    我的意思是 1.1與 1.2不在同一層(Level 1),而是
    Level 0 只有1,Level 1只有 1.1,Level 2只有 1.2即 1.1展開後會看到 1.2,而在 1.2展開後才會看到 1.1.1 ...

    在 addService()函數中第28行 TreeView1.Nodes.Add(MyNode); 會讓 1.1與 1.2在同一層(Level 1)

    如果改用PopulateProducts函數中第34行 node.ChildNodes.Add(NewNode); 會讓 1.2變成跟 1.1.1一樣。

    不知道你是否有好方法可以解決? :)-

    再次感謝ㄋ的幫忙!

  • willy0080 2009/8/13 上午 10:59 回覆

    # re: [C#]樹狀圖(TreeView)的用法

    to 路人甲 :

    ㄜ...先撇開技術問題不說...

    假如你是要把 1.2 納入 1.1 底下,

    這樣就違背了當初創建這架構的原意...

    因為 1.2 不僅僅只是一個代號,

    它還說明了目前的節點的位置以及隸屬關係,

    以我的作法,要把 1.2 放到 1.1 底下技術上是做得到,

    但是 1.2 不會再叫做 1.2 (也許是 1.1.2)

    而且底下節點名稱也要全部更動...

    你可以參考小喵大那篇用拖曳的方法去變更節點的位置...

    應該會對你比較有幫助~^^"

  • 路人甲 2009/8/13 下午 01:28 回覆

    # re: [C#]樹狀圖(TreeView)的用法

    >以我的作法,要把 1.2 放到 1.1 底下技術上是做得到,
    >但是 1.2 不會再叫做 1.2 (也許是 1.1.2)
    我只是用 1.2來當例子,這樣會比較容易了解,我也知道技術上做的到,但就是不知如何撰寫? XDDD

    〔小喵大那篇用拖曳的方法去變更節點的位置〕這篇文章,我也看過,但還是遇到在〔路人甲 2009/8/13 上午 09:29 〕回覆中所提到的問題。

    不論如何都感謝你的幫忙!

  • willy0080 2009/8/13 下午 03:04 回覆

    # re: [C#]樹狀圖(TreeView)的用法

    to 路人甲 :

    我本來以為我已經了解你的問題了...

    先請教一下,

    你的樹狀圖父子節點是怎麼定義的?

    就像你講的,技術上是OK,

    但是移動某個節點對整個架構的變動是非比尋常,

    你可以把你的情形想成是在操作檔案總管,

    假設在 D:\ 之下有兩個資料夾 1.1 和1.2,

    這兩個資料夾裡面各有10個檔案,

    依你說的操作方式,就好像要把 1.2 搬到 1.1 裡面,

    而且還要把原本 1.1 底下的檔案再搬到 1.2 中,

    你想想看,你要是實際操作的話,至少也要兩個步驟,

    寫程式怎可能同一時間處理完呢?

    ==================================

    建議你,

    1. 先將動作拆解比較好處理,

    例如剛剛的動作其實就是兩次「剪下-貼上」

    在程式撰寫方面,可以考慮個別寫出副程式,

    這樣在呼叫時比較不會混淆。

    2. 還是要先請你重新思考一下你的命名架構,

    雖然我寫出來沒有多加敘述,

    但其實那樣的架構是我跟同事花了快一個月不斷修正才得到的結果,

    如果你連自己的命名架構都還沒定義的話...

    建議先重新思考一下這樣的架構是否適合你?

     

  • 路人甲 2009/8/13 下午 04:20 回覆

    # re: [C#]樹狀圖(TreeView)的用法

    因為ㄡ建立的 Tree是固定不會變,所以ㄡ撰寫一行又一行的程式來建立節點,而你是透過資料庫來建立 Tree,目前建立的結果跟你在文章中所提到的整體架構一樣。

    因為不想透過 SiteMap或寫節點在<asp:TreeView>與</asp:TreeView>之間,才在研究程式如何寫? :)-

  • willy0080 2009/8/13 下午 04:27 回覆

    # re: [C#]樹狀圖(TreeView)的用法

    可以把你的程式碼寄給我嗎?

    看了之後應該會比較清楚...

    SiteMap 老實說我也不會用...

  • 路人甲 2009/8/13 下午 04:53 回覆

    # re: [C#]樹狀圖(TreeView)的用法

    程式如下:
    Dim temp_tree_node, temp_child_tree_node As TreeNode

    '建立1.1節點
    temp_tree_node = New TreeNode
    temp_tree_node.Text = "1.1"
    temp_tree_node.Value = 0
    TreeView1.Nodes.Add(temp_tree_node)

    '建立子節點
    temp_child_tree_node = New TreeNode
    temp_child_tree_node.Text = "1.1.1"
    temp_child_tree_node.Value = 1
    temp_child_tree_node.NavigateUrl = "XXX1.HTM"
    temp_tree_node.ChildNodes.Add(temp_child_tree_node)
    temp_child_tree_node = Nothing

    temp_child_tree_node = New TreeNode
    temp_child_tree_node.Text = "1.1.2"
    temp_child_tree_node.Value = 2
    temp_child_tree_node.NavigateUrl = "XXX2.aspx"
    temp_tree_node.ChildNodes.Add(temp_child_tree_node)
    temp_child_tree_node = Nothing

    '建立1.2節點
    temp_tree_node = Nothing
    temp_tree_node = New TreeNode
    temp_tree_node.Text = "1.2"
    temp_tree_node.Value = 3
    TreeView1.Nodes.Add(temp_tree_node)

    temp_child_tree_node = New TreeNode
    temp_child_tree_node.Text = "1.2.1"
    temp_child_tree_node.Value = 4
    temp_child_tree_node.NavigateUrl = "XXX4.aspx"
    temp_tree_node.ChildNodes.Add(temp_child_tree_node)
    temp_child_tree_node = Nothing

    temp_child_tree_node = New TreeNode
    temp_child_tree_node.Text = "1.2.2"
    temp_child_tree_node.Value = 5
    temp_child_tree_node.NavigateUrl = "XXX5.aspx"
    temp_tree_node.ChildNodes.Add(temp_child_tree_node)
    temp_child_tree_node = Nothing

  • willy0080 2009/8/13 下午 05:12 回覆

    # re: [C#]樹狀圖(TreeView)的用法

    to 路人甲 :

    假如你只是要單純把 1.2 塞到 1.1 底下,

    '建立1.2節點---->那段最後一行改成:

    node1_1.ChildNodes.Add(node1_2);

    類似這樣的寫法,

    不過你的名稱都一樣,

    可能要稍微區分一下。

     

  • 路人甲 2009/8/14 上午 09:55 回覆

    # re: [C#]樹狀圖(TreeView)的用法

    你提到方法,以前ㄡ就有試過,但因為在1.2下沒有建立子節點,所以1.2顯示的樣式跟1.2.1一樣(當成子節點、樹葉)。

    今天早上看到你的回覆,靈機一動,在1.2下建立1.2.1子節點後,1.2顯示的樣式跟1.1一樣,達到目的。 XDDDDD

    謝謝你的幫忙!

  • 小胖子 2010/8/30 下午 04:30 回覆

    # re: [C#]樹狀圖(TreeView)的用法

    請問可以公開資料欄位及型態嗎?

  • bb 2010/9/2 下午 01:04 回覆

    # re: [C#]樹狀圖(TreeView)的用法

    請問可以提供一下 資料欄位嗎

  • Willy 2010/9/2 下午 04:22 回覆

    # re: [C#]樹狀圖(TreeView)的用法

    不太方便,我想文章裡面應該說明的很清楚了

  • Willy 2010/9/2 下午 04:24 回覆

    # re: [C#]樹狀圖(TreeView)的用法

    to bb :
    如果有啥不懂得可以提出來,我會盡量解答

  • topcat 2010/9/2 下午 07:28 回覆

    # re: [C#]樹狀圖(TreeView)的用法

     to bb : 

    小喵的這一篇有資料庫的欄位設計方式,您可以參考看看!!

    http://www.dotblogs.com.tw/topcat/archive/2008/03/05/1234.aspx

    如果您擔心的是C#與VB.NET的差異,網路上有很多互轉的資源可以處理


*標 題:

*姓 名:

 電子郵件: (將不會被顯示)

 個人網頁:

*回應

登入後使用進階評論

Please add 5 and 5 and type the answer here: