程序設計風格、幾個控件、遞歸在TreeView中的使用

又來了,這次是Level 100,木哈哈,簡單總結及有些小細節需要注意的地方。
      應用程序的設計風格大致上分三種:MDI,SDI,資源管理器。所謂MDI的意思是多文檔應用程序,例如Excel,程序的特點是有父窗口,也叫容器窗口,其他窗口均包含在父窗口內,是大多數複雜應用程序的設計風格。SDI就是單文檔界面應用程序,例如Winrar,特點是隻有一個底層窗口,所有彈出窗口均以ShowDialog方式顯示,適合功能簡單,需求不高的應用程序。資源管理器,這種設計風格基本上都要使用到兩個控件-TreeView及ListView,這種設計風格很少獨立使用,多與以上兩種混合使用,尤以MDI風格居多。MDI/SDI共有的特點還有菜單欄(MenuStrip)、工具欄(ToolStrip)、狀態欄(StatusStrip)。
      MDI/SDI所用到的普通控件就那麼幾種,MenuStrip,ToolStrip,StatusStrip,ImageList,Timer等等,都是拖拽就能搞定,簡單的很,但是幾點需要注意。1.如果是MDI風格,記得將主窗體的IsMdiContainer屬性設置爲true,並new新窗口對象後指定其父窗體,且不要調用ShowDialog()方法。2.控件一般都有幾個屬性比較需要注意,一個是Dock屬性,一個是Anchor,一個是AutoSize,Dock是定義要綁定到控件的邊框,簡單的說就是控件的排列方式,Anchor是當主窗體縮放時,控件的邊緣與哪邊距離保持不變,AutoSize是設置控件是否自動調整大小以適應容器大小,比較明顯一個例子,VS2003里加個StatusBar,然後加幾個Panel,然後就開始調Panel大小,當用VS2005加個StatusStrip,加幾個StatusLabel,好了,大小調不了了,這裏就需要設置StatusLabel的AutoSize屬性,改爲False就OK了。3.除了上面三個,還要注意各個控件的Items Collection,各種Style及各種Alignment。
     我這篇文章想多寫一點TreeView及ListView,這兩種控件不是我們拖拖拽拽就搞的定的,而且TreeView與數據庫聯繫緊密,也涉及到一個很麻煩的東西-遞歸(Recursion),ListView則涉及到四種呈現方式,在某些方面,ListView還是比DataGridView優秀的,話不多說,扔一個例子。
     示例1:實現簡單的Windows資源管理器
     窗體:
  
    界面左邊是個TreeView,右邊是個ListView,因爲是簡單實現,沒有各種ico,當在左邊選擇節點時,右邊顯示相應文件夾中的內容(注意,即包含文件夾,也包含文件)。
    OK,先分析,TreeView中的內容在窗體Load的時候需要做什麼工作?賤人甲跳出來大喊:“查詢出所有節點及節點下的節點及節點下的節點下的節點……顯示到TreeView中”林大少將之一腳T飛,這是不科學的,賤人乙跳出來大喊:“老子電腦牛B,性能有保障!”林大少將之一腳T飛,這也是不科學的,我這破電腦不說了,要是你160G裏全是文件,窗體Load時查詢全部文件,那得了,數分鐘內你別幹別的了,等吧。爲了提高程序性能,最科學的是窗體生成時,產生兩級節點,爲什麼產生兩級?不產生兩級你盤符前面就沒+號。。太醜,然後應該再寫TreeView的BeforeExpand事件,當點擊節點時,繼續產生下一級的下一級節點,以此類推。
    代碼:

private void Form1_Load(object sender, EventArgs e) //窗體生成事件
{
    this.listView1.Columns.Add("Name", 250, HorizontalAlignment.Left);
    try
    {

         //獲得以本地計算機硬盤驅動器名字爲成員的string數組
         string[] drivers = Environment.GetLogicalDrives();
        
         for (int i = 0; i < drivers.Length; i++)
         {

              //以每個驅動器名爲參數構造節點
              TreeNode node = new TreeNode(drivers[i]);

              //將每個節點添加到TreeView控件中
              this.treeView1.Nodes.Add(node);

              //調用獲得該節點下子節點方法
              GetChild(node);
          }
     }
     catch //注意一定要寫Try catch,不要忘記你的光盤驅動器。。
     {
 
     }
}
private void GetChild(TreeNode node) //設置參數節點下的節點方法
{
     try
     {

         //獲得該節點絕對路徑
         string path = GetPath(node);

         //只對路徑操作,因此用Directory或者DirectoryInfo
         DirectoryInfo di = new DirectoryInfo(path);

         //獲得該路徑下的所有文件夾路徑集合
         DirectoryInfo[] directory = di.GetDirectories();
         for (int i = 0; i < directory.Length; i++)
         {

            //以該集合內所有文件夾名做爲參數構造節點
            TreeNode tn = new TreeNode(directory[i].Name);

            //將構造的節點掛到父節點上
            node.Nodes.Add(tn);
          }
     }
     catch //必須,光盤驅動器
     { 
     }
}

private string GetPath(TreeNode node) //獲得節點路徑方法
{

    //下面單獨說,遞歸方法
    if (node.Parent == null)
    {
        return node.Text;
    }
    return Path.Combine(GetPath(node.Parent), node.Text);
}

private void treeView1_BeforeExpand(object sender,TreeViewCancelEventArgs e) //展開節點事件
{

     //獲得事件源的節點
     TreeNode node = e.Node;

     //獲得該節點下的所有節點
     TreeNodeCollection tnc = node.Nodes;
     for (int i = 0; i < tnc.Count; i++)
     {

          //設置節點下的所有節點
          GetChild(tnc[i]);
     }
}

    大體過程就是這樣,除了那個遞歸方法,其他的只是注意下寫法,還有展開節點事件中,不要取得事件源的節點就把它當參數去調GetChild方法,仔細考慮考慮應該知道爲什麼。
    遞歸是個比較難理解的方法,很容易被繞進去,我舉個簡單的例子,我們可以大概看一下遞歸是個什麼流程。
    示例2:用遞歸做1到10的遞加。
    代碼:

private int getNum(int i)
{
     if (i == 10)
     {
         return 10;
     }
     return i + getNum(++i);
}

int total=getNum(1);

    OK就這麼幾行,你光死盯着看絕對要被繞進去,你加斷點一步一步走都不一定看的明白,我建議還是拿筆寫寫,當傳個1進去,程序怎麼走的?好,程序走到i+getNum(++i),寫出來就是1+getNum(2),OK,調用自身,2進來了運行到i+getNum(++i),變成了1+2+getNum(3)了,依次類推,最後變成1+2+3+...+9+getNum(10),當10進來的時候,符合條件,return一個10,表達式於是變成1+2+3+...+9+10,return了回去,於是就實現了1到10的累加,需要注意的是如果需要使用遞歸算法,需要指定Break條件,不然跟死循環沒什麼區別,OK,反過來看示例1的遞歸取得節點的絕對路徑方法:

private string GetPath(TreeNode node)
{
    if (node.Parent == null)
    {
        return node.Text;
    }
    return Path.Combine(GetPath(node.Parent), node.Text);
}

   好了這個跟剛纔我們寫的那個原理一樣,如果看不懂,還是建議拿筆寫寫看,Path.Combine是將兩個string組合生成一個路徑字符串,好了寫來看看,當node傳進來運行到Path.Combine(GetPath(node.Parent), node.Text),接着把node.Parent當參數調自身,表達式變成Path.Combine((Path.Combine(node.Parent.Parent),node.Parent.Text),node.Text),舉個實際的例子,打個比方,E:/Program Files/MSDN/MSDN8.0這個文件夾,當我們要取MSDN8.0的絕對路徑的時候,我們傳的是MSDN8.0,進了方法,Path.Combine(GetPath("MSDN"),"MSDN8.0"),好,再次調用,Path.Combine((Path.Combine(GetPath("Program Files")),"MSDN"),"MSDN8.0"),再調(別嫌麻煩。。)Path.Combine((Path.Combine(Path.Combine(GetPath("E:"),"Program Files")),"MSDN"),"MSDN8.0"),好了,再次調用的時候滿足條件了,因爲E:上面沒節點了,也就是GetPath("E:")將返回node.Text,也就是返回"E:",那最裏層的Combine方法將返回"E:/Program Files",往外走,倒數第二層返回"E:/Program Files/MSDN",繼續走,最後一層返回"E:/Program Files/MSDN/MSDN8.0/",這就是這個遞歸程序路線的大致走向,因此你可以看出,要靈活的運用遞歸是有一定難度的,除了需要大量的積累,還需要有點感覺。

    最後ListView裏顯示節點內文件、文件夾代碼:

private void getContent(TreeNode node)

{
     try
     {

         //獲得節點路徑
         sting path = GetPath(node);
         DirectoryInfo di = new DirectoryInfo(path);
         FileInfo[] fi = di.GetFiles();
         DirectoryInfo[] directory=di.GetDirectories();
         for (int i = 0; i < fi.Length; i++)
         {
              ListViewItem lvi = new ListViewItem();
              lvi.SubItems[0].Text = fi[i].Name;
              this.listView1.Items.Add(lvi);
         }
         for (int i = 0; i < directory.Length; i++)
         {
              ListViewItem lvi = new ListViewItem();
              lvi.SubItems[0].Text = directory[i].Name;
              this.listView1.Items.Add(lvi);
         }
      }
      catch
      {

      }
}

private void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
     TreeNode node = e.Node;
     if (node != null)
     {
          this.listView1.Clear();
          getContent(node);
     }

}

 

    又跟HTML代碼較上勁了,寫註釋老是出問題,不寫了,只需要注意幾個地方就行了,System.IO.Directory或者System.IO.DirectoryInfo都是目錄級別操作的類,而File或者FileInfo都是文件級別操作的類,每種兩個,Directory及File類主要是工具類,其中的方法都是靜態方法,如果沒有特殊需要可以使用這兩個類,剩下兩個都是有實例方法的類,與前面兩個大體相同,少許的不同,比如FileInfo及DirectoryInfo都有Name屬性,可以直接獲得文件或者文件路徑的文件名,看需求情況,另外ListView介紹的比較少,主要注意下該控件的四種文件呈現方式及ListViewItem第一列的賦值方法就可以了,其他的與ListBox用法差不多。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章