Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WPF

Easy Navigation in Windows Explorer and Total Commander

4.88/5 (12 votes)
30 Oct 2011MIT7 min read 46.4K   677  
Easy navigation in Windows Explorer and Total Commander.

Introduction

Have you ever thought of an idea that navigation in Windows could be much more convenient? Why wouldn't one employ a technique similar to the "Navigate to class" in most common IDEs, when the relevant folder matches are displayed in the popup window, brought to the foreground by a predefined key combination, like that:

MatchesList.png

After thinking of that, I wrote a tiny utility titled "Navigation Assistant", displayed above. The latest executable (as well as the complete source code) is available on Google code, it's Open Source, and distributed by MIT license. Hope it will be useful for someone. This article source code contains just the most tricky parts, explained below (merely due to CodeProject size recommendations).

Why needed at all

Here are Windows Explorer and Total Commander disadvantages which may frustrate most users:

  1. One needs to perform a huge amount of mouse clicks to access a deeply nested folder.
  2. Search in Windows Explorer is rather slow, even with indexing turned on.
  3. Low usability of Windows Explorer search: need to open an Explorer, eyefocus on the search field (not centered on the screen), click it, type a (preferably complete) folder name, and wait. If you've made a mistake in the folder name, need to repeat it all over.
  4. Low usability of address bar hints in Windows Explorer and Total Commander (subfolder names which appear after pressing Tab in the address bar). You need to type the full path anyway and press the Tab button for each subfolder, and search for the correct hint for each subfolder. And if you are expecting a wrong folder path (e.g., "Program Files (x32)" instead of "Program Files"), you need to perform all these actions again.

For these particular reasons, I've written the "Navigation Assistant", whose operation is equivalent to the "Navigate to class" or "Navigate to File" features of most common IDEs: combinations Ctrl-N and Ctrl-Shift-N in JetBrains products (ReSharper, IDEA, PhpStorm, WebStorm), Ctrl-Shift-T in Eclipse.

Other advantages

Besides eliminating the aforementioned disadvantages, the following benefits are obtained:

  1. no need to know the exact folder path
  2. no need to know the exact folder name
  3. less typing, search speed, great usability
  4. instant search results preview at each keypress (so you can quickly amend the search query)

Better folder structure comes as a free bonus with the usage of Navigation Assistant. Indeed, many developers (especially familiar with a brilliant Code Complete) are aware of a rule of 7+-2 (the most efficient number of elements in the brain active memory). Therefore, it's a good strategy to restrict the number of subfolders in a given folder up to 7, as well as to restrict the number of fields in a class, parameters in a method, etc.

Unfortunately, such an approach would be utilized at a cost of inconvenient folder navigation. Navigation Assistant will let you forget about concerning this disadvantage.

How to use it

To navigate quickly to the needed folder, simply press a predefined key combination (Ctrl-Shift-M by default) in a Windows Explorer or Total Commander application (or at any other application, which will imply a new Windows Explorer instantiation for navigation). The main application window will appear:

NavigationWindow.png

Start typing a destination folder name.

Killer feature: it's not necessary to type a complete folder name; e.g., it's sufficient to type "documents and" to reach Documents and Settings. Moreover, it's not even necessary to type the word "documents" completely, as "doc and" will also suit (best wishes, JetBrains!); also, it's not mandatory to type folder name from the start, "and settings" will be sufficient. Pascal/Camel case in folder names is also recognized for developers' sake: search query "nav assist" will match the folder "NavigationAssistant".

A list of folder matches will appear due to these manipulations:

MatchesList.png

All you have to do is to click a suitable element with mouse or use Up/Down keys to select a required path and press Enter.

If you are dissatisfied for some reason with the navigation window, you can quickly minimize it to the tray with the Escape button.

If you bring the program to front outside Windows Explorer or Total Commander, a new Window Explorer application will be launched on selecting a folder match (with the needed folder already open); it's possible to change the default navigator to Total Commander in settings.

Options are covered in detail in the Google Code project home page.

How it's implemented

The utility is written in Microsoft .NET 3.5 and Windows Presentation Foundation; therefore it should operate wherever .NET 3.5 is installed.

Installer is implemented with the help of Inno Setup, which I found extremely convenient: it's much better than Visual Studio Installer, Install Shield, or WiX:

  1. it's free and Open-Source
  2. it's modular (in the sense that it's disconnected from Microsoft technologies)
  3. it's transparent (one needs to amend just a text script, no visual programming, similar to Visual Studio Installer)
  4. consequently, installer files are not re-created at each update and thousands of GUIDs are not re-generated, so no troubles with version control systems
  5. flexible (supports Pascal/Delphi scripting); I had programmed with Delphi last time 7 years ago, but Inno Setup advantages definitely compensated this circumstance
  6. oh yeah, it magically creates a single installer for both x32 and x64 versions

An installers comparison may be found, for example, here.

Certain non-trivial parts are present in the code:

Global keyboard hooks

The application needs to listen to global hotkey strokes to reveal itself appropriately, whichever window is active currently. .NET doesn't provide this functionality out-of-the box. I used this code, although other solutions are present, just Google for "global hooks/global hotkeys".

Bringing an inactive application to the foreground

If the application has encountered an expected key combination, it has to render itself as a foreground window.

WPF does have a method Window.Activate, which uses a SetForegroundWindow WinAPI function internally and, therefore, possesses all its restrictions. More precisely, if the application is inactive when calling the method, the focus will remain in the initially active window. Actually, this is a good behaviour, which prevents malicious programs from bringing themselves to the front and interrupting user experience. But it's not what is needed in the particular case of Navigation Assistant.

I used this approach, though other solutions exist (here and here), but they didn't work appropriately in all cases. Unfortunately, the link that inspired me is in Visual Basic 6, so the working C# code is below:

C#
//WinApi functions declarations
[DllImport("user32.dll")]
static extern bool InvalidateRect(IntPtr hWnd, IntPtr lpRect, bool bErase);

[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();

[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);

[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, 
                           IntPtr lpdwProcessId);

[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();

[DllImport("user32.dll")]
private static extern bool AttachThreadInput(uint idAttach, 
        uint idAttachTo, bool fAttach);

//The function to bring the WPF window to the foreground
private void ReallySetForegroundWindow(Window window)
{
    //Initial attempt
    window.Show();
    window.Activate();
    
    //Get window handle
    IntPtr hWnd = new WindowInteropHelper(window).Handle;

    //Check to see if we are on the foreground thread
    uint foregroundThreadId = 
      GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
    uint ourThreadId = GetCurrentThreadId();

    //If not, attach our thread's 'input' to the foreground thread's
    if (foregroundThreadId != ourThreadId)
    {
        AttachThreadInput(foregroundThreadId, ourThreadId, true);
    }

    //Bring our window to the foreground
    SetForegroundWindow(hWnd);

    //If we are attached to our thread, detach it now
    if (foregroundThreadId != ourThreadId)
    {
        AttachThreadInput(foregroundThreadId, ourThreadId, false);
    }

    //Force our window to redraw
    InvalidateRect(hWnd, IntPtr.Zero, true);
}

Windows Explorer integration

Manipulating Windows Explorer windows was inspired by this article, though significantly modified. The core code is below, extensively commented:

C#
//A function to return all windows explorer instances,
//not internet browsers (don't be confused with the class name InternetExplorer).
//InternetExplorer, ShellWindows, ShellWindowsClass
//are classes from SHDocVw namespace of the Interop.SHDocVw assembly.
private List<InternetExplorer> GetAllExplorers()
{
    //Get the collection of shell windows
    ShellWindows shellWindows = new ShellWindowsClass();

    //First filter InternetExplorer instances from shell windows
    //(includes Windows Explorers and Internet Explorer browsers).
    //Then leave just windows explorers, not internet browsers.
    List<InternetExplorer> explorers = 
      shellWindows.OfType<InternetExplorer>().Where(IsWindowsExplorer).ToList();

    return explorers;
}

//Determines whether the InternetExplorer instance
//is actually a windows explorer, not internet browser
private bool IsWindowsExplorer(InternetExplorer ie)
{
    string filename = Path.GetFileNameWithoutExtension(ie.FullName).ToLower();
    return filename.Equals("explorer");
}

//Changes the current path in the windows explorer window
private void NavigateTo(InternetExplorer windowsExplorer, string path)
{
    windowsExplorer.Navigate("file:///" + Path.GetFullPath(path));
}

New Windows Explorer instantiation requires some dubious work, too. Please refer to the WindowsExplorerManager class in the source code.

Total Commander integration

Total Commander integration is performed with the help of command line options; first and foremost, /O, which sets an active path not in a new Total Commander instance, but in the currently opened one (if present). Sample code is shown below.

C#
public void NavigateTo(string path, bool openNewCommander, 
       string totalCommanderFolder, string totalCommanderFileName)
{
    //Switches from help:
    // /T Opens the passed dir(s) in new tab(s). Now also works
    //    when Total Commander hasn't been open yet;
    // /L= Set path in left window;
    // /O If Total Commander is already running, activate it
    //    and pass the path(s) in the command line to that instance 
    //    (overrides the settings in the configuration dialog to have multiple windows);
    // /S Interprets the passed dirs as source/target instead of left/right (for usage with /O).
    //We use /S to make total commander set path for the currently active panel and tab.

    //A small hack is used: if there are several open total commanders 
    //and /O option is used, totalcmd.exe /O will open
    //a currently topmost visible commander.
    string template = openNewCommander
                          ? "/T /L=\"{0}\""
                          : "/O /S /L=\"{0}\"";

    string arguments = string.Format(template, Path.GetFullPath(path));

    ProcessStartInfo processStartInfo = new ProcessStartInfo();
    processStartInfo.WorkingDirectory = totalCommanderFolder;
    processStartInfo.FileName = totalCommanderFileName;
    processStartInfo.Arguments = arguments;
    Process.Start(processStartInfo);
}

Architecture

A standard multitier architecture is being used in the code (an excellent overview is provided at Domain Driven Design Quickly Online by Avram and Marinescu).

  1. UI Layer
    1. Views
  2. Presenters Layer
    1. Presenters
    2. Presentation Services
    3. View Models
    4. View Model Mappers
  3. Domain Model a.k.a. Business Layer
    1. Application Services
    2. Domain Data Objects (plain Data Transfer Objects)
  4. No Data Access Layer :-)

I tried to use the Model-View-ViewModel pattern at first (common to WPF), but then switched to MVP for the following reasons:

  1. MVP adds another layer of abstraction (presenters); otherwise, a huge amount of logic would go to View Models and
  2. this logic does not conceptually belong to View Models (see Single Responsibility Principle)
  3. View Models become too heavy
  4. not possible to use interface-driven development for Views, consequently
  5. UI layer becomes a mess (no separation of concerns, modularity, clear interfaces)

So MVP is used currently, but binding data to the UI is performed through View Models as follows:

  1. either pass View Models to the IView interface (methods or properties)
  2. instantiate View Models in Views internally

Limitations

If Navigation Assistant has been closed manually before the last computer shutdown or has not been launched at the last Windows start up, it's impossible to tell on Navigation Assistant launch whether the folders cache is in actual state; so the latter will be updated silently in the background thread and the last cache version (probably outdated) will be used before this process is finished.

.NET and WPF can be quite slow and heavy and resource consuming, but you'd have to live with that.

Conclusion

I tried to share a small utility for convenient navigation in Windows Explorer and Total Commander. Hope it will be useful for the community. Also, I hope that the technical decisions overview and the code for integration with the Shell and Windows Explorer and Total Commander will be helpful.

Once again, links to the latest installer and Google Code project (SVN is used as most it is probably known to a majority of developers).

Thank you for the attention!

License

This article, along with any associated source code and files, is licensed under The MIT License