Introduction
I've written applications in the past in which users need to open files and have found that they usually re-open the same files over and over. So instead of forcing them to browse and open the same file over and over again, I decided to create an MRUManager
, to make things easier.
Background
It uses the Windows Registry to store the paths of the recently opened files, so if you're not familiar with the registry, Wikipedia would be a good place to start. As a reference, use Microsoft's documentation of the C# class Microsoft.Win32.Registry if you're unsure about the registry manipulation methods.
Using the Code
You can use the MRUManager
class right away (at your own risk, of course) without having to modify the code. There are only a few requirements.
void myOwnRecentFileGotClicked_handler(void obj, EventArgs evt)
This method will be called when the user clicks on one of the recent items.
- Using Visual Studio, create a
ToolStripMenuItem
to be used as the parent menu item of the recent files list. Don't put sub-menu items in this one as they will be removed by the MRUManager
. - Create a method with this prototype.
- Optional: create a method that has the same parameters as the previous one. This will be called after a user clicks 'Clear list'.
Once you have those, simply create a new instance of the MRUManager
class, like so:
private MRUManager mruManager;
private void Form1_Load(object sender, EventArgs e)
{
this.mruManager = new MRUManager(
this.recentFilesToolStripMenuItem,
"myProgram",
this.myOwn_recentFileGotClicked_handler,
this.myOwn_recentFilesGotCleared_handler);
}
Afterwards, there are two public
methods that can be called:
public void AddRecentFile(string fileNameWithFullPath)
public void RemoveRecentFile(string fileNameWithFullPath)
Examples of their usage:
private void openToolStripMenuItem_Click(object obj, EventArgs evt)
{
FileDialog openFileDlg = new OpenFileDialog();
openFileDlg.InitialDirectory = Environment.CurrentDirectory;
if(openFileDlg.ShowDialog() != DialogResult.OK)
return;
string openedFile = openFileDlg.FileName;
this.mruManager.AddRecentFile(openedFile);
MessageBox.Show("Through the 'Open' menu item, you opened: " + openedFile);
}
private void myOwn_recentFileGotClicked_handler(object obj, EventArgs evt)
{
string fName = (obj as ToolStripItem).Text;
if (!File.Exists(fName))
{
if (MessageBox.Show(string.Format("{0} doesn't exist. Remove from recent " +
"workspaces?", fName), "File not found",
MessageBoxButtons.YesNo) == DialogResult.Yes)
this.mruManager.RemoveRecentFile(fName);
return;
}
MessageBox.Show(string.Format("Through the 'Recent Files' menu item, you opened: {0}", fName));
}
- If a user opens (or even saves) a file
- When the user clicks on a recent file, but it doesn't exist
Inside the Class
Below is an overall view of the class:
public class MRUManager
{
private string NameOfProgram;
private string SubKeyName;
private ToolStripMenuItem ParentMenuItem;
private Action<object, EventArgs> OnRecentFileClick;
private Action<object, EventArgs> OnClearRecentFilesClick;
private void _onClearRecentFiles_Click(object obj, EventArgs evt)
private void _refreshRecentFilesMenu()
public void AddRecentFile(string fileNameWithFullPath)
public void RemoveRecentFile(string fileNameWithFullPath)
public MRUManager(
ToolStripMenuItem parentMenuItem,
string nameOfProgram,
Action<object, EventArgs> onRecentFileClick,
Action<object, EventArgs> onClearRecentFilesClick = null
)
}
There are two private
methods that do some of the work for the class: _refreshRecentFilesMenu()
and _onClearRecentFiles_Click()
.
When a new MRUManager
object is instantiated, the constructor checks for invalid parameters. If there are any, it throws a new ArgumentException
. It then calls _refreshRecentFilesMenu()
to update the list, in case there were any entries previously stored in the registry.
The registry key that the class stores recent files under is "HKEY_CURRENT_USER\SOFTWARE\{program name that you supplied}\MRU". It stores this string
in a private
member, SubKeyName
.
Whenever you call AddRecentFile()
, it creates a new value under that key. Value names are numerical and go from 0
to as many as you add. It then calls _refreshRecentFilesMenu()
.
Whenever you call RemoveRecentFile()
, it searches for a value under SubKeyName
that contains the file name that you pass in and deletes it. It then calls _refreshRecentFilesMenu()
.
_refreshRecentFilesMenu()
calls {ToolStripMenuItem that you supplied}.DropDownItems.Clear()
and then repopulates it with all the values in the registry. Each recent file menu item that it adds gets passed an EventHandler
that points to the function that you supplied. It then adds two items: a separator and 'Clear list' menu item, which, when clicked, runs the private
method _onClearRecentFiles_Click()
.
_onClearRecentFiles_Click()
does three things: clears the registry of recent files, clears the menu item and calls the function that the user may have optionally specified.
Simplified versions of _refreshRecentFilesMenu()
and _onClearRecentFiles_Click()
are shown below:
private void _refreshRecentFilesMenu()
{
string s;
ToolStripItem tSI;
RegistryKey rK = Registry.CurrentUser.OpenSubKey(this.SubKeyName, false);
this.ParentMenuItem.DropDownItems.Clear();
string[] valueNames = rK.GetValueNames();
foreach (string valueName in valueNames)
{
s = rK.GetValue(valueName, null) as string;
if (s == null)
continue;
tSI = this.ParentMenuItem.DropDownItems.Add(s);
tSI.Click += new EventHandler(this.OnRecentFileClick);
}
if (this.ParentMenuItem.DropDownItems.Count == 0)
{
this.ParentMenuItem.Enabled = false;
return;
}
this.ParentMenuItem.DropDownItems.Add("-");
tSI = this.ParentMenuItem.DropDownItems.Add("Clear list");
tSI.Click += new EventHandler(this._onClearRecentFiles_Click);
this.ParentMenuItem.Enabled = true;
}
private void _onClearRecentFiles_Click_SIMPLIFIED(object obj, EventArgs evt)
{
RegistryKey rK = Registry.CurrentUser.OpenSubKey(this.SubKeyName, true);
if (rK == null)
return;
string[] values = rK.GetValueNames();
foreach (string valueName in values)
rK.DeleteValue(valueName, true);
rK.Close();
this.ParentMenuItem.DropDownItems.Clear();
this.ParentMenuItem.Enabled = false;
if (OnClearRecentFilesClick != null)
this.OnClearRecentFilesClick(obj, evt);
}
Deviations from the Original Article
Although the MRUManager
class in this article was written from scratch, it bears a resemblance to the original article's implementation. For example, it still uses the registry to store file paths and the GUI interface it creates is much like the original. I decided to make my MRUManager
more minimalistic, by not including things like 'maxNumberOfFiles
' or 'maxDisplayLength
', yet still functional enough to use right away.
History
- June 19, 2012 - Article created
- June 20, 2012 - Article changed as an alternative
- November 20, 2012 - Bug fix:
RemoveRecentFile()
not properly removing files from list