.NET Framework中实现TreeView展开节点后加载FTP目录
WinForm TreeView FTP目录懒加载实现方案
核心思路
仅在初始化时加载FTP根目录的第一层内容,给每个目录节点添加临时空节点(让TreeView显示展开按钮);当用户点击节点展开按钮时,再加载该节点对应的子目录,替换临时节点,避免一次性加载所有层级导致的登录耗时过长问题。
具体实现步骤
1. 重构目录加载方法
修改原有递归加载逻辑,让方法只加载当前FTP路径的直接子目录/文件,同时把节点对应的FTP完整路径存入Tag属性,方便后续展开时调用。另外修正原有的文件/目录判断逻辑(原逻辑用是否含.判断不可靠,后续会给出优化方案):
using System; using System.IO; using System.Net; using System.Windows.Forms; namespace GISFileManagerClient { public static class FtpTreeHelper { // 加载指定FTP路径的直接子节点(不递归) public static void LoadDirectChildren(TreeNode parentNode, string ftpPath, string username, string password) { parentNode.Nodes.Clear(); // 清空节点,避免重复加载 try { var request = (FtpWebRequest)WebRequest.Create(ftpPath); request.Credentials = new NetworkCredential(username, password); request.Method = WebRequestMethods.Ftp.ListDirectory; request.KeepAlive = true; using (var response = (FtpWebResponse)request.GetResponse()) using (var responseStream = response.GetResponseStream()) using (var reader = new StreamReader(responseStream)) { string directoryContents = reader.ReadToEnd(); string[] items = directoryContents.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string item in items) { string itemName = Path.GetFileName(item); if (string.IsNullOrEmpty(itemName)) continue; TreeNode node = new TreeNode(itemName); // 存储当前节点对应的FTP完整路径 node.Tag = $"{ftpPath}/{itemName}"; // 临时判断是否为目录(后续建议优化此逻辑) bool isDirectory = !itemName.Contains("."); if (isDirectory) { // 添加空节点,让TreeView显示展开按钮 node.Nodes.Add(new TreeNode()); } parentNode.Nodes.Add(node); } } } catch (WebException ex) { MessageBox.Show($"加载失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } }
2. 初始化TreeView根节点
在Form的加载事件中,仅加载FTP根目录的第一层内容:
private void Form1_Load(object sender, EventArgs e) { string ftpRoot = "ftp://你的服务器地址/"; string username = "你的FTP用户名"; string password = "你的FTP密码"; // 清空TreeView并创建根节点 treeView1.Nodes.Clear(); TreeNode rootNode = new TreeNode("FTP根目录"); rootNode.Tag = ftpRoot; treeView1.Nodes.Add(rootNode); // 加载根节点的直接子目录 FtpTreeHelper.LoadDirectChildren(rootNode, ftpRoot, username, password); }
3. 绑定展开事件实现懒加载
给TreeView添加BeforeExpand事件处理,在用户点击展开按钮时加载对应节点的子目录:
private void treeView1_BeforeExpand(object sender, TreeViewCancelEventArgs e) { // 判断节点是否未加载过子目录(仅含一个临时空节点) if (e.Node.Nodes.Count == 1 && string.IsNullOrEmpty(e.Node.Nodes[0].Text)) { string ftpPath = e.Node.Tag.ToString(); string username = "你的FTP用户名"; // 建议存到全局变量或配置文件,避免硬编码 string password = "你的FTP密码"; // 加载当前节点的子目录 FtpTreeHelper.LoadDirectChildren(e.Node, ftpPath, username, password); } }
4. 优化建议
- 文件/目录判断优化:原逻辑用是否包含
.判断目录不可靠(比如目录名可能带点,文件名可能不带点),建议用以下方法准确判断:// 判断指定FTP路径是否为目录 private static bool IsFtpDirectory(string ftpPath, string username, string password) { try { var request = (FtpWebRequest)WebRequest.Create(ftpPath); request.Credentials = new NetworkCredential(username, password); request.Method = WebRequestMethods.Ftp.ListDirectory; using (var response = (FtpWebResponse)request.GetResponse()) { return true; } } catch (WebException ex) { var response = (FtpWebResponse)ex.Response; // 550错误表示路径不存在或不是目录 if (response.StatusCode == FtpStatusCode.ActionNotTakenFileUnavailable) { return false; } throw; } } - 异步加载避免UI卡顿:将加载方法改为异步,防止加载目录时UI冻结:
对应展开事件改为异步:public static async Task LoadDirectChildrenAsync(TreeNode parentNode, string ftpPath, string username, string password) { parentNode.Nodes.Clear(); try { var request = (FtpWebRequest)WebRequest.Create(ftpPath); request.Credentials = new NetworkCredential(username, password); request.Method = WebRequestMethods.Ftp.ListDirectory; request.KeepAlive = true; using (var response = (FtpWebResponse)await request.GetResponseAsync()) using (var responseStream = response.GetResponseStream()) using (var reader = new StreamReader(responseStream)) { string directoryContents = await reader.ReadToEndAsync(); string[] items = directoryContents.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string item in items) { string itemName = Path.GetFileName(item); if (string.IsNullOrEmpty(itemName)) continue; TreeNode node = new TreeNode(itemName); node.Tag = $"{ftpPath}/{itemName}"; bool isDirectory = await Task.Run(() => IsFtpDirectory($"{ftpPath}/{itemName}", username, password)); if (isDirectory) { node.Nodes.Add(new TreeNode()); } // 跨线程更新UI需调用Invoke parentNode.TreeView.Invoke((Action)(() => parentNode.Nodes.Add(node))); } } } catch (WebException ex) { parentNode.TreeView.Invoke((Action)(() => MessageBox.Show($"加载失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error))); } }private async void treeView1_BeforeExpand(object sender, TreeViewCancelEventArgs e) { if (e.Node.Nodes.Count == 1 && string.IsNullOrEmpty(e.Node.Nodes[0].Text)) { string ftpPath = e.Node.Tag.ToString(); string username = "你的FTP用户名"; string password = "你的FTP密码"; await FtpTreeHelper.LoadDirectChildrenAsync(e.Node, ftpPath, username, password); } }
内容的提问来源于stack exchange,提问作者P0tat0627
相关产品推荐
相关产品推荐

