CustomPlaces
2021 JAN 06
While working I usually start by browsing to a project directory using Windows Explorer. From there I open files and start programs. Mostly double clicking files to edit. This works great for most text files. Not so great for other types of files.
Sometimes it is necessary to create a new file or open an existing file from within a program using Window’s OpenFileDialog
. This is the dialog that shows up when you press File > Open... Unfortunately, this dialog has a habit of starting in the wrong directory. It always starts in Documents, Desktop, or some other folder used severals days ago. I end up having to navigate to my projects directory again. Perhaps severals times if multiple programs are involved.
It would be great if OpenFileDialog
had a shortcut to my projects directory. It would be even greater if OpenFileDialog
had a shortcut to all Explorer windows that are currently open.
We can make it so using FileDialog.CustomPlaces
and shell programming.
The poor implementation in Microsoft Visual Studio 2017 inspired this idea. It inserts a shortcut to repos:
It is trivial to add CustomPlaces
to OpenFileDialog
and SaveFileDialog
. A custom place is a shortcut to a folder; for our intents and purposes at-least.
The following example demonstrates how to add a shortcut to C:\
using System;
using System.Windows.Forms;
namespace Example
{class Program
{
[STAThread]static void Main(string[] args)
{var dialog = new OpenFileDialog();
CustomPlaces.Add("C:\\");
dialog.ShowDialog();
dialog.
}
} }
How about adding useful paths? Such as locations already open in Explorer. Turns out it is almost trivial (thanks to the help of Luc Morin.)
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Example
{class Program
{
[STAThread]static void Main(string[] args)
{var dialog = new OpenFileDialog();
// Add shortcuts to all locations open in Windows Explorer.
var paths = GetExplorerPaths();
ForEach(dialog.CustomPlaces.Add);
paths.
ShowDialog();
dialog.
}
// Returns a list of all locations open in Windows Explorer.
static List<string> GetExplorerPaths()
{var paths = new List<string>();
// Based on code by Luc Morin
// https://stackoverflow.com/a/20961047
// Use the Windows Shell to get a list of all Explorer locations.
null;
dynamic shell = try
{var t = Type.GetTypeFromProgID("Shell.Application");
CreateInstance(t);
shell = Activator.
// Enumerate all InternetExplorer shell objects. These include
// Internet Explorer and regular file Explorer.
// https://docs.microsoft.com/en-us/windows/win32/shell/shellwindows
var windows = shell.Windows();
for (int i = 0; i < windows.Count; i++)
{var explorer = windows.Item(i);
if (explorer == null)
{continue;
}
// Don't include locations from Internet Explorer.
var processFileName = Path.GetFileName((string)explorer.FullName);
if (0 == string.Compare(processFileName, "explorer.exe", true))
{// LocationURL is null if Explorer is showing "This PC".
if (!string.IsNullOrEmpty(explorer.LocationURL))
{var location = new Uri(explorer.LocationURL);
Add(location.LocalPath);
paths.
}
}
}
}finally
{FinalReleaseComObject(shell);
Marshal.
}// TODO: catch (Exception ex) { ... }
return paths;
}
} }
The result is an OpenFileDialog
with CustomPlaces
pointing to all location currently open in Window Explorer. This should help our users find the files they are looking for a little faster:
Next time your are implementing an OpenFileDialog
or a SaveFileDialog
consider employing this trick.
For C++ programmers there is a similar API under IFileDialog::AddPlace
. Partial example here.
In a future article we will explore the possibility to add this feature to all OpenFileDialog
and SaveFileDialog
regardless if we create them or not: