用C#制作一个目录选择对话框

作者:王凯明 本文选自:赛迪网 2002年03月19日

C#作为Microsoft.Net战略下的新兴语言,有其不可比拟的强大功能。作为一种RAD语言,它有Visual Basic快速开发应用程序的优点,又不乏C++语言的面相对象的优良特性。

目录选择对话框是应用程序中经常用到的。它能让用户选择一个系统中的特定的文件目录,在安装程序以及媒体播放器中有大量的运用。本文就从C#快速开发的特点出发,介绍在C#下如何制作应用程序中常用到的目录选择对话框。本文介绍的目录选择对话框是完全基于Windows Forms的,因此具有一些标准窗口所共有的特性。

一、系统要求

微软公司Windows 2000服务器版或Windows XP 版
.Net Framework SDK Beta 2版

二、实现方法

1.在VS.net下新建一个C#的工程,不妨取名为"FolderSelect",图示如下:

diag5_1

2.接下来,我们开始界面部分的设计:

1)先往主界面上添加一个按钮控件、两个标签控件、两个文本框控件(用于显示目录的完整路径以及目录的一些信息)。将按钮控件的Text属性设置为"浏览目录选择对话框",将两个标签控件的Text属性分别设置为"完整路径:"、"路径信息:",将第二个文本框的MultiLine属性设置为True。最终设计所得的界面如下:

diag5_2

2)现在我们的主界面已经好了,接着就是完成目录选择对话框的设计。

选择VS.net的菜单:项目'添加Windows窗体,出现如下图形:

diag5_3

选择默认即可,按"打开"。

现在开始设计目录选择对话框的界面:

往窗体上添加两个标签控件、两个按钮控件、一个文本框控件、一个图象列表(ImageList)控件和一个目录树(TreeView)控件。将两个标签控件的的Text属性分别设置为"完整路径:"、"请选择一个文件夹:";将两个按钮控件的Text属性分别设置为"选择"和"取消";编辑图象列表控件:给它的Images属性添加两个图标

diag5_4

diag5_5

分别表示文件夹关闭和打开状态;将目录树控件的ImageList属性设置为imageList1,并将它的ImageIndex和SelectedImageIndex属性分别设置为0和1。好了,最终的界面如下:

diag5_6

3.现在我们开始编写代码:

因为我们的程序中均用到了有关目录信息的类,所以必须在每个代码文件的头上添加System.IO名字空间,具体如下:

using System.IO;

1)对于主窗体部分,我们要写的代码量相对较少。因为这是一个测试用的程序,所以主窗体的作用只是用来显示用户所选择的目录的一些相关信息以表明程序能正常运行、目录选择对话框能正常工作,所以只要添加主窗体上按钮的OnClick事件函数即可:

private void button1_Click(object sender, System.EventArgs e)
{
    try
    {
        Form2 dlg = new Form2(); if ( dlg.ShowDialog() == DialogResult.OK)
        {
            DirectoryInfo info = dlg.info;
            textBox1.Text = dlg.fullPath;

            // 在文本框中添加目录信息
            string [] strArray = new string[4];

            strArray[0] = "创建时间 : "+ info.CreationTime.ToString();
            strArray[1] = "全名 : "+ info.FullName;
            strArray[2] = "上次访问时间 : "+ info.LastAccessTime.ToString();
            strArray[3] = "上次改写时间 : "+ info.LastWriteTime.ToString();

            textBox2.Lines = strArray;
        }
    }
    catch( Exception err)
    {
        Console.WriteLine(err.Message);
    }
}  

2)对于文件选择对话框,我们要完成系统驱动器及其子目录的取得并显示、设定目录路径信息以及目录相关信息等工作,所以代码量相对较大。先给该类添加两个必须的私有数据成员如下:

private static string driveLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// 用于列举驱动器盘符
private DirectoryInfo folder; // 用于保存目录信息

修改类的构造函数如下:fillTree()函数在后面介绍
public Form2()
{
    //
    // Required for Windows Form Designer support
    //
    InitializeComponent();

    //
    // TODO: Add any constructor code after InitializeComponent call
    //
    fillTree();// 该函数调用完成目录选择对话框的初始设置
}  

接着为该类添加几个有用的成员函数fillTree()getSubDirs()fixPath(),访问类型均为Private。fillTree()函数用来设置目录树的各个节点以填充到目录树控件,这样目录树中就充满了系统中各个根目录以及其所有子目录的节点。getSubDirs()函数用来获得各个目录节点可能存在的所有子节点,其中用到了递推搜索算法。fixPath()函数用来设置文本框的内容,使当前用户所选择的目录显示在文本框中。各函数如下:

private void fillTree()
{
    DirectoryInfo directory;
    string sCurPath = ""; // 重新清空
    treeView1.Nodes.Clear();

    // 将硬盘上的所有的驱动器都列举出来
    foreach (char c in driveLetters)
    {
        sCurPath = c + ":\\";
        try
        {
            // 获得该路径的目录信息
            directory = new DirectoryInfo(sCurPath);

            // 如果获得的目录信息正确,则将它添加到目录树视中
            if (directory.Exists == true)
            {
                TreeNode newNode = new TreeNode(directory.FullName);
                treeView1.Nodes.Add(newNode); // 添加新的节点到根节点
                getSubDirs(newNode);
                // 调用getSubDirs()函数,检查该驱动器上的任何存在子目录
            }
        }
        catch (Exception doh)
        {
            Console.WriteLine(doh.Message);
        }
    }
}

private void getSubDirs(TreeNode parent)
{
    DirectoryInfo directory;
    try
    {
        // 如果还没有检查过这个文件夹,则检查之
        if (parent.Nodes.Count == 0)
        {
            directory = new DirectoryInfo(parent.FullPath);
            foreach (DirectoryInfo dir in directory.GetDirectories())
            {
                // 新建一个数节点,并添加到目录树视
                TreeNode newNode = new TreeNode(dir.Name);
                parent.Nodes.Add(newNode);
            }
        }

        foreach (TreeNode node in parent.Nodes)
        {
            // 如果还没有检查过这个文件夹,则检查
            if (node.Nodes.Count == 0)
            {
                directory = new DirectoryInfo(node.FullPath);

                // 检查该目录上的任何子目录
                foreach (DirectoryInfo dir in directory.GetDirectories())
                {
                    // 新建一个数节点,并添加到目录树视
                    TreeNode newNode = new TreeNode(dir.Name);
                    node.Nodes.Add(newNode);
                }
            }
        }
    }
    catch (Exception doh)
    {
        Console.WriteLine(doh.Message);
    }
}

private string fixPath(TreeNode node)
{
    string sRet = "";
    try
    {
        sRet = node.FullPath;
        int index = sRet.IndexOf("\\\\");
        if (index > 1)
        {
            sRet = node.FullPath.Remove(index, 1);
        }
    }
    catch (Exception doh)
    {
        Console.WriteLine(doh.Message);
    }
    return sRet;
}

接着,给该类添加以下几个属性,这几个属性都是反映所选择的目录的相关信息的,它们将被主窗体的类调用:

public string name// 返回所选择的目录的名称
{
    get { return ((folder != null && folder.Exists)) ? folder.Name : null; }
}

public string fullPath// 返回所选择的目录的完整路径
{
    get { return ((folder != null && folder.Exists && treeView1.SelectedNode != null)) ? fixPath(treeView1.SelectedNode) : null; }
}

public DirectoryInfo info// 返回所选择的目录的信息
{
    get { return ((folder != null && folder.Exists)) ? folder : null; }
}

最后,还要添加目录树控件的BeforeSelect()BeforeExpand()事件函数以及两个按钮的OnClick事件函数。BeforeSelect()事件函数是用户选定目录前的一个事件函数,它完成了子目录取得、设置文本框内容、获取该目录信息等功能。同样,BeforeExpand()事件函数完成相似的功能,只不过它是在目录节点被展开前发生的。具体的函数实现如下:

private void treeView1_BeforeSelect(object sender, System.Windows.Forms.TreeViewCancelEventArgs e)
{
    getSubDirs(e.Node); // 取得选择节点的子文件夹
    textBox1.Text = fixPath(e.Node); // 更新文本框内容
    folder = new DirectoryInfo(e.Node.FullPath); // 获得它的目录信息
}

private void treeView1_BeforeExpand(object sender, System.Windows.Forms.TreeViewCancelEventArgs e)
{
    getSubDirs(e.Node); // 取得选择节点的子文件夹
    textBox1.Text = fixPath(e.Node); // 更新文本框内容
    folder = new DirectoryInfo(e.Node.FullPath); // 获得它的目录信息
}

private void button1_Click(object sender, System.EventArgs e)
// "选择"按钮的消息处理函数
{
    this.DialogResult = DialogResult.OK;
    this.Close();
}

private void button2_Click(object sender, System.EventArgs e)
// "取消"按钮的消息处理函数
{
    folder = null;
    this.Close();
}

4.到此为止,我们已经完成了所有的代码编写工作。现在可以按Ctrl+F5试试运行效果了。首先会出现下面的主窗体:

diag5_7
按下"浏览目录选择对话框"按钮后就出现下面的目录选择对话框了:

diag5_8

在目录选择对话框上选定一个目录后,我们按"选择"按钮。这样我们又回到了程序的主界面。因为该主界面是用来显示所选择的目录的一些相关信息的,其中包括了:完整路径、路径信息中的创建时间、全名、上次访问时间、上次改写时间等。图示如下:

diag5_9

这样,我们就完成了程序的测试工作了。当然这样的范例是还没有实用功能的,不过你可以试着用同样的方法开发出一个合适的目录选择对话框运用到自己的应用程序中,甚至是一个目录选择对话框组件以供后来者使用,让更多的人受益于你的成果。这样其效用就完全不同了,作为作者的我也会感到相当欣慰的。

三、总结

现在这个程序虽然只具有一般的目录选择功能,不过做完这个程序,我们不难发现用C#完成一些应用程序是非常方便的。相比以前我们在SDK、MFC下完成这些还要写很多东西,工作量是非常大而且复杂的。现在微软为我们做好了许多此类工作,我们何不乐意接受呢?所以,快加入C#的行列吧。

(责任编辑 尤北)

Contributors: FHL