
Introduction
The Most Recently Used (MRU) files list is standard feature of most Windows applications.
This article describes how to add MRU support to Windows application using the class
MRUManager
. This class is included in the demo project. It may be added
to your C# project or compiled in DLL and used by developers working with any .NET
language. While writing this class I tried to reproduce MFC's CRecentFileList
class behaviour.
Using the MRUManager Class
The starting point is a simple image viewer application which has File - Open menu
and shows an image file in the form's client area. When user selects the Open menu item, the program
shows the Open File dialog:
private void mnuFileOpen_Click(object sender, System.EventArgs e)
{
OpenFileDialog openDlg = new OpenFileDialog();
openDlg.Filter = "Jpeg files (*.jpg)|*.jpg|Bitmap files (*.bmp)|*.bmp|All Files (*.*)|*.*";
openDlg.FileName = "" ;
openDlg.CheckFileExists = true;
openDlg.CheckPathExists = true;
if ( openDlg.ShowDialog() != DialogResult.OK )
return;
OpenFile(openDlg.FileName);
}
The method OpenFile
loads the image file into the class member
Bitmap bitmap
, and the Paint
event handler shows
this bitmap on the screen:
private Bitmap bitmap;
private void OpenFile(string fileName)
{
Bitmap bmp;
try
{
bmp = (Bitmap)Bitmap.FromFile(fileName, false);
if ( bmp != null )
{
bitmap = (Bitmap) bmp.Clone();
this.AutoScroll = true;
this.AutoScrollMinSize = new Size (bitmap.Width, bitmap.Height);
Invalidate();
}
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "Error loading from file");
}
}
private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
if ( bitmap != null )
{
e.Graphics.DrawImage(
bitmap,
new Rectangle(this.AutoScrollPosition.X,
this.AutoScrollPosition.Y,
bitmap.Width,
bitmap.Height));
}
}
To add MRU support to this application, we need to take the following steps:
1. Add a Recent Files menu item to the application menu:
In the demo project this item is called mnuFileMRU
.
2. Implement IMRUClient
interface in the form. This interface is defined
in the same file as MRUManager
:
public interface IMRUClient
{
void OpenMRUFile(string fileName);
}
Implementation:
public class Form1 : System.Windows.Forms.Form, IMRUClient
{
public void OpenMRUFile(string fileName)
{
OpenFile(fileName);
}
}
3. Add an MRUManager
member to the form class and initialize it:
private MRUManager mruManager;
private void Form1_Load(object sender, System.EventArgs e)
{
mruManager = new MRUManager();
mruManager.Initialize(this, mnuFileMRU, registryPath);
}
MRUManager
is initialized with a reference to the owner form, Recent Files
menu item and registry path to store the MRU. If the registry path is, for example,
"Software\MyCompany\MyProgram", the MRU list is stored in the
HKEY_CURRENT_USER\Software\MyCompany\MyProgram\MRU
registry entry.
Additional properties (CurrentDir
, MaxMRULength
,
MaxDisplayNameLength
) may be optionally changed.
CurrentDir
- the default value is the program's current directory at the time
when the Initialize
method is called. Files in this directory are
shown in the menu without a path.
MaxMRULength
- the default value is 10. This is the maximum number of files stored
in MRU list.
MaxDisplayNameLength
- the default value is 40. Long file names are
truncated to this length in the menu.
4. Call MRUManager.Add()
and Remove
methods when necessary:
private void OpenFile(string fileName)
{
Bitmap bmp;
try
{
mruManager.Add(fileName);
}
catch (Exception ex)
{
mruManager.Remove(fileName);
}
}
Add
is called when file is open successfully. Remove
is called when Open method failed.
The program is ready. Run it and see how it works. When the program runs the first time
and the MRU list is empty, the Recent Files menu item is disabled. When the file is opened,
its name is added to the MRU list. If a user opens the file which is already in the MRU
list, the file name is moved to the first position.
The MRUManager Class Implementation
The class has the following members:
private Form ownerForm;
private MenuItem menuItemMRU;
private MenuItem menuItemParent;
private string registryPath;
private int maxNumberOfFiles = 10;
private int maxDisplayLength = 40;
private string currentDirectory;
private ArrayList mruList;
private const string regEntryName = "file";
The Initialize
method fills class members and subscribes to the Recent Files
parent's Popup
and owner form's Closing
events:
public void Initialize(Form owner, MenuItem mruItem, string regPath)
{
ownerForm = owner;
if ( ! ( owner is IMRUClient ) )
{
throw new Exception("MRUManager: Owner form doesn't implement IMRUClient interface");
}
menuItemMRU = mruItem;
try
{
menuItemParent = (MenuItem) menuItemMRU.Parent;
}
catch
{
}
if ( menuItemParent == null )
{
throw new Exception("MRUManager: Cannot find parent of MRU menu item");
}
registryPath = regPath;
if ( registryPath.EndsWith("\\") )
registryPath += "MRU";
else
registryPath += "\\MRU";
// keep current directory in the time of initialization
currentDirectory = Directory.GetCurrentDirectory();
// subscribe to MRU parent Popup event
menuItemParent.Popup += new EventHandler(this.OnMRUParentPopup);
// subscribe to owner form Closing event
ownerForm.Closing += new System.ComponentModel.CancelEventHandler(OnOwnerClosing);
// load MRU list from Registry
LoadMRU();
}
The LoadMRU
method reads the MRU list from registry. The OnOwnerClosing
method is called when the owner form is closed and saves the MRU list to the registry.
OnMRUParentPopup
is called when the Recent Files menu item parent is opened.
This method fills the MRU menu list:
private void OnMRUParentPopup(object sender, EventArgs e)
{
if ( menuItemMRU.IsParent )
menuItemMRU.MenuItems.Clear();
if ( mruList.Count == 0 )
{
menuItemMRU.Enabled = false;
return;
}
menuItemMRU.Enabled = true;
MenuItem item;
IEnumerator myEnumerator = mruList.GetEnumerator();
while ( myEnumerator.MoveNext() )
{
item = new MenuItem(GetDisplayName((string)myEnumerator.Current));
item.Click += new EventHandler(this.OnMRUClicked);
menuItemMRU.MenuItems.Add(item);
}
}
The GetDisplayName
method gets the short file name from the full file name.
If the file is in the current directory, it is shown without a path. The file name is also
truncated to the maximum allowed length using the Shell API's PathCompactPathEx
function (thanks to CodeProject and GotDotNet C# expert Richard Deeming for this
solution).
The OnMRUClicked
method is called when the user clicks selects one of the MRU
menu items. It calls the owner form's OpenMRUFile
method:
private void OnMRUClicked(object sender, EventArgs e)
{
string s;
try
{
MenuItem item = (MenuItem) sender;
if ( item != null )
{
s = (string)mruList[item.Index];
if ( s.Length > 0 )
{
((IMRUClient)ownerForm).OpenMRUFile(s);
}
}
}
catch ( Exception ex )
{
Trace.WriteLine("Exception in OnMRUClicked: " + ex.Message);
}
}