[英]Need help implementing multithreading into my TreeView (C#, WPF)
好的,我有一個TreeView可以用作Windows的目錄樹。 我可以加載所有目錄並正常運行,但是它在加載具有許多子級的目錄時暫停了我的GUI。 我正在嘗試實現多線程,但是它是新手,沒有運氣。
這是我的TreeView所擁有的:
private readonly object _dummyNode = null;
public MainWindow()
{
InitializeComponent();
foreach (string drive in Directory.GetLogicalDrives())
{
DriveInfo Drive_Info = new DriveInfo(drive);
if (Drive_Info.IsReady == true)
{
TreeViewItem item = new TreeViewItem();
item.Header = drive;
item.Tag = drive;
item.Items.Add(_dummyNode);
item.Expanded += folder_Expanded;
TreeViewItemProps.SetIsRootLevel(item, true);
Dir_Tree.Items.Add(item);
}
}
}
private void folder_Expanded(object sender, RoutedEventArgs e)
{
TreeViewItem item = (TreeViewItem)sender;
if (item.Items.Count == 1 && item.Items[0] == _dummyNode)
{
item.Items.Clear();
try
{
foreach (string dir in Directory.GetDirectories(item.Tag as string))
{
DirectoryInfo tempDirInfo = new DirectoryInfo(dir);
bool isSystem = ((tempDirInfo.Attributes & FileAttributes.System) == FileAttributes.System);
if (!isSystem)
{
TreeViewItem subitem = new TreeViewItem();
subitem.Header = tempDirInfo.Name;
subitem.Tag = dir;
subitem.Items.Add(_dummyNode);
subitem.Expanded += folder_Expanded;
subitem.ToolTip = dir;
item.Items.Add(subitem);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
每當我擴展包含大量子目錄的目錄時,該程序似乎就會凍結幾秒鍾。 我想在處理過程中顯示加載消息或動畫,但是我不確定如何從多線程開始。 我知道我必須使用TreeView的Dispatcher.BeginInvoke方法,但除此之外,我有點迷失了。
任何幫助將不勝感激!!!
啟動異步過程最簡單的方法之一是對BeginInvoke使用匿名委托。 例如,您可以將構造函數中的代碼移動到單獨的方法(例如RenderTreeView),然后異步調用它以開始一個新線程,如下所示:
Action action = RenderTreeView;
action.BeginInvoke(null, null);
訣竅在於,每次與異步流程中的任何UI元素進行交互時,都需要重新加入主UI線程,否則您將獲得有關跨線程訪問的異常。 這也是相對簡單的。
在Windows窗體中,它是:
if (InvokeRequired)
Invoke(new MethodInvoker({item.Items.Add(subitem)}));
else
item.Items.Add(subitem);
在WPF中,它是:
if (!Dispatcher.CheckAccess())
Dispatcher.Invoke(new Action(() => item.Items.Add(subitem)));
else
item.Items.Add(subitem);
您確實需要分解代碼以使其在方法方面更加靈活。 目前,所有內容都捆綁在一種方法中,這使得處理異步流程和對其進行重構非常困難。
更新這里,你去:)
public partial class MainWindow : Window
{
private readonly object dummyNode = null;
public MainWindow()
{
InitializeComponent();
Action<ItemCollection> action = RenderTreeView;
action.BeginInvoke(treeView1.Items, null, null);
}
private void RenderTreeView(ItemCollection root)
{
foreach (string drive in Directory.GetLogicalDrives())
{
var driveInfo = new DriveInfo(drive);
if (driveInfo.IsReady)
{
CreateAndAppendTreeViewItem(root, drive, drive, drive);
}
}
}
private void FolderExpanded(object sender, RoutedEventArgs e)
{
var item = (TreeViewItem) sender;
if (item.Items.Count == 1 && item.Items[0] == dummyNode)
{
item.Items.Clear();
var directory = item.Tag as string;
if (string.IsNullOrEmpty(directory))
{
return;
}
Action<TreeViewItem, string> action = ExpandTreeViewNode;
action.BeginInvoke(item, directory, null, null);
}
}
private void ExpandTreeViewNode(TreeViewItem item, string directory)
{
foreach (string dir in Directory.GetDirectories(directory))
{
var tempDirInfo = new DirectoryInfo(dir);
bool isSystem = ((tempDirInfo.Attributes & FileAttributes.System) == FileAttributes.System);
if (!isSystem)
{
CreateAndAppendTreeViewItem(item.Items, tempDirInfo.Name, dir, dir);
}
}
}
private void AddChildNodeItem(ItemCollection collection, TreeViewItem subItem)
{
if (Dispatcher.CheckAccess())
{
collection.Add(subItem);
}
else
{
Dispatcher.Invoke(new Action(() => AddChildNodeItem(collection, subItem)));
}
}
private void CreateAndAppendTreeViewItem(ItemCollection items, string header, string tag, string toolTip)
{
if (Dispatcher.CheckAccess())
{
var subitem = CreateTreeViewItem(header, tag, toolTip);
AddChildNodeItem(items, subitem);
}
else
{
Dispatcher.Invoke(new Action(() => CreateAndAppendTreeViewItem(items, header, tag, toolTip)));
}
}
private TreeViewItem CreateTreeViewItem(string header, string tag, string toolTip)
{
var treeViewItem = new TreeViewItem {Header = header, Tag = tag, ToolTip = toolTip};
treeViewItem.Items.Add(dummyNode);
treeViewItem.Expanded += FolderExpanded;
return treeViewItem;
}
}
多線程在這里可能無濟於事,因為TreeView必須在其Dispatcher線程上進行更新。
加載大量條目時,TreeView將暫停。 解決此問題的一種方法是將內容存儲到鏡像TreeView結構的對象中,然后以編程方式僅加載TreeView的第一級。
當用戶單擊某個節點時,加載下一級子節點並展開該節點。 當該節點折疊時,刪除其子節點以節省TreeView內存。 這對我來說效果很好。 對於超大型結構,我使用了本地Sqlite數據庫(通過System.Data.Sqlite )作為后備存儲,即使如此,TreeView仍快速加載並且響應迅速。
您還可以查看使用BackgroundWorker。 這是在單獨的線程上執行操作的最簡單方法(對我來說:))。
BackgroundWorker組件概述: http : //msdn.microsoft.com/zh-cn/library/8xs8549b.aspx
BackgroundWorker類: http : //msdn.microsoft.com/zh-cn/library/system.componentmodel.backgroundworker.aspx
您可以將其與Command模式結合使用,如此處所述-
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.