在递归函数中使用 BeginInvoke 将节点添加到 BackgroundWorker 中的 TreeView
Using BeginInvoke in a recursive function adding nodes to a TreeView in a BackgroundWorker
我的代码具有扫描根目录并在 TreeView
中显示 txt 文件(例如)的功能:
private TreeNode DirectoryToTreeView(TreeNode parentNode, string path,
string extension = ".txt")
{
var result = new TreeNode(parentNode == null ? path : Path.GetFileName(path));
foreach (var dir in Directory.GetDirectories(path))
{
TreeNode node = DirectoryToTreeView(result, dir);
if (node.Nodes.Count > 0)
{
result.Nodes.Add(node);
}
}
foreach (var file in Directory.GetFiles(path))
{
if (Path.GetExtension(file).ToLower() == extension.ToLower())
{
result.Nodes.Add(Path.GetFileName(file));
}
}
return result;
}
这个函数应该像这样从按钮调用:
treeView1.Nodes.Add(DirectoryToTreeView(null, @"C:\Users\Tomer\Desktop\a"));
它显然冻结了 UI。
我是新手,我在网上搜索了一下,似乎没有什么与我的问题相关,因为没有人使用递归函数,我不能简单地调用 BeginInvoke
整个函数,因为它没有任何效果。
我应该走什么路?也许更改函数以使用 while 循环,然后在 if 语句中调用 BeginInvoke
?在内存中创建一个 TreeNode
对象来填充(可能太大)?
下面是一个异步 Task
方法的示例,用于使用给定文件类型的目录树填充 TreeNode
。里面的CreateTree(...)
是一个local function递归调用遍历目录
private async Task<TreeNode> CreateTreeAsync(string startDir, string fileExt)
{
var di = new DirectoryInfo(startDir);
var result = new TreeNode(di.Name);
var searchPattern = $"*.{fileExt.TrimStart('.')}";
return await Task.Run(() =>
{
void CreateTree(DirectoryInfo dirInfo, TreeNode node)
{
try
{
foreach (var fileInfo in dirInfo.EnumerateFiles(searchPattern))
node.Nodes.Add(fileInfo.Name);
foreach (var subDir in dirInfo.EnumerateDirectories())
{
try
{
// Optional to skip the branches with no files at any level.
if (!subDir.EnumerateFiles(searchPattren,
SearchOption.AllDirectories).Any()) continue;
var newNode = new TreeNode(subDir.Name);
node.Nodes.Add(newNode);
CreateTree(subDir, newNode);
}
catch (Exception ex)
{
// Skip exceptions like UnauthorizedAccessException
// and continue...
Console.WriteLine(ex.Message);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
CreateTree(di, result);
return result;
});
}
注意: 如果您在 .NET 5+/.NET Core 下,则不需要 try..catch
块来跳过无法访问的目录和文件。使用带有 EnumerationOptions
参数的 EnumerateXXX 方法重载。
现在您需要 async
来电者:
private async void someButton_Click(object sender, EventArgs e)
{
// Optional...
treeView1.Nodes.Clear();
var dir = @"...";
var ext = "txt";
var node = await CreateTreeAsync(dir, ext);
if (node.Nodes.Count == 0)
MessageBox.Show($"No '{ext}' files were found.");
else
{
treeView1.Nodes.Add(node);
node.Expand();
}
}
您可以将 DirectoryToTreeNode
方法转换为 asynchronous method, and offload any blocking I/O operation to the ThreadPool
, by using the Task.Run
方法:
private async Task<TreeNode> DirectoryToTreeNodeAsync(string path,
TreeNode parentNode = null)
{
var node = new TreeNode(parentNode == null ? path : Path.GetFileName(path));
string[] subdirectories = await Task.Run(() => Directory.GetDirectories(path));
foreach (string dirPath in subdirectories)
{
TreeNode childNode = await DirectoryToTreeNodeAsync(dirPath, node);
node.Nodes.Add(childNode);
}
string[] files = await Task.Run(() => Directory.GetFiles(path));
foreach (string filePath in files)
{
node.Nodes.Add(Path.GetFileName(filePath));
}
return node;
}
请注意,运行 在 ThreadPool
上(在 Task.Run
委托内)时没有触及 UI 控件。所有 UI 控件都应被操作 exclusively by the UI thread。
用法示例:
private async void Button1_Click(object sender, EventArgs e)
{
Button1.Enabled = false;
Cursor = Cursors.WaitCursor;
try
{
TreeView1.Nodes.Clear();
TreeView1.Nodes.Add(
await DirectoryToTreeNodeAsync(@"C:\Users\Tomer\Desktop\a"));
}
finally
{
Cursor = Cursors.Default;
Button1.Enabled = true;
}
}
我的代码具有扫描根目录并在 TreeView
中显示 txt 文件(例如)的功能:
private TreeNode DirectoryToTreeView(TreeNode parentNode, string path,
string extension = ".txt")
{
var result = new TreeNode(parentNode == null ? path : Path.GetFileName(path));
foreach (var dir in Directory.GetDirectories(path))
{
TreeNode node = DirectoryToTreeView(result, dir);
if (node.Nodes.Count > 0)
{
result.Nodes.Add(node);
}
}
foreach (var file in Directory.GetFiles(path))
{
if (Path.GetExtension(file).ToLower() == extension.ToLower())
{
result.Nodes.Add(Path.GetFileName(file));
}
}
return result;
}
这个函数应该像这样从按钮调用:
treeView1.Nodes.Add(DirectoryToTreeView(null, @"C:\Users\Tomer\Desktop\a"));
它显然冻结了 UI。
我是新手,我在网上搜索了一下,似乎没有什么与我的问题相关,因为没有人使用递归函数,我不能简单地调用 BeginInvoke
整个函数,因为它没有任何效果。
我应该走什么路?也许更改函数以使用 while 循环,然后在 if 语句中调用 BeginInvoke
?在内存中创建一个 TreeNode
对象来填充(可能太大)?
下面是一个异步 Task
方法的示例,用于使用给定文件类型的目录树填充 TreeNode
。里面的CreateTree(...)
是一个local function递归调用遍历目录
private async Task<TreeNode> CreateTreeAsync(string startDir, string fileExt)
{
var di = new DirectoryInfo(startDir);
var result = new TreeNode(di.Name);
var searchPattern = $"*.{fileExt.TrimStart('.')}";
return await Task.Run(() =>
{
void CreateTree(DirectoryInfo dirInfo, TreeNode node)
{
try
{
foreach (var fileInfo in dirInfo.EnumerateFiles(searchPattern))
node.Nodes.Add(fileInfo.Name);
foreach (var subDir in dirInfo.EnumerateDirectories())
{
try
{
// Optional to skip the branches with no files at any level.
if (!subDir.EnumerateFiles(searchPattren,
SearchOption.AllDirectories).Any()) continue;
var newNode = new TreeNode(subDir.Name);
node.Nodes.Add(newNode);
CreateTree(subDir, newNode);
}
catch (Exception ex)
{
// Skip exceptions like UnauthorizedAccessException
// and continue...
Console.WriteLine(ex.Message);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
CreateTree(di, result);
return result;
});
}
注意: 如果您在 .NET 5+/.NET Core 下,则不需要 try..catch
块来跳过无法访问的目录和文件。使用带有 EnumerationOptions
参数的 EnumerateXXX 方法重载。
现在您需要 async
来电者:
private async void someButton_Click(object sender, EventArgs e)
{
// Optional...
treeView1.Nodes.Clear();
var dir = @"...";
var ext = "txt";
var node = await CreateTreeAsync(dir, ext);
if (node.Nodes.Count == 0)
MessageBox.Show($"No '{ext}' files were found.");
else
{
treeView1.Nodes.Add(node);
node.Expand();
}
}
您可以将 DirectoryToTreeNode
方法转换为 asynchronous method, and offload any blocking I/O operation to the ThreadPool
, by using the Task.Run
方法:
private async Task<TreeNode> DirectoryToTreeNodeAsync(string path,
TreeNode parentNode = null)
{
var node = new TreeNode(parentNode == null ? path : Path.GetFileName(path));
string[] subdirectories = await Task.Run(() => Directory.GetDirectories(path));
foreach (string dirPath in subdirectories)
{
TreeNode childNode = await DirectoryToTreeNodeAsync(dirPath, node);
node.Nodes.Add(childNode);
}
string[] files = await Task.Run(() => Directory.GetFiles(path));
foreach (string filePath in files)
{
node.Nodes.Add(Path.GetFileName(filePath));
}
return node;
}
请注意,运行 在 ThreadPool
上(在 Task.Run
委托内)时没有触及 UI 控件。所有 UI 控件都应被操作 exclusively by the UI thread。
用法示例:
private async void Button1_Click(object sender, EventArgs e)
{
Button1.Enabled = false;
Cursor = Cursors.WaitCursor;
try
{
TreeView1.Nodes.Clear();
TreeView1.Nodes.Add(
await DirectoryToTreeNodeAsync(@"C:\Users\Tomer\Desktop\a"));
}
finally
{
Cursor = Cursors.Default;
Button1.Enabled = true;
}
}