2010-09-22 83 views
4

好吧,我有作爲一個Windows的目錄樹一個TreeView。我已經加載了所有的目錄並正常運行,但它在加載具有許多子目錄的目錄時正在暫停我的GUI。我試圖實現多線程,但對它來說是新的,並且沒有運氣。需要幫助實現多線程進入我的TreeView(C#,WPF)

這是我爲我的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方法,但除此之外,我有點失落。

任何幫助將不勝感激!

回答

2

一個啓動一個異步過程最簡單的方法是使用匿名委託與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; 
    } 
} 
+0

非常感謝您的回覆,但我仍然有麻煩。按照您的建議,我已將folder_Expanded事件的內容移至RenderTreeView方法,但該代碼引用了發件人對象,並且我無法讓發件人通過該操作。我有點新東西,所以如果這是一個簡單的「新手」錯誤,請原諒我。 – 2010-09-23 15:48:57

+0

我添加了一個快速示例,瞭解重新分解代碼的外觀。抱歉,無法將命名約定修改爲更友好的內容。 – TheCodeKing 2010-09-23 17:28:34

+0

工程就像一個魅力!非常感謝你!!! – 2010-09-23 19:16:18

0

多線程可能幫助不大,因爲這裏樹視圖必須更新它的調度線程。加載大量條目時

的TreeView將暫停。要解決這個問題的方法之一是將內容存儲到該鏡像TreeView的結構,然後編程方式加載剛剛TreeView中的第一級的對象。

當用戶點擊一個節點上,裝入子節點的下一級並展開該節點。當該節點被摺疊時,刪除其子節點以節省TreeView內存。這對我來說很好。對於非常大的結構,我已經使用本地Sqlite數據庫(通過System.Data.Sqlite)作爲我的後備存儲,即使這樣TreeView快速加載並響應。

+0

這是避免導致鎖定的長任務的好方法,但爲了避免真正流暢的UI需要使用多線程,完全避免鎖定。 – TheCodeKing 2010-09-23 00:57:33