C# ObjectListView实现树状文件夹浏览

C# ObjectListView 实现树状图与表格显示文件夹内容详解

概述

在C#桌面应用程序开发中,经常需要展示文件系统的树状结构和详细内容。ObjectListView是一个功能强大的开源.NET控件,它扩展了标准的ListView控件,提供了更多高级功能。本文将介绍如何使用ObjectListView实现树状结构显示文件夹内容,并支持图片预览功能。

环境准备

首先,需要通过NuGet安装必要的依赖包:

复制代码
Install-Package ObjectListView

核心实现

1. 文件系统数据模型

创建一个表示文件系统项的模型类:

复制代码
public class MyFileSystemInfo : IEquatable<MyFileSystemInfo>
{
    public MyFileSystemInfo(FileSystemInfo fileSystemInfo)
    {
        if (fileSystemInfo == null) throw new ArgumentNullException("fileSystemInfo");
        this.info = fileSystemInfo;
    }

    public bool IsDirectory { get { return this.AsDirectory != null; } }
    public DirectoryInfo AsDirectory { get { return info as DirectoryInfo; } }
    public FileInfo AsFile { get { return info as FileInfo; } }
    
    private readonly FileSystemInfo info;
    
    public string Name { get { return info.Name; } }
    public string Extension { get { return info.Extension; } }
    public DateTime CreationTime { get { return info.CreationTime; } }
    public DateTime LastWriteTime { get { return info.LastWriteTime; } }
    public string FullName { get { return info.FullName; } }
    public FileAttributes Attributes { get { return info.Attributes; } }
    public long Length { get { return this.AsFile != null ? this.AsFile.Length : 0; } }

    public IEnumerable GetFileSystemInfos()
    {
        ArrayList children = new ArrayList();
        if (this.IsDirectory)
        {
            foreach (FileSystemInfo x in this.AsDirectory.GetFileSystemInfos())
                children.Add(new MyFileSystemInfo(x));
        }
        return children;
    }

    // 实现相等性比较接口
    public bool Equals(MyFileSystemInfo other)
    {
        return other != null && Equals(other.info.FullName, this.info.FullName);
    }
    
    public override bool Equals(object obj)
    {
        return Equals(obj as MyFileSystemInfo);
    }
    
    public override int GetHashCode()
    {
        return this.info.FullName.GetHashCode();
    }
}

2. 初始化树状列表视图

复制代码
void InitializeTreeListExample()
{
    // 设置树形列表属性
    this.treeListView.HierarchicalCheckboxes = true;
    this.treeListView.HideSelection = true;
    
    // 设置节点展开判断逻辑
    this.treeListView.CanExpandGetter = delegate (object x)
    {
        return ((MyFileSystemInfo)x).IsDirectory;
    };
    
    // 设置获取子节点逻辑
    this.treeListView.ChildrenGetter = delegate (object x)
    {
        MyFileSystemInfo myFileSystemInfo = (MyFileSystemInfo)x;
        try
        {
            return myFileSystemInfo.GetFileSystemInfos();
        }
        catch (UnauthorizedAccessException ex)
        {
            MessageBox.Show(ex.Message, "权限错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            return new ArrayList();
        }
    };

    // 自定义树形连接线样式
    TreeListView.TreeRenderer renderer = this.treeListView.TreeColumnRenderer;
    renderer.LinePen = new Pen(Color.Firebrick, 0.5f);
    renderer.LinePen.DashStyle = DashStyle.Dot;

    // 设置系统图标
    SysImageListHelper helper = new SysImageListHelper(this.treeListView);
    this.treeColumnName.ImageGetter = delegate (object x)
    {
        return helper.GetImageIndex(((MyFileSystemInfo)x).FullName);
    };
    
    // 设置自定义渲染器
    this.treeColumnDetail.Renderer = new OperationRenderer();
    this.treeColumnDelete.Renderer = new OperationRenderer1();
    
    // 设置文件类型列
    this.treeColumnFileType.AspectGetter = delegate (object x)
    {
        return ShellUtilities.GetFileType(((MyFileSystemInfo)x).FullName);
    };
    
    // 加载根节点
    string path = "E://Imgs//" + DateTime.Now.ToString("yyyyMMdd");
    ArrayList roots = new ArrayList();
    roots.Add(new MyFileSystemInfo(new DirectoryInfo(path)));
    this.treeListView.Roots = roots;
    this.treeListView.Refresh();
    
    SelectAllNodes(); // 选中所有节点
}

3. 自定义列渲染器

实现操作按钮的自定义渲染:

复制代码
public class OperationRenderer : BaseRenderer
{
    private readonly Pen borderPen = new Pen(Color.DarkBlue, 1.5f);
    private readonly Brush textBrush = new SolidBrush(Color.White);
    private readonly Brush backgroundBrush = new SolidBrush(Color.FromArgb(70, 130, 180));

    public override void Render(Graphics g, Rectangle r)
    {
        var rowData = this.RowObject as MyFileSystemInfo;
        
        if (ShouldShowOperation(rowData))
        {
            // 绘制按钮背景和边框
            g.FillRectangle(backgroundBrush, r);
            g.DrawRectangle(borderPen, r.X, r.Y, r.Width - 1, r.Height - 1);

            // 绘制文字
            StringFormat fmt = new StringFormat
            {
                Alignment = StringAlignment.Center,
                LineAlignment = StringAlignment.Center
            };

            g.DrawString("详情", 
                new Font("微软雅黑", 9, FontStyle.Bold),
                textBrush, 
                r, 
                fmt);
        }
        else
        {
            // 清空不满足条件的单元格
            using (var clearBrush = new SolidBrush(this.ListView.BackColor))
            {
                g.FillRectangle(clearBrush, r);
            }
        }
    }

    // 条件判断逻辑
    private bool ShouldShowOperation(MyFileSystemInfo data)
    {
        return data.IsDirectory && data.Name != DateTime.Now.ToString("yyyyMMdd");
    }
}

4. 图片预览功能

实现点击查看图片详情功能:

复制代码
private void treeListView_CellClick(object sender, CellClickEventArgs e)
{
    if (e.Column == this.treeColumnDetail)
    {
        // 获取绑定的文件系统对象
        var fileInfo = e.Model as MyFileSystemInfo;
        if (fileInfo == null) return;
        
        // 清空现有图片
        if (images.Count > 0)
        {
            foreach (var img in images)
            {
                img.Dispose();
            }
            images.Clear();
        }
        
        // 加载新图片
        imagePaths = GetImageFiles(fileInfo.FullName);
        images = new List<Image>();
        LoadImages();
        
        // 显示图片
        for (int i = 0; i < 4; i++)
        {
            if (images.Count - 1 >= i)
            {
                DisplayImage(i);
            }
            else
            {
                pictureBoxeList[i].Image = defaultImage;
            }
        }
    }
    else if (e.Column == this.treeColumnDelete)
    {
        // 删除操作
        var fileInfo = e.Model as MyFileSystemInfo;
        if (fileInfo == null) return;
        
        if (Directory.Exists(fileInfo.FullName))
        {
            try
            {
                Directory.Delete(fileInfo.FullName, true);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"删除失败: {ex.Message}");
            }
        }
        RefreshTreeView();
    }
}

// 获取文件夹中的所有图片文件
static List<string> GetImageFiles(string folderPath)
{
    List<string> imagePaths = new List<string>();
    
    if (Directory.Exists(folderPath))
    {
        string[] imageExtensions = { "*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.tiff" };
        
        foreach (var extension in imageExtensions)
        {
            string[] files = Directory.GetFiles(folderPath, extension, SearchOption.AllDirectories);
            imagePaths.AddRange(files);
        }
    }
    
    return imagePaths;
}

5. 搜索功能

实现按名称搜索文件系统项:

复制代码
private void BtnSearch_Click(object sender, EventArgs e)
{
    ArrayList roots = new ArrayList();
    string targetPath = "E://Imgs//" + DateTime.Now.ToString("yyyyMMdd");
    
    if (string.IsNullOrEmpty(TxtCX.Text))
    {
        roots.Add(new MyFileSystemInfo(new DirectoryInfo(targetPath)));
        this.treeListView.Roots = roots;
        return;
    }
    
    // 获取所有文件系统项
    var allEntries = GetAllFileSystemEntries(targetPath);
    ArrayList filteredResults = new ArrayList();
    
    // 筛选匹配项
    foreach (var entry in allEntries)
    {
        var myInfo = new MyFileSystemInfo(entry);
        if (myInfo.Name.Contains(TxtCX.Text))
        {
            filteredResults.Add(myInfo);
        }
    }
    
    // 更新树形视图
    if (filteredResults.Count > 0)
    {
        this.treeListView.Roots = filteredResults;
    }
    this.treeListView.Refresh();
}

// 递归获取所有文件系统项
private List<FileSystemInfo> GetAllFileSystemEntries(string rootPath)
{
    var entries = new List<FileSystemInfo>();
    try
    {
        var rootDir = new DirectoryInfo(rootPath);
        entries.Add(rootDir);
        
        // 递归遍历子目录
        foreach (var dir in rootDir.EnumerateDirectories("*", SearchOption.AllDirectories))
        {
            entries.Add(dir);
            foreach (var file in dir.EnumerateFiles())
            {
                entries.Add(file);
            }
        }
        
        // 添加根目录文件
        foreach (var file in rootDir.EnumerateFiles())
        {
            entries.Add(file);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"遍历错误: {ex.Message}");
    }
    return entries;
}

界面设计建议

  1. 布局设计

    • 使用SplitContainer将界面分为左右两部分

    • 左侧放置TreeListView显示文件树

    • 右侧放置PictureBox数组用于图片预览

  2. 列配置

    • 名称列:显示文件/文件夹名称和系统图标

    • 类型列:显示文件类型描述

    • 修改时间列:显示最后修改时间

    • 大小列:显示文件大小

    • 操作列:放置详情和删除按钮

  3. 交互设计

    • 支持节点展开/折叠

    • 支持复选框多选

    • 支持点击操作按钮执行相应功能

    • 支持搜索过滤

总结

通过使用ObjectListView控件,我们可以轻松实现一个功能丰富的文件浏览器,具备树形结构展示、多选操作、图片预览等高级功能。本文介绍了核心实现方法,包括数据模型设计、树形列表初始化、自定义渲染器、图片预览和搜索功能。

关键点:

  • 使用MyFileSystemInfo类封装文件系统项

  • 通过CanExpandGetterChildrenGetter实现树形结构

  • 自定义渲染器实现操作按钮

  • 递归遍历文件系统实现搜索功能

  • 使用PictureBox数组实现多图预览

这种实现方式可以广泛应用于文件管理、图片浏览、资源管理等场景,具有良好的扩展性和用户体验。


希望本文对您理解和使用C# ObjectListView控件有所帮助。如有任何问题,欢迎在评论区留言讨论

相关推荐
ccut 第一混10 小时前
c# 调用basler 相机
c#·halcon·basler
TomCode先生11 小时前
c#动态树形表达式详解
开发语言·c#
上位机付工19 小时前
C#与倍福TwinCAT3进行ADS通信
开发语言·c#
土了个豆子的20 小时前
02.继承MonoBehaviour的单例模式基类
开发语言·visualstudio·单例模式·c#·里氏替换原则
疯狂的维修20 小时前
c#中public类比博图
c#·自动化
土了个豆子的1 天前
03.缓存池
开发语言·前端·缓存·visualstudio·c#
xiaowu0802 天前
策略模式-不同的鸭子的案例
开发语言·c#·策略模式
VisionPowerful2 天前
九.弗洛伊德(Floyd)算法
算法·c#
ArabySide2 天前
【C#】 资源共享和实例管理:静态类,Lazy<T>单例模式,IOC容器Singleton我们该如何选
单例模式·c#·.net core