Lookup in DirectoryTree

When folder change, FileExplorer select the current folder in DirectoryTree, which set the current folder VM's IsSelected, and it's parent folders' IsExpanded to true. This is done by it's Lookup code.


In v1, DirectoryTreeViewModel Broadcast change of directory to all it's sub-DirectoryTreeItemViewModel, and then it's sub-sub, the process continues until the directory found.

Folder lookup may involve many layers of directory, so the Lookup code do invoke AsyncObservableCollection's loading code for a number of times. Because Lookup is synchronous and has to be run on UI thread. The UI just jam when Lookup is happening.

internal void BroascastCurrentDirectoryChanging(DirectoryInfoEx newDirectory)
    if (IsExpanded)
        if (this.EmbeddedDirectoryModel.EmbeddedDirectoryEntry.Equals(newDirectory) ||
            IOTools.HasParent(newDirectory, this.EmbeddedDirectoryModel.EmbeddedDirectoryEntry) ||
            IOTools.HasParent(this.EmbeddedDirectoryModel.EmbeddedDirectoryEntry, newDirectory))
            if (IsLoaded)
                foreach (DirectoryTreeItemViewModel subItem in SubDirectories)
        else IsExpanded = false;


In v2, another approach is used. All VM in DirectoryTree (DirectroryTreeItemViewModel, DIVM) contains IsExpanded property, so:
1. DirectoryTreeViewModel (DVM) .PlaceBounty() set bounty to selected directory, and lookup the first level of DirectoryTreeItemViewModel, set it's IsExpanded to true.
2. In UI thread, the TreeViewItem that represent the DIVM is expanded, thus request Items from the DIVM.
3. In Background thread, in AsyncObservableCollection, DIVM continue load sub-DIVM, and compare with DVM's bounty using a hierarchy comparer (DIM.EqualsOrHasChild()), if match result is child, set it's IsExpanded to true again, and go to 2 again.
4. At one point when hierarchy comparer returns Current, the bounty is found and DVM.ReportBounty(foundDIVM) is called and DVM.Bounty is cleared.


In v3, async operation is avaliable and something similar to v1 is used, except it's refactored and is async:

The lookup code is encapsulated in TreeRootSelector and TreeSelector.

These classes can be embedded by Hierarchy based ViewModels, e.g. TreeViewMode and TreeViewNodeModel below:

public class TreeViewModel : INotifyPropertyChanged      
   public TreeViewModel()
     Entries = new EntriesHelper<TreeNodeViewModel>(); 
     //Value is based on string
     Selection = new TreeRootSelector<TreeNodeViewModel, string>(Entries, compareFunc);                 
     Entries.SetEntries(new TreeNodeViewModel("", "Root", this, null));
   public ITreeSelector<TreeNodeViewModel, string> Selection { get; set; }
   public IEntriesHelper<TreeNodeViewModel> Entries { get; set; }

   HierarchicalResult compareFunc(string path1, string path2)
       if (path1.Equals(path2, StringComparison.CurrentCultureIgnoreCase))
           return HierarchicalResult.Current;
       if (path1.StartsWith(path2, StringComparison.CurrentCultureIgnoreCase))
           return HierarchicalResult.Parent;
       if (path2.StartsWith(path1, StringComparison.CurrentCultureIgnoreCase))
           return HierarchicalResult.Child;
       return HierarchicalResult.Unrelated;
public class TreeNodeViewModel : INotifyPropertyChanged
   public TreeNodeViewModel(TreeViewModel root, TreeNodeViewModel parentNode)
     Entries = new EntriesHelper<TreeNodeViewModel>(() => 
          Task.Run(() => { /* Load your subentries (when needed) */ )));
     Selection = new TreeSelector<TreeNodeViewModel, string>(value, this, 
          parentNode == null ? root.Selection : parentNode.Selection, Entries);
   public ITreeSelector<TreeNodeViewModel, string> Selection { get; set; }
   public IEntriesHelper<TreeNodeViewModel> Entries { get; set; }


Since the code of TreeSelector are used by both BreadcrumbTree and DirectoryTree, lookup supports ITreeLookups and ITreeLookupProcessors.

ITreeLookup define how to lookup, e.g. RecrusiveSearch<VM, T>.LoadSubentriesIfNotLoaded,
ITreeLookupProcessor define what to do when process an entry (what entry to process depends on ITreeLookup).

//In TreeRootSelector<VM, T> class's SelectAsync() method:

 await LookupAsync(value, RecrusiveSearch<VM, T>.LoadSubentriesIfNotLoaded,
                    SetSelected<VM, T>.WhenSelected, SetChildSelected<VM, T>.ToSelectedChild);
  • SearchNextLevel - just like recrusive search but only work until next level, this is used when ComboBox value changes, it find the appropriate item in next level and set it's IsSelected to true.
  • RecrusiveSearch - Recrusive search to find the required value, this is used when SelectAsync(value) is called
  • RecrusiveBroadcast - Recrusive, but unlike Recrusive search, broadcast to all nodes.
  • SearchNextUsingReverseLookup - Search item in next level, based on your supplied value and an ITreeSelector that's associated with it. This is required because in DirectoryInfoEx, there may be different ways to reach a path (e.g. Desktop \ This PC \ Documents and C:\Users\{User}\Documents), to update the SelectedValue when an item no longer selected.
  • SetSelected - Update Selector.IsSelected to true when HierarchicalResult is Current.
  • SetNotSelected - Update Selector.IsSelected to true when HierarchicalResult is / is not Current.
  • SetExpanded - Update Entries.IsExpanded to true when HierarchicalResult is Child.
  • SetChildSelected - Update Selector.SelectedValue to child's value when HierarchicalResultis Current or Child.
  • SetChildNotSelected - Update Selector.SelectedValue to null when HierarchicalResultis Current or Child.

More info can be found in Breadcrumb Tree

Last edited May 24, 2014 at 9:46 AM by lycj, version 5


No comments yet.