1. 最近去公司实习,实习有一周的考核期,其中就给我布置了这个任务:做一个文件管理系统,类似于我的电脑,不过得用 DevExpress插件里面的。用 C# 写的。
首先 VS 新建 WinForm 项目,左边添加 Dev TreeList,右边添加 Dev GridControl,右边上面添加前进、后退按钮和一个textButton。

接下来直接上代码:
2.原先 GridControl 的数据源是 datatable,然后直接用
DataTable dt = new DataTable();
DataRow dr = dt.NewRow();
dr["FileName"] = fi.Name;
dr["DataTime"] = fi.LastWriteTime;
dr["Getpath"] = s; 。
在后面用的是 this.gridControl1.DataSource = dt;
给 GridControl 初始化时直接用的:
dt.Columns.Add("FileName", System.Type.GetType("System.String")); dt.Columns[0].Caption = "文件名称";
dt.Columns.Add("FileSize", System.Type.GetType("System.String")); dt.Columns[1].Caption = "文件大小";
dt.Columns.Add("DataType", System.Type.GetType("System.String")); dt.Columns[2].Caption = "文件类型";
dt.Columns.Add("DataTime", System.Type.GetType("System.String")); dt.Columns[3].Caption = "修改时间";
dt.Columns.Add("Getpath", System.Type.GetType("System.String")); dt.Columns[4].Caption = "文件路径";
但是,带我的老大说这写得不够面向对象,扩展性不好。后来苦思冥想,用 List<T> 来作GridControl 的数据源,然后封装成一个对象给GridControl 的列赋值这样就是面向对象了。
下面直接上代码。
一、先增加一个类 FormItem.cs,代码如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TreeTest
{
public class FormItem
{
//文件名称
public string FileName
{
get;
set;
}
//文件大小
public string FileSize
{
get;
set;
}
//文件类型
public string DataType
{
get;
set;
}
//修改时间
public string DataTime
{
get;
set;
}
//文件路径
public string Getpath
{
get;
set;
}
}
}
二、在 Form1.cs 上写下如下代码,代码都有注释,应该有点基础的都能看懂。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using DevExpress.XtraTreeList.Nodes;
using System.IO;
using DevExpress.XtraTreeList;
using System.Net.Mime;
namespace TreeTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// DataTable dt = new DataTable();
bool _flag = false;
bool _check = true;
IList<FormItem> itemsList = new BindingList<FormItem>();
//通过代码给TreeList添加列
private void CreateColumns()
{
string[] captions = { "FullName","Name", "Type" };
for (int i = 0; i < 3; i++)
{
treeList1.Columns.Add(); //增加一列
treeList1.Columns[i].Caption = captions[i]; //设置每一列的标题
treeList1.Columns[i].VisibleIndex = (i == 0 || i == 2) ? -1 : i; // VisibleIndex为-1表示隐藏
treeList1.Columns[i].OptionsColumn.AllowEdit = false;
}
}
//遍历磁盘
private void InitDrives()
{
treeList1.BeginUnboundLoad();
TreeListNode node;
try
{
string[] root = Directory.GetLogicalDrives(); //获取计算机上格式为 "<drive letter>:\" 的逻辑驱动器的名称。例如C盘、D盘等
foreach (string s in root)
{
node = treeList1.AppendNode(new object[] { s, s, "Logical Driver" }, null); //给treeList添加节点
node.HasChildren = true;
node.Tag = true;
}
}
catch { }
treeList1.EndUnboundLoad();
}
//遍历指定路径下的文件---当不是最底层的文件夹时调用此方法,即此文件夹里面既有文件,又有文件夹。
private void InitFolders(string path, TreeListNode pNode)
{
treeList1.BeginUnboundLoad();
TreeListNode node;
DirectoryInfo di;
FileInfo fi;
try
{
string[] root = Directory.GetDirectories(path); //返回指定目录中的子目录的名称(包括其路径)。
foreach (string s in root)
{
try{
fi = new FileInfo(s); // FileInfo
di = new DirectoryInfo(s);
if (!pNode.Expanded && _flag) //DirectoryInfo
{
node = treeList1.AppendNode(new object[] { s, di.Name,"Folder" }, pNode);
node.HasChildren = true;
node.Tag = true;
}
_check = true;
InitGridControl1(fi, s ,path);
}
catch { }
}
this.gridControl1.DataSource = itemsList;
}
catch { }
InitFiles(path, pNode);
treeList1.EndUnboundLoad();
}
//遍历指定路径下的文件---当为最底层的文件夹时调用此方法,即此文件夹里面都是文件,没有文件夹。
private void InitFiles(string path, TreeListNode pNode)
{
TreeListNode node;
FileInfo fi;
try
{
string[] root = Directory.GetFiles(path); //GetFiles()获取当前目录下的所有文件
foreach (string s in root)
{
fi = new FileInfo(s);
if (!pNode.Expanded && _flag)
{
node = treeList1.AppendNode(new object[] { s, fi.Name, "File" }, pNode);
node.HasChildren = false;
}
_check = false;
InitGridControl1(fi, s,path);
}
this.gridControl1.DataSource = itemsList;
this.gridView1.Columns["Getpath"].Visible = false; //隐藏文件路径那一栏
}
catch { }
}
//右侧表格的初始化事件
private void InitGridControl1(FileInfo fi,string s,string path)
{
FormItem Fi = new FormItem();
Fi.FileName = fi.Name;
Fi.DataTime = fi.LastWriteTime;
Fi.Getpath = s;
if (_check == false) //当为文件时
{
Fi.FileSize = System.Math.Ceiling(fi.Length / 1024.0).ToString() + " KB";
Fi.DataType = fi.Extension;
}
else //当为文件夹时
{
Fi.DataType = "文件夹";
}
itemsList.Add(Fi);
textBox1.Text = path;
textBox1.Font = new Font(textBox1.Font.Name, 16);
}
//treeList1的点击事件
private void treeList1_FocusedNodeChanged(object sender, DevExpress.XtraTreeList.FocusedNodeChangedEventArgs e)
{
itemsList.Clear();
// dt.Clear();
_flag = false; //当点击某个结点时,不管treeList有没有展开,都不在tree上增加结点
InitFolders(e.Node.GetDisplayText("FullName"), e.Node); //这里有点问题,第二次点击treelist节点时,节点下又重新生成了子节点
}
//在扩展一个节点之前引发
private void treeList1_BeforeExpand_1(object sender, BeforeExpandEventArgs e)
{
if (e.Node.Tag != null)
{
Cursor currentCursor = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
itemsList.Clear();
// dt.Clear();
_flag = true;
InitFolders(e.Node.GetDisplayText("FullName"), e.Node);
e.Node.Tag = null;
Cursor.Current = currentCursor;
}
}
private void Form1_Load(object sender, EventArgs e)
{
//给右侧列表添加列属性
this.gridControl1.DataSource = itemsList;
// this.gridView1.Columns["Getpath"].Visible = false; //隐藏文件路径那一栏,不能在这里写,会发生未将对象引用到对象的实例
CreateColumns();
InitDrives();
}
//给文件夹和文件加图片
private void treeList1_GetSelectImage(object sender, GetSelectImageEventArgs e)
{
if (e.Node.GetDisplayText("Type") == "Folder")
e.NodeImageIndex = e.Node.Expanded ? 1 : 0;
else if (e.Node.GetDisplayText("Type") == "File") e.NodeImageIndex = 2;
else e.NodeImageIndex = 3;
}
//返回上一级目录
private void button1_Click(object sender, EventArgs e)
{
string path = textBox1.Text; //将当前目录保存到字符串path
int i = path.LastIndexOf("\\"); //获取字符串最后一个斜杠的位置
string strPath = path.Substring(0, i); //取当前目录的字符串第一个字符到最后一个斜杠所在位置。 相当于上级目录
if (i <= 2) {
strPath = path.Substring(0, i+1);
}
if (i >= 2) {
itemsList.Clear();
// dt.Clear();
FileInfo fi;
try
{
string[] root = Directory.GetDirectories(strPath); //返回指定目录中的子目录的名称(包括其路径)。
foreach (string s in root)
{
try
{
fi = new FileInfo(s);
_check = true;
InitGridControl1(fi, s, strPath);
}
catch { }
}
this.gridControl1.DataSource = itemsList;
}
catch
{
}
textBox1.Text = strPath;
}
}
//前进一步事件
private void button2_Click(object sender, EventArgs e)
{
object ojb = gridView1.GetRowCellValue(gridView1.FocusedRowHandle, "Getpath"); //获取gridControl里面行中的某一列的值,获取文件夹
string path = (string)ojb;
MessageBox.Show(path);
}
//右键菜单
private void gridView1_PopupMenuShowing(object sender, DevExpress.XtraGrid.Views.Grid.PopupMenuShowingEventArgs e)
{
//选择的行数
int intselect = gridView1.SelectedRowsCount;
MenuItemOpen.Enabled = false;
MenuItemCopy.Enabled = false;
MenuItemRename.Enabled = false;
MenuItemDelete.Enabled = false;
if (intselect == 1)
{
MenuItemOpen.Enabled = true;
MenuItemCopy.Enabled = true;
MenuItemRename.Enabled = true;
MenuItemDelete.Enabled = true;
}
}
//右键点击事件
private void contextMenuStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
if ((e.ClickedItem).Name == "MenuItemOpen")
{
object ojb = gridView1.GetRowCellValue(gridView1.FocusedRowHandle, "Getpath"); //获取gridControl里面行中的某一列的值,获取文件夹
string path = (string)ojb;
System.Diagnostics.Process.Start(path); //打开此文件
}
}
//右侧列表双击事件
private void gridView1_DoubleClick(object sender, EventArgs e)
{
string strName = (string)gridView1.GetRowCellValue(gridView1.FocusedRowHandle, "DataType"); //读出文件类型是文件夹还是文件
string path = (string)gridView1.GetRowCellValue(gridView1.FocusedRowHandle, "Getpath"); //读出文件夹的路径
if (strName == "文件夹")
{ //假如是文件夹
itemsList.Clear();
//dt.Clear();
FileInfo fi;
try
{
string[] root = Directory.GetDirectories(path); //返回指定目录中的子目录的名称(包括其路径)。
if (root.Length == 0)
{
root = Directory.GetFiles(path);
foreach (string s in root)
{
fi = new FileInfo(s);
_check = false;
InitGridControl1(fi, s, path);
}
}
else
{
foreach (string s in root)
{
try
{
fi = new FileInfo(s);
_check = true;
InitGridControl1(fi, s, path);
}
catch { }
}
}
this.gridControl1.DataSource = itemsList;
}
catch
{
}
textBox1.Text = path;
}
else {
System.Diagnostics.Process.Start(path); //打开此文件
}
}
}
}
三、原先写的不是面向对象的,特别是 InitGridControl1() 函数有很大的不同。以下是原先写的。可以对比上面我写的。
//右侧表格的初始化事件--原先的
private void InitGridControl1(FileInfo fi,string s,string path)
{
DataRow dr = dt.NewRow();
dr["FileName"] = fi.Name;
dr["DataTime"] = fi.LastWriteTime;
dr["Getpath"] = s;
if (_check == false) //当为文件时
{
dr["FileSize"] = System.Math.Ceiling(fi.Length / 1024.0).ToString() + " KB";
dr["DataType"] = fi.Extension;
}
else {
dr["DataType"] = "文件夹";
}
dt.Rows.Add(dr);
textBox1.Text = path;
textBox1.Font = new Font(textBox1.Font.Name, 16);
}
四,最后的结果图:
五、其中遇到的问题
(1)读取文件夹里面的文件时,有三种情况的,一种是文件夹里既有文件夹又有文件,一种是文件夹里面只有文件夹,一种是文件夹里面只有文件。前两种情况可以用方法InitFolders(string path, TreeListNode pNode)读取出来,后一种可用方法InitFiles(string path, TreeListNode pNode)读出。这个地方卡了好久,最后一步步的调试看出来的。
(2)怎么点击左侧 TreeList 的文件夹,在右侧 gridControl 里显示出文件夹里面的具体信息。这个卡壳了好久,得亏一个师兄帮忙才做出来[捂脸][捂脸]。
(3)隐藏 gridControl 的某一列方法,我原先在 Form1_Load(object sender, EventArgs e) 里面加的这一句:
this.gridView1.Columns["Getpath"].Visible = false; 总报很恐怖的错误,后来在 InitFiles(string path, TreeListNode pNode) 方法里面加的这句,没有报错。
(4)List<T> 对象初始化时不能直接写成 List<FormItem> itemsList = new List<FormItem>(); 而应该写成 IList<FormItem> itemsList = new BindingList<FormItem>();
不然会报很大的错误。
六、未做出来的功能。右边最上头的 前进、后退功能,此功能比较难做,可能需要用到 队列、栈等将点击事件记录下来,然后前进或后退时再取出。比较难做,以后有时间了可能会再做。