程序设计风格、几个控件、递归在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用法差不多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值