Build a most recently used file (MRU) list in C#


An MRU list displays the files that a program has used most recently in a menu. If the user selects a file from the list, the program reopens that file.

This example builds an MruList class that a program can use to make providing an MRU list easier. This example is fairly involved but many of its pieces are interesting so I’m going to describe them all, divided into the following sections.


Initialization


The following shows variables that the class uses to maintain its list.

// The application's name.
private string ApplicationName;
// A list of the files.
private int NumFiles;
private List FileInfos;
// The File menu.
private ToolStripMenuItem MyMenu;
// The menu items we use to display files.
private ToolStripSeparator Separator;
private ToolStripMenuItem[] MenuItems;
// Raised when the user selects a file from the MRU list.
public delegate void FileSelectedEventHandler(FileInfo file_info);
public event FileSelectedEventHandler FileSelected;

The ApplicationName variable holds the application’s name. This is used to store file information in the Registry.

The NumFiles variable holds the number of files that the list can hold, not the number of files that it is actually holding at the time. Most programs set this value to 4 so the MRU list can hold at most 4 files.

The FileInfos list contains FileInfo objects describing the files currently in the list. (I use FileInfo objects because they easily store a file’s full path and short file name. You could use some other type of object instead if you want the MRU list to represent something other than files. For example, if you want a list of recently used engineering diagrams, you might want this list to hold objects that store the diagrams’ titles, file locations, and other data.

The MyMenu variable holds a reference to the program’s menu that should display the list. The Separator and MenuItems variables hold references to the menu items created by the MruList.

The FileSelectedEventHandler delegate defines the type of the FileSelected event that the MruList raises when the user selects a file from the list.

The following code shows the class’s constructor, which initializes the list.

// Constructor.
public MruList(string application_name, ToolStripMenuItem menu,
 int num_files) { ApplicationName = application_name; MyMenu = menu; NumFiles = num_files; FileInfos = new List(); // Make a separator. Separator = new ToolStripSeparator(); Separator.Visible = false; MyMenu.DropDownItems.Add(Separator); // Make the menu items we may later need. MenuItems = new ToolStripMenuItem[NumFiles + 1]; for (int i = 0; i < NumFiles; i++) { MenuItems[i] = new ToolStripMenuItem(); MenuItems[i].Visible = false; MyMenu.DropDownItems.Add(MenuItems[i]); } // Reload items from the registry. LoadFiles(); // Display the items. ShowFiles(); }

The constructor saves the application’s name, the menu that should hold the list items, and the number of files that the list can hold. It then creates a new FileInfos list.

Next the constructor creates the separator and menu items that it might need to display later. It finishes by calling LoadFiles to load saved files from the Registry and ShowFiles to display the correct menu items.

The following code shows the LoadFiles method.

// Load saved items from the Registry.
private void LoadFiles()
{
// Reload items from the registry.
for (int i = 0; i < NumFiles; i++)
{
string file_name = (string)RegistryTools.GetSetting(
ApplicationName, "FilePath" + i.ToString(), "");
if (file_name != "")
{
FileInfos.Add(new FileInfo(file_name));
}
}
}

The LoadFiles method uses the RegistryTools class’s GetSetting method to load file names stored in the Registry. The following code shows the GetSetting method.

// Get a value.
public static object GetSetting(string app_name, string name, object default_value)
{
RegistryKey reg_key = Registry.CurrentUser.OpenSubKey("Software", true);
RegistryKey sub_key = reg_key.CreateSubKey(app_name);
return sub_key.GetValue(name, default_value);
}

The GetSetting method opens the current user’s Software hive, gets a subkey named after the application, and then gets the desired setting, which in this case is a file path.

After the constructor has loaded saved file information from the Registry, it calls the following ShowFiles method to prepare the menu items for use.

// Display the files in the menu items.
private void ShowFiles()
{
Separator.Visible = (FileInfos.Count > 0);
for (int i = 0; i < FileInfos.Count; i++)
{
MenuItems[i].Text = string.Format("&{0} {1}", i + 1, FileInfos[i].Name);
MenuItems[i].Visible = true;
MenuItems[i].Tag = FileInfos[i];
MenuItems[i].Click -= File_Click;
MenuItems[i].Click += File_Click;
}
for (int i = FileInfos.Count; i < NumFiles; i++)
{
MenuItems[i].Visible = false;
MenuItems[i].Click -= File_Click;
}
// Update the Registry.
SaveFiles();
}

The ShowFiles method starts by displaying the separator if there are any files in the list. It then loops through the FileInfo objects in the FileInfos collection. For each object, it sets the corresponding menu item’s Text, Visible, and Tag properties. It also attaches the File_Click event handler to the menu item’s Click event. (It first removes any previously installed event handler so the event handler isn’t installed twice.)

The code then hides any menu items that are not needed.

The following code shows the SaveFiles method that saves the current file list in the Registry.

// Save the current items in the Registry.
private void SaveFiles()
{
// Delete the saved entries.
for (int i = 0; i < NumFiles; i++)
{
RegistryTools.DeleteSetting(ApplicationName, "FilePath" + i.ToString());
}
// Save the current entries.
int index = 0;
foreach (FileInfo file_info in FileInfos)
{
RegistryTools.SaveSetting(ApplicationName,
"FilePath" + index.ToString(), file_info.FullName);
index++;
}
}

The SaveFiles method deletes any existing Registry entries and then saves the current files’ full paths.


Adding and Removing Files


When the main program opens a file or saves a newly created file, it should call the MruLists’s AddFile method shown in the following code.

// Add a file to the list, rearranging if necessary.
public void AddFile(string file_name)
{
// Remove the file from the list.
RemoveFileInfo(file_name);
// Add the file to the beginning of the list.
FileInfos.Insert(0, new FileInfo(file_name));
// If we have too many items, remove the last one.
if (FileInfos.Count > NumFiles) FileInfos.RemoveAt(NumFiles);
// Display the files.
ShowFiles();
// Update the Registry.
SaveFiles();
}

This method calls the RemoveFileInfo method to remove the file from the FileInfos list and then inserts the file at the beginning of the list. This prevents the list from containing duplicates. Next if the list contains too many files, the code removes the last item. The method finishes by calling ShowFiles to update the menu items and SaveFiles to save the current list in the Registry.

The following code shows the RemoveFileInfo method.

// Remove a file's info from the list.
private void RemoveFileInfo(string file_name)
{
// Remove occurrences of the file's information from the list.
for (int i = FileInfos.Count - 1; i >= 0; i--)
{
if (FileInfos[i].FullName == file_name) FileInfos.RemoveAt(i);
}
}

This method loops through the file entries from back to front. If it finds an entry with the target file name, it removes that entry. (The code searches the list from back to front so removing an item doesn’t renumber the remaining items.) Note that the code cannot simply use the list’s Remove method because that method only looks for matching FileInfo objects not objects that are different but that represent the same file.

The RemoveFile method shown in the following code removes a file from the MRU list. The main program should call this method whenever it needs to remove a file from the list. For example, if the program tries to open a file and fails, most programs remove that file from the MRU list.

// Remove a file from the list, rearranging if necessary.
public void RemoveFile(string file_name)
{
// Remove the file from the list.
RemoveFileInfo(file_name);
// Display the files.
ShowFiles();
// Update the Registry.
SaveFiles();
}

This method calls RemoveFileInfo to remove the file from the list. It then calls ShowFiles to update the menu items and SaveFiles to update the Registry.


Handling Events


When the user selects a file from the MRU list, the following File_Click event handler executes.

// The user selected a file from the menu.
private void File_Click(object sender, EventArgs e)
{
// Don't bother if no one wants to catch the event.
if (FileSelected != null)
{
// Get the corresponding FileInfo object.
ToolStripMenuItem menu_item = sender as ToolStripMenuItem;
FileInfo file_info = menu_item.Tag as FileInfo;
// Raise the event.
FileSelected(file_info.FullName);
}
}

This method simply raises the FileSelected event, passing it the FileInfo object representing the selected file.


Using MruList


The example program uses the following code to define and initialize its MruList variable.

// The MruList.
MruList MyMruList;
// Make the MruList.
private void Form1_Load(object sender, EventArgs e)
{
MyMruList = new MruList("howto_mru_list", mnuFile, 4);
MyMruList.FileSelected += MyMruList_FileSelected;
}

The program defines the variable MyMruList at the form level so all of its methods can use it. The form’s Load event handler initializes the MruList and registers the MyMruList_FileSelected event handler to handle the list’s FileSelected event.

The following code shows how the program tries to open a file.

// Open a file and add it to the MRU list.
private void OpenFile(string file_name)
{
try
{
// Load the file.
rchFile.Clear();
if (file_name.ToLower().EndsWith(".rtf"))
{
rchFile.LoadFile(file_name);
}
else
{
rchFile.Text = File.ReadAllText(file_name);
}
// Add the file to the MRU list.
MyMruList.AddFile(file_name);
}
catch (Exception ex)
{
// Remove the file from the MRU list.
MyMruList.RemoveFile(file_name);
// Tell the user what happened.
MessageBox.Show(ex.Message);
}
}

If the file’s name ends with .rtf, the program uses its RichTextBox control’s LoadFile method to load the file as an RTF file. If the name doesn’t end in .rtf, the program loads the file as text. If it successfully loads the file, the program calls the MruList’s AddFile method to add the file to the MRU list.

If the program fails to load the file, it calls the MruList’s RemoveFile method to remove the file from the MRU list, and it tells the user that there was a problem.

If the user selects a file from the MRU list, the MruList raises its FileSelected event and the following event handler executes.

// Open a file selected from the MRU list.
private void MyMruList_FileSelected(string file_name)
{
OpenFile(file_name);
}

This code simply calls the previous OpenFile method to open the selected file.

This program doesn’t have Save or Save As commands, but if it did it should call the MruList’s AddFile method whenever it is saving a new or renamed file.

   

This entry was posted in files, programs. Bookmark the permalink.

2 Responses to Build a most recently used file (MRU) list in C#

  1. V Hellin says:

    Thanks for the code.
    Don’t you forget a parameter of type string called application_name for the constructor ?

  2. Rod Stephens says:

    Sorry about that. I’m not sure how that declaration got removed from the text. It is in the downloadable example.

Leave a Reply

Your email address will not be published. Required fields are marked *