首页 > 编程语言 >C# WinForms 实现文件变化跟踪

C# WinForms 实现文件变化跟踪

时间:2023-12-29 10:15:09浏览次数:49  
标签:node CustomTreeNode C# void private 节点 WinForms 跟踪 目录

在实际应用中,有时会想要知道特定目录或磁盘上文件的变化情况,尤其是每天新增的文件。以下是一种使用C# WinForms的方法来轻松实现这一目标。

功能概述

  1. 添加目录:通过浏览功能选择要监控的目录,并将其添加到树形视图中。
  2. 按钮触发:通过按钮触发执行目录检测,获取最新的文件变动情况。
  3. 简易操作界面:简洁的用户界面包括添加目录、手动开始/停止检测、设置时间段等功能,方便灵活地进行操作。
  4. 文件信息显示:界面清晰展示每个目录的文件数量、最后修改时间等信息,帮助了解文件的状态。

图片描述

初始化界面和事件处理

首先在WinForms中创建了一个简单的用户界面。通过界面中的按钮来添加要监控的目录,开始和停止检测以及设置时间段。右键菜单项可以方便地删除或者打开选定的目录。

 // 在 Form1_Load 中初始化时间段和右键菜单
 private void Form1_Load(object sender, EventArgs e)
 {
     InitDateTimePicker(dateTimePicker1, dateTimePicker2); // 初始化时间段
     MakeContextMenuStrip(treeView1); // 创建右键菜单
 }


 // 开始检测按钮点击事件
 private void buttonStartCheck_Click(object sender, EventArgs e)
 {
     StartCheck(treeView1, dateTimePicker1.Value, dateTimePicker2.Value);
 }

 //停止检测按钮点击事件
 private void buttonStop_Click(object sender, EventArgs e)
 {
     StopCheck();
 }

 //添加目录按钮点击事件
 private void buttonAddDir_Click(object sender, EventArgs e)
 {
     AddDir(treeView1);
 }

搜索目录文件变化

使用了异步任务来搜索每个目录中新增的文件。通过StartCheck方法,遍历每个节点,为其启动一个异步任务来执行LoadDirectory方法。这个函数负责遍历目录、检测新增或修改文件,是应用程序核心的一部分。

 //开始检测各节点目录
 private void StartCheck(TreeView treeView, DateTime sTime, DateTime eTime)
 {
     runningTasks.Clear();// 清除现有任务
     foreach (TreeNode node in treeView.Nodes)
     {
         node.Nodes.Clear();// 清空节点下的子节点
         CancellationTokenSource source = new CancellationTokenSource();
         // 异步执行加载目录操作
         Task.Run(() =>
         {
             LoadDirectory((CustomTreeNode)node, sTime, eTime, source.Token);
         });
         runningTasks.Add(node, source);// 将节点和对应的取消 Token 加入任务列表
     }
 }

//检测目录
private void LoadDirectory(CustomTreeNode node, DateTime sTime, DateTime eTime, CancellationToken source)
{
    if (source.IsCancellationRequested)
    {
        return;//在取消请求的情况下退出任务
    }
    try
    {
        // 获取当前目录的子目录
        string[] subDirectories = Directory.GetDirectories(node.ResourePath);
        // 处理当前目录下的子目录
        foreach (string subDirectory in subDirectories)
        {
            //对节点目录进行检测
            CustomTreeNode subNode = new CustomTreeNode(subDirectory);
            LoadDirectory(subNode, sTime, eTime, source);
            if (subNode.Nodes.Count > 0)
            {
                node.InsertNode(subNode);
            }
        }
        node.UpdateNodeUI();

        //检测文件
        string[] files = Directory.GetFiles(node.ResourePath);
        foreach (string file in files)
        {
            // 处理在时间段内修改的文件
            DateTime lastModified = GetResourceLastWriteTime(file);
            if (lastModified >= sTime && lastModified <= eTime)
            {
                node.InsertNode(new CustomTreeNode(file) { ForeColor = Color.Red, LastDateTime = lastModified, Count = 1 });
            }
        }

    }
    catch (UnauthorizedAccessException ex)
    {
        node.InsertNode(new CustomTreeNode($"无法访问目录 {ex.Message}"));
    }
}

更新UI显示

为了保证界面的实时响应,CustomTreeNode类(继承TreeNode)中实现了更新节点信息的方法。

// 更新显示文本
public void UpdateNodeUI()
{
    SetTimeAndCount();
    if (TreeView != null)
    {
        TreeView.Invoke(new Action(() =>
        {
            SetText();
        }));
    }
    else
    {
        SetText();
    }
}

全部代码

Form1.cs

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        #region 逻辑
        private Dictionary<TreeNode, CancellationTokenSource> runningTasks = new Dictionary<TreeNode, CancellationTokenSource>();
        /// <summary>
        /// 开始检测各节点目录
        /// </summary>
        private void StartCheck(TreeView treeView, DateTime sTime, DateTime eTime)
        {
            runningTasks.Clear();// 清除现有任务
            foreach (TreeNode node in treeView.Nodes)
            {
                node.Nodes.Clear();// 清空节点下的子节点
                CancellationTokenSource source = new CancellationTokenSource();
                // 异步执行加载目录操作
                Task.Run(() =>
                {
                    LoadDirectory((CustomTreeNode)node, sTime, eTime, source.Token);
                });
                runningTasks.Add(node, source);// 将节点和对应的取消 Token 加入任务列表
            }
        }

        /// <summary>
        /// 停止检测正在进行的任务
        /// </summary>
        private void StopCheck()
        {
            foreach (var item in runningTasks)
            {
                CancellationTokenSource source = item.Value;// 获取任务对应的取消 Token
                source.Cancel();// 取消任务执行
            }
        }

        /// <summary>
        /// 检测目录
        /// </summary>
        /// <param name="directoryPath"></param>
        /// <param name="node"></param>
        private void LoadDirectory(CustomTreeNode node, DateTime sTime, DateTime eTime, CancellationToken source)
        {
            if (source.IsCancellationRequested)
            {
                return;//在取消请求的情况下退出任务
            }
            try
            {
                // 获取当前目录的子目录
                string[] subDirectories = Directory.GetDirectories(node.ResourePath);
                // 处理当前目录下的子目录
                foreach (string subDirectory in subDirectories)
                {
                    //对节点目录进行检测
                    CustomTreeNode subNode = new CustomTreeNode(subDirectory);
                    LoadDirectory(subNode, sTime, eTime, source);
                    if (subNode.Nodes.Count > 0)
                    {
                        node.InsertNode(subNode);
                    }
                }
                node.UpdateNodeUI();

                //检测文件
                string[] files = Directory.GetFiles(node.ResourePath);
                foreach (string file in files)
                {
                    // 处理在时间段内修改的文件
                    DateTime lastModified = GetResourceLastWriteTime(file);
                    if (lastModified >= sTime && lastModified <= eTime)
                    {
                        node.InsertNode(new CustomTreeNode(file) { ForeColor = Color.Red, LastDateTime = lastModified, Count = 1 });
                    }
                }

            }
            catch (UnauthorizedAccessException ex)
            {
                node.InsertNode(new CustomTreeNode($"无法访问目录 {ex.Message}"));
            }
        }

        /// <summary>
        /// 获取文件或者文件夹最后修改时间
        /// </summary>
        /// <param name="resourcePath"></param>
        /// <returns></returns>
        private static DateTime GetResourceLastWriteTime(string resourcePath)
        {
            if (File.Exists(resourcePath))
            {
                return File.GetLastWriteTime(resourcePath);
            }
            return Directory.GetLastWriteTime(resourcePath);
        }



        /// <summary>
        /// 添加检索目录
        /// </summary>
        private static void AddDir(TreeView treeView)
        {
            using FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
            DialogResult result = folderBrowserDialog.ShowDialog();

            if (result == DialogResult.OK && !string.IsNullOrWhiteSpace(folderBrowserDialog.SelectedPath))
            {
                // 用户选择的目录路径
                CustomTreeNode customTreeNode = new CustomTreeNode(folderBrowserDialog.SelectedPath);
                // 将目录加入节点
                treeView.Nodes.Add(customTreeNode);
            }
        }

        /// <summary>
        /// 为TreeView创建右键菜单栏,添加删除和打开目录选项
        /// </summary>
        /// <param name="treeView">要添加右键菜单的TreeView</param>
        private static void MakeContextMenuStrip(TreeView treeView)
        {
            // 创建右键菜单
            ContextMenuStrip contextMenuStrip = new ContextMenuStrip();
            // 添加删除选项
            ToolStripMenuItem deleteMenuItem = new ToolStripMenuItem("删除");
            //删除选项的点击事件处理
            deleteMenuItem.Click += (sender, e) =>
            {
                // 获取选中的节点
                if (treeView.SelectedNode is CustomTreeNode selectedNode)
                {
                    // 删除选中的节点
                    treeView.Nodes.Remove(selectedNode);
                }
            };
            contextMenuStrip.Items.Add(deleteMenuItem);

            // 添加删除选项
            ToolStripMenuItem openFolder = new ToolStripMenuItem("打开目录");
            //删除选项的点击事件处理
            openFolder.Click += (sender, e) =>
            {
                // 获取选中的节点
                if (treeView.SelectedNode is CustomTreeNode selectedNode)
                {
                    Process.Start("explorer.exe", selectedNode.ResourePath);
                }
            };
            contextMenuStrip.Items.Add(openFolder);

            // 将 ContextMenuStrip 分配给 TreeView
            treeView.ContextMenuStrip = contextMenuStrip;
        }

        /// <summary>
        /// 初始化两个DateTimePicker控件,设置其格式为自定义日期时间格式,并设置初始值为当天和第二天
        /// </summary>
        /// <param name="p1">第一个DateTimePicker控件</param>
        /// <param name="p2">第二个DateTimePicker控件</param>
        private static void InitDateTimePicker(DateTimePicker p1, DateTimePicker p2)
        {
            // 设置日期时间格式为自定义格式
            p1.Format = DateTimePickerFormat.Custom;
            p1.CustomFormat = "yyyy-MM-dd HH:mm:ss";
            p2.Format = DateTimePickerFormat.Custom;
            p2.CustomFormat = "yyyy-MM-dd HH:mm:ss";

            // 设置初始值为当天和第二天
            p1.Text = DateTime.Today.ToString();
            p2.Text = DateTime.Today.AddDays(1).ToLongDateString();
        }

        #endregion


        // 在 Form1_Load 中初始化时间段和右键菜单
        private void Form1_Load(object sender, EventArgs e)
        {
            InitDateTimePicker(dateTimePicker1, dateTimePicker2); // 初始化时间段
            MakeContextMenuStrip(treeView1); // 创建右键菜单
        }


        // 开始检测按钮点击事件
        private void buttonStartCheck_Click(object sender, EventArgs e)
        {
            StartCheck(treeView1, dateTimePicker1.Value, dateTimePicker2.Value);
        }

        //停止检测按钮点击事件
        private void buttonStop_Click(object sender, EventArgs e)
        {
            StopCheck();
        }

        //添加目录按钮点击事件
        private void buttonAddDir_Click(object sender, EventArgs e)
        {
            AddDir(treeView1);
        }
    }

Class.CustomTreeNode.cs

    internal class CustomTreeNode : TreeNode
    {
        /// <summary>
        /// 资源类型 1:文件 2:文件夹 3:头节点
        /// </summary>
        private int pathType = 0;
        public CustomTreeNode(string path)
        {
            ResourePath = path;
            Text = path;
        }
        /// <summary>
        /// 资源地址
        /// </summary>
        public string ResourePath { get; set; }
        /// <summary>
        /// 拥有文件数量
        /// </summary>
        public int Count { get; set; }
        /// <summary>
        /// 最后修改时间
        /// </summary>
        public DateTime LastDateTime { get; set; }
        
        /// <summary>
        /// 插入节点
        /// </summary>
        /// <param name="subNode"></param>
        public void InsertNode(CustomTreeNode subNode)
        {
            // 检查节点所属的树视图是否存在
            if (TreeView != null)
            {   
                // 使用 Invoke 方法确保在 UI 线程上执行插入操作
                TreeView.Invoke(new Action(() =>
                {
                    Nodes.Add(subNode);
                    Expand();//展开节点
                }));
            }
            else
            {
                // 如果当前已在 UI 线程上,直插入节点
                Nodes.Add(subNode);
            }
            subNode.UpdateNodeUI();
        }

        /// <summary>
        /// 更新显示文本
        /// </summary>
        public void UpdateNodeUI()
        {
            SetTimeAndCount();
            if (TreeView != null)
            {
                TreeView.Invoke(new Action(() =>
                {
                    SetText();
                }));
            }
            else
            {
                SetText();
            }
        }
        #region 私有
        /// <summary>
        /// 设置最后修改时间和节点文件数量
        /// </summary>
        private void SetTimeAndCount()
        {   
            if (Nodes.Count <= 0) return;//过滤叶子节点
            Count = 0;
            foreach (CustomTreeNode item in Nodes)
            {
                if (LastDateTime < item.LastDateTime)
                {
                    LastDateTime = item.LastDateTime;//记录最后修改时间
                }
                Count += item.Count;//统计每个节点文件数量
            }
        }

        /// <summary>
        /// 设置显示文本
        /// </summary>
        private void SetText()
        {
            pathType = File.Exists(ResourePath) ? 1 : Parent==null ? 3 : 2;
            switch (pathType)
            {   
                case 1://文件
                    Text = $"{Path.GetFileName(ResourePath)} ---时间:{LastDateTime}";
                    break;
                case 2://文件夹
                    Text = $"{Path.GetFileName(ResourePath)} ---{Count} 时间:{LastDateTime}";
                    break;
                case 3://头节点
                    Text = $"{ResourePath} ---{Count} 时间:{LastDateTime}";
                    break; 
                default:
                    break;
            }
        }
        #endregion
    }

总结

本文展示了如何使用C#和WinForms来实现一个简单的目录文件监控应用。通过监控目录中每日新增的文件变化,可以随时了解到目录内文件的变动情况。

这个应用程序是一个起点,你可以根据自己的需求和想法进行扩展和优化。感谢阅读!

GitHub地址:https://github.com/laijingming/CSharp-AJTool

软件下载地址:https://raw.githubusercontent.com/laijingming/CSharp-AJTool/main/resource/publish.rar 由于我的开发框架选的.NET6.0,因此想要运行你的电脑得有.ENT6.0。

.NET6.0官网下载地址:https://dotnet.microsoft.com/zh-cn/download/dotnet/thank-you/runtime-desktop-6.0.25-windows-x64-installer?cid=getdotnetcore

标签:node,CustomTreeNode,C#,void,private,节点,WinForms,跟踪,目录
From: https://www.cnblogs.com/aj407blogs/p/17934113.html

相关文章

  • 高级软件工程Advanced Software Engineering
    《高级软件工程》教学大纲Teaching(Course)OutlineofAdvancedSoftwareEngineering 第一部分 大纲说明1.课程代码:3290240012.课程类型:学科必修课3.开课时间:秋季学期,第3周—第20周4.课程目标:通过该课程的学习,培养学生掌握软件工程的基本原理、概念和技术方法,了解软件工程领域......
  • 机器学习Machine Learning
    附件5:课程教学大纲参考模板(注:各学院可采用该模板,也可自设模板,但每个学院需使用统一模板) 《机器学习》教学大纲Teaching(Course)Outlineof MachineLearning 第一部分 大纲说明(宋体,四号加粗,居中)1.课程代码:3290210032.课程类型:学科必修课3.开课时间:秋季学期,第1周—第 9周......
  • numpy、scipy、pandas、matplotlib的读书报告
    numpy、scipy、pandas、matplotlib的读书报告一、numpy的读书报告1Numpy概述1.1概念Numpy提供了两种基本的对象:ndarray和ufunc。Ndarray是存储单一数据类型的多维数组,而ufunc则是能够对数组进行处理的函数。1.2功能l  创建n维数组(矩阵)l  对数组进行函数运算,使用函......
  • ApplicationHost.config文件被破坏导致IIS崩溃
    https://www.8a.hk/index.php/news/content/4105.html 今天临近下班时,突然出现了一个重大BUG。服务器IIS崩溃了,所有的站点都打不开了。提示错误:“配置文件的XML格式不正确”。一下就惊到了,感觉自已又要加班了。 根据提示,找到了applicationHost.config文件,打开后,发现,里面全部......
  • idea 项目加载 全线飘红, @Service飘红,连 import java.util.List; 也飘红,终极解决方案
    常规方案:1、maven,检查maven配置,重新加载maven依赖,重新加载项目,把maven的本地私库删了重新下载2、检查idea配置,jdk配置,加载的项目的配置,idea清理缓存重启,等等3、是否更新了其他idea配置,比如git忽略文件里是否添加了*.class 4、重新安装idea5、及其他以上方案自行百度,都可以......
  • 解决PHP项目在Docker(WSL2)中运行缓慢的问题(转)
    原文:https://blog.csdn.net/ljfrocky/article/details/1298073081、环境Windows11DockerDesktop4.17.1(开启WSL2)WSL2(Ubuntu-22.04)2、前言最近在使用Docker容器运行PHP项目的时候,发现特别缓慢。例如一个干净的ThinkPHP5.1框架,访问首页都需要1秒以上,如果再加上数据库查......
  • PyCharm基本使⽤-hello world
    Projects-NewProject,新建一个项目 选择python解释器,可以在cmd中输入wherepython切换到ProjectFiles可以只看项目文件,简洁一些 新建PythonFile 书写print('helloworld!'),然后右键空白处,运行 运行结果如下: ......
  • C# 面试题整理
    C#是单根继承还是多根继承?C#是一种单根继承语言,一个类不能直接从两个类派生。 值类型和引用类型的区别?值类型:就是一个包含实际数据的对象。即当定义一个值类型的变量时,C#会根据它所声明的类型,以栈方式分配一块大小相适应的存储区域给这个变量,随后对这个变量的读或写操作就......
  • 在不使用内置函数和中间变量的情况交换数字LeetCode力扣题解面试题16.01
    #异或法#Kotlin```KotlinclassSolution{  funswapNumbers(numbers:IntArray):IntArray{    numbers[0]=numbers[0]xornumbers[1]    numbers[1]=numbers[1]xornumbers[0]    numbers[0]=numbers[0]xor......
  • go-carbon v2.3.1 发布,轻量级、语义化、对开发者友好的 Golang 时间处理库
    carbon是一个轻量级、语义化、对开发者友好的golang时间处理库,支持链式调用。目前已被awesome-go收录,如果您觉得不错,请给个star吧github.com/golang-module/carbongitee.com/golang-module/carbon安装使用Golang版本大于等于1.16//使用github库goget-ugithu......