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:
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:
- One needs to perform a huge amount of mouse clicks to access a deeply nested folder.
- Search in Windows Explorer is rather slow, even with indexing turned on.
- 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.
- 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:
- no need to know the exact folder path
- no need to know the exact folder name
- less typing, search speed, great usability
- 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:
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:
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:
- it's free and Open-Source
- it's modular (in the sense that it's disconnected from Microsoft technologies)
- it's transparent (one needs to amend just a text script, no visual programming, similar to Visual Studio Installer)
- 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
- flexible (supports Pascal/Delphi scripting); I had programmed with Delphi last time 7 years ago, but Inno Setup advantages definitely compensated this circumstance
- 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:
[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);
private void ReallySetForegroundWindow(Window window)
{
window.Show();
window.Activate();
IntPtr hWnd = new WindowInteropHelper(window).Handle;
uint foregroundThreadId =
GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
uint ourThreadId = GetCurrentThreadId();
if (foregroundThreadId != ourThreadId)
{
AttachThreadInput(foregroundThreadId, ourThreadId, true);
}
SetForegroundWindow(hWnd);
if (foregroundThreadId != ourThreadId)
{
AttachThreadInput(foregroundThreadId, ourThreadId, false);
}
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:
private List<InternetExplorer> GetAllExplorers()
{
ShellWindows shellWindows = new ShellWindowsClass();
List<InternetExplorer> explorers =
shellWindows.OfType<InternetExplorer>().Where(IsWindowsExplorer).ToList();
return explorers;
}
private bool IsWindowsExplorer(InternetExplorer ie)
{
string filename = Path.GetFileNameWithoutExtension(ie.FullName).ToLower();
return filename.Equals("explorer");
}
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.
public void NavigateTo(string path, bool openNewCommander,
string totalCommanderFolder, string totalCommanderFileName)
{
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).
- UI Layer
- Views
- Presenters Layer
- Presenters
- Presentation Services
- View Models
- View Model Mappers
- Domain Model a.k.a. Business Layer
- Application Services
- Domain Data Objects (plain Data Transfer Objects)
- 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:
- MVP adds another layer of abstraction (presenters); otherwise, a huge amount of logic would go to View Models and
- this logic does not conceptually belong to View Models (see Single Responsibility Principle)
- View Models become too heavy
- not possible to use interface-driven development for Views, consequently
- 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:
- either pass View Models to the
IView
interface (methods or properties) - 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!