Winform开发——本地磁盘目录树浏览
这学期的项目做了一堆,几乎都是用Winform(C#)写的,一个大的项目涉及到的Winform控件非常多,基本的控件几乎都要使用一遍,有用VS自带的,有用第三方的,有用自己写的。这篇说一下常用的本地磁盘目录树以及当前文件夹内容浏览“套件”的开发。
本地磁盘内容浏览“套件”涉及到两个基本的控件:TreeView和ListView,其中TreeView用来显示磁盘目录的层次关系,节点为磁盘的某个文件夹,用户可以单击节点展开文件夹浏览其子文件夹的层次关系。ListView的视图类似Windows自带的资源管理器,可以给出当前浏览目录下的文件和文件和问文件夹图标,双击后可以进入下一层目录。
第一步:搞定ListView中每个显示项目的图标
系统中的文件夹和不同扩展名的文件在资源管理器中显示的图标也不同,如果在使用ListView显示当前文件夹下所有项目的图标时都自定义添加,这显然是不现实的。那么我们有没有一种办法可以让系统根据某个文件项目的扩展名自动从系统图标库中选取相应的图标呢?免去我们找各种图标图片的麻烦。答案是肯定的,只是我们需要调用一些Win32的API。
首先申明如下:
#region 图标
public static uint SHGFI_ICON = 0x100;
public static uint SHGFI_DISPLAYNAME =
0x150;
public static uint SHGFI_TYPENAME =
0x400;
public static uint SHGFI_ATTRIBUTES =
0x800;
public static uint SHGFI_ICONLOCATION =
0x1000;
public static uint SHGFI_EXETYPE =
0x1500;
public static uint SHGFI_SYSICONINDEX =
0x4000;
public static uint SHGFI_LINKOVERLAY =
0x8000;
public static uint SHGFI_SELECTED =
0x10000;
public static uint SHGFI_LARGEICON =
0x0;
public static uint SHGFI_SMALLICON =
0x1;
public static uint SHGFI_OPENICON =
0x2;
public static uint SHGFI_SHELLICONSIZE =
0x4;
public static uint SHGFI_PIDL = 0x8;
public static uint SHGFI_USEFILEATTRIBUTES
= 0x10;
public static uint FILE_ATTRIBUTE_NORMAL
= 0x80;
public static uint LVM_FIRST = 0x1000;
public static uint LVM_SETIMAGELIST =
LVM_FIRST + 3;
public static uint LVSIL_NORMAL =
0;
public static uint LVSIL_SMALL = 1;
[DllImport("Shell32.dll")]
public static extern IntPtr SHGetFileInfo(string pszPath,
uint dwFileAttributes, ref SHFILEINFO
psfi,
int cbfileInfo, uint uFlags);
public struct SHFILEINFO
{
public IntPtr hIcon;
public int iIcon;
public int dwAttributes;
public string szDisplayName;
public string szTypeName;
}
[DllImport("User32.DLL")]
public static extern int SendMessage(IntPtr hWnd,
uint Msg, IntPtr wParam, IntPtr lParam);
public void ListViewSysImages(ListView AListView)
{
SHFILEINFO vFileInfo = new SHFILEINFO();
IntPtr vImageList = SHGetFileInfo("", 0, ref vFileInfo,
Marshal.SizeOf(vFileInfo), SHGFI_SHELLICONSIZE |
SHGFI_SYSICONINDEX | SHGFI_LARGEICON);
SendMessage(AListView.Handle,
LVM_SETIMAGELIST, (IntPtr)LVSIL_NORMAL,
vImageList);
vImageList = SHGetFileInfo("", 0, ref vFileInfo,
Marshal.SizeOf(vFileInfo), SHGFI_SHELLICONSIZE |
SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
SendMessage(AListView.Handle,
LVM_SETIMAGELIST, (IntPtr)LVSIL_SMALL,
vImageList);
}
public int FileIconIndex(string AFileName)
{
SHFILEINFO vFileInfo = new SHFILEINFO();
SHGetFileInfo(AFileName,
0, ref vFileInfo,
Marshal.SizeOf(vFileInfo), SHGFI_SYSICONINDEX);
return vFileInfo.iIcon;
}
#endregion
假设现在我们有ListView控件实例testListView,在使用这个控件之前我们对其进行初始化处理:
ListViewSysImages(testListView);
当我们需要向testListView中添加项目时,使用如下语句:
ListViewItem lvi = new ListViewItem(FileName.Substring(FileName.LastIndexOf("\\") + 1));
lvi.ImageIndex = FileIconIndex(FileName);
第二步:树形结构添加节点
使用TreeView之前我们需要找到四个基本图标:选定状态的文件夹、未选定状态的文件夹、磁盘图标、CD驱动器图标。将这四个图标添加到ImageList控件中调整好顺序。
#region 树形结构
//树形结构初始化
private void InitialTreeView(TreeNode rootNode)
{
//将驱动器字符串数组设为空
string[] drivers = null;
//检索此计算机上逻辑驱动器的名称
drivers = Directory.GetLogicalDrives();
int i = 0;
//初始化每一个逻辑驱动器
while (i < drivers.GetLength(0))
{
TreeNode newNode = new TreeNode(drivers[i].ToString().Substring(0,
2));
newNode.Tag = drivers[i];
//获得驱动器类型,设置图标
DriveInfo di = new DriveInfo(drivers[i]);
if (di.DriveType ==
DriveType.CDRom)
{
newNode.ImageIndex = 2;
newNode.SelectedImageIndex =
2;
}
else
{
newNode.ImageIndex = 1;
newNode.SelectedImageIndex =
1;
}
rootNode.Nodes.Add(newNode);
string path = drivers[i];
string[] dirs = null;
try
{
//获得指定驱动器中第一级目录的名称
dirs = Directory.GetDirectories(path);
}
catch (Exception error)
{
dirs = null;
//错误处理为空,即忽略
}
if (dirs != null)
{
//为每一个代表驱动器的根节点添加子节点
for (int j = 0; j
< dirs.Length; j++)
{
if ((File.GetAttributes(dirs[j]) & FileAttributes.Hidden) == FileAttributes.Hidden && showHiden.Value
== false)
{
continue;
}
//获得节点去掉路径的目录名
TreeNode node = new TreeNode(dirs[j].ToString().Substring(dirs[j].ToString().LastIndexOf("\\") + 1));
node.Tag = dirs[j];
//设置不选定状态下的图标
node.ImageIndex =
3;
//设置打开状态下的图标
node.SelectedImageIndex
= 4;
//添加节点
rootNode.Nodes[i].Nodes.Add(node);
}
}
//继续下一循环
i++;
}
}
//树形结构显示
private void TreeViewShow(TreeNode NodeDir)
{
try
{
string[] dirs = null;
int i = 0;
while (i < NodeDir.Nodes.Count)
{
//添加子节点
try
{
dirs = Directory.GetDirectories((string)NodeDir.Nodes[i].Tag);
}
catch
{
dirs = null;
}
if (dirs != null)
{
//删除以前节点
NodeDir.Nodes[i].Nodes.Clear();
//为驱动器根节点添加子节点
for (int j = 0; j
< dirs.Length; j++)
{
//获得节点去掉路径的目录名
TreeNode node = new TreeNode(dirs[j].ToString().Substring(dirs[j].ToString().LastIndexOf("\\") + 1));
node.Tag = dirs[j];
//设置不选定状态下的图标
node.ImageIndex =
3;
//设置打开状态图标
node.SelectedImageIndex
= 4;
//添加节点
NodeDir.Nodes[i].Nodes.Add(node);
}
}
i++;
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
//树形结构节点点击事件
private void treeView_AfterSelect(object sender, TreeViewEventArgs
e)
{
currentPath = (string)e.Node.Tag;
if (currentPath !=
null && !currentPath.EndsWith("\\"))
{
currentPath += "\\";
}
urlBox.Text = currentPath;
ListViewShow(e.Node);
}
//树形结构节点展开事件
private void treeView_AfterExpand(object sender, TreeViewEventArgs
e)
{
if (e.Node.Parent !=
null)
{
TreeViewShow(e.Node);
}
}
#endregion
第三步:显示当前路径下的所有文件项目
#region 文件浏览
//初始化ListView控件,把TrreView控件中的数据添加进来
private void ListViewShow(TreeNode NodeDir)
{
int itemNum = 0;//统计项目总数
listView1.Clear();
listView1.Columns.Add("名称", 150);
listView1.Columns.Add("修改日期", 150);
listView1.Columns.Add("大小", 100, HorizontalAlignment.Right);
try
{
if (NodeDir.Parent ==
null)// 如果当前TreeView的父结点为空,就把我的电脑下的分区名称添加进来
{
foreach (string DrvName in Directory.GetLogicalDrives())//获得硬盘分区名
{
FileInfo fileInfo = new FileInfo(DrvName.Substring(0,
2));
ListViewItem ItemList = new ListViewItem(DrvName.Substring(0,
2));
ItemList.SubItems.Add(fileInfo.LastWriteTime.ToString());
ItemList.ImageIndex =
FileIconIndex(DrvName);
listView1.Items.Add(ItemList);//添加进来
itemNum++;
}
}
else//如果当前父节点不为空,把点击的结点,做为一个目录文件的总结点
{
foreach (string DirName in Directory.GetDirectories((string)NodeDir.Tag))//遍历当前分区或文件夹下所有目录
{
if ((File.GetAttributes(DirName) & FileAttributes.Hidden) == FileAttributes.Hidden && showHiden.Value
== false)
{
continue;
}
FileInfo fileInfo = new FileInfo(DirName);
ListViewItem ItemList = new ListViewItem(DirName.Substring(DirName.LastIndexOf("\\") + 1));
ItemList.SubItems.Add(fileInfo.LastWriteTime.ToString());
ItemList.ImageIndex =
FileIconIndex(DirName);
ItemList.Tag =
DirName;
listView1.Items.Add(ItemList);
itemNum++;
}
foreach (string FileName in Directory.GetFiles((string)NodeDir.Tag))//遍历当前分区或文件夹所有目录的文件
{
if ((File.GetAttributes(FileName) & FileAttributes.Hidden) == FileAttributes.Hidden && showHiden.Value
== false)
{
continue;
}
FileInfo fileInfo = new FileInfo(FileName);
ListViewItem ItemList = new ListViewItem(FileName.Substring(FileName.LastIndexOf("\\") + 1));
ItemList.SubItems.Add(fileInfo.LastWriteTime.ToString());
long length = fileInfo.Length
/ 1024;
if (length == 0)
length = 1;
ItemList.SubItems.Add(length
+ " KB");
ItemList.ImageIndex =
FileIconIndex(FileName);
ItemList.Tag =
FileName;
listView1.Items.Add(ItemList);
itemNum++;
}//
}
}
catch { }
itemNumber.Text = itemNum.ToString();
}
//获取当有文件夹内的文件和目录
public void ListViewShow(string DirFileName)
{
int itemNum = 0;
listView1.Clear();
listView1.Columns.Add("名称", 150);
listView1.Columns.Add("修改日期", 150);
listView1.Columns.Add("大小", 100, HorizontalAlignment.Right);
try
{
foreach (string DirName in Directory.GetDirectories(DirFileName))//遍历当前分区或文件夹所有目录
{
if ((File.GetAttributes(DirName) & FileAttributes.Hidden) == FileAttributes.Hidden && showHiden.Value
== false)
{
continue;
}
FileInfo fileInfo = new FileInfo(DirName);
ListViewItem ItemList = new ListViewItem(DirName.Substring(DirName.LastIndexOf("\\") + 1));
ItemList.SubItems.Add(fileInfo.LastWriteTime.ToString());
ItemList.ImageIndex =
FileIconIndex(DirName);
ItemList.Tag = DirName;
listView1.Items.Add(ItemList);
itemNum++;
}
foreach (string FileName in Directory.GetFiles(DirFileName))//编历当前分区或文件夹所有目录的文件
{
if ((File.GetAttributes(FileName) & FileAttributes.Hidden) == FileAttributes.Hidden && showHiden.Value
== false)
{
continue;
}
FileInfo fileInfo = new FileInfo(FileName);
ListViewItem ItemList = new ListViewItem(FileName.Substring(FileName.LastIndexOf("\\") + 1));
ItemList.SubItems.Add(fileInfo.LastWriteTime.ToString());
long length = fileInfo.Length
/ 1024;
if (length == 0)
length = 1;
ItemList.SubItems.Add(length
+ " KB");
ItemList.ImageIndex =
FileIconIndex(FileName);
ItemList.Tag = FileName;
listView1.Items.Add(ItemList);
itemNum++;
}//
}
catch
{ }
itemNumber.Text = itemNum.ToString();
}
//列表框鼠标双击事件
private void listView1_DoubleClick(object sender, EventArgs
e)
{
string fullName = currentPath
+ listView1.FocusedItem.Text;
if (Directory.Exists(fullName))
{
currentPath = fullName
+ "\\";
fullName += "\\";
ListViewShow(fullName);
urlBox.Text = currentPath;
}
}
#endregion
第四步:回到上层目录
//工具栏上层目录
private void parentDir_Click(object sender, EventArgs
e)
{
if (currentPath !=
"" && currentPath
!= null)
{
currentPath = currentPath.Substring(0,
currentPath.Length - 2);
if (currentPath.LastIndexOf('\\') > 0)
{
currentPath = currentPath.Substring(0,
currentPath.LastIndexOf('\\') +
1);
urlBox.Text = currentPath;
ListViewShow(currentPath);
}
else//如果是根节点,则添加磁盘驱动器
{
listView1.Items.Clear();
currentPath = "";
urlBox.Text = currentPath;
TreeNode rootNode = new TreeNode("My");
ListViewShow(rootNode);
}
}
}
第五步:添加右键菜单
为了能够对控件组合能够更好的操作,我们通常情况下需要添加右键菜单,首先我们在窗体设计界面上添加ComtextMenuStrip控件listView1Menu:
//列表框鼠标点击事件
private void listView1_MouseClick(object sender, MouseEventArgs
e)
{
if (e.Button == MouseButtons.Right && currentPath
!= "" &&
currentPath != null)
{
Point position;
position = new Point(e.X,
e.Y);
listView1Menu.Show(this.listView1, position);
}
}
此时在运行程序后在ListView控件显示的项目上右键单击鼠标便会弹出listView1Menu菜单。
第六步:初始化,开始运行
运行之前需要在窗体加载事件中写入下面的代码:
#region treelist初始化
//初始化列表图标
ListViewSysImages(listView1);
ListViewSysImages(listView2);
ListViewSysImages(fileDownload);
//初始化树形结构
treeView.ImageList = imageList;
//添加根节点
TreeNode rootNode = new TreeNode("我的电脑");//初始化TreeList添加总根节点
rootNode.ImageIndex =
0;
rootNode.SelectedImageIndex =
0;
treeView.Nodes.Add(rootNode);
//初始化树形结构
InitialTreeView(rootNode);
rootNode.Expand();
ListViewShow(rootNode);
#endregion
之后,编译运行。