Click here to Skip to main content
Click here to Skip to main content

Add Most Recently Used Files (MRU) List to Windows Applications

, 6 Feb 2003
Rate this:
Please Sign up or sign in to vote.
Provides an implementation for and teaches you how to add a list of most recently used files to your Windows Forms applications.

Sample Image

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:


Sample Image

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);

    // Optional:
    // mruManager.CurrentDir = ".....";        // default is current directory
    // mruManager.MaxMRULength = ...;          // default 10
    // mruMamager.MaxDisplayNameLength = ...;  // default 40
}

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
    {
        // ...

        // add successfully opened file to MRU list
        mruManager.Add(fileName);
    }
    catch (Exception ex)
    {
        // remove file from MRU list
        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;                 // owner form
private MenuItem menuItemMRU;           // Recent Files menu item
private MenuItem menuItemParent;        // Recent Files menu item parent
private string registryPath;            // Registry path to keep MRU list
private int maxNumberOfFiles = 10;      // maximum number of files in MRU list
private int maxDisplayLength = 40;      // maximum length of file name for display
private string currentDirectory;        // current directory
private ArrayList mruList;              // MRU list (file names)
private const string regEntryName = "file";  // entry name to keep MRU (file0, file1...)

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;

    // check if owner form implements IMRUClient interface
    if ( ! ( owner is IMRUClient ) )
    {
        throw new Exception("MRUManager: Owner form doesn't implement IMRUClient interface");
    }

    // keep reference to MRU menu item
    menuItemMRU = mruItem;

    // keep reference to MRU menu item parent
    try
    {
        menuItemParent = (MenuItem) menuItemMRU.Parent;
    }
    catch
    {
    }

    if ( menuItemParent == null )
    {
        throw new Exception("MRUManager: Cannot find parent of MRU menu item");
    }

    // keep Registry path adding MRU key to it
    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)
{
    // remove all childs
    if ( menuItemMRU.IsParent )
        menuItemMRU.MenuItems.Clear();
 
    // Disable menu item if MRU list is empty
    if ( mruList.Count == 0 )
    {
        menuItemMRU.Enabled = false;
        return;
    }

    // enable menu item and add child items
    menuItemMRU.Enabled = true;

    MenuItem item;
    IEnumerator myEnumerator = mruList.GetEnumerator();

    while ( myEnumerator.MoveNext() )
    {
        item = new MenuItem(GetDisplayName((string)myEnumerator.Current));

        // subscribe to item's Click event
        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];

            // call owner's OpenMRUFile function
            if ( s.Length > 0 )
            {
                ((IMRUClient)ownerForm).OpenMRUFile(s);
            }
        }
    }
    catch ( Exception ex )
    {
        Trace.WriteLine("Exception in OnMRUClicked: " + ex.Message);
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Alex Fr
Software Developer
Israel Israel
No Biography provided

Comments and Discussions

 
Questionlast accessed files (not from MRU or Open File Dialog)? PinmemberSwab.Jat3-Jan-14 14:43 
GeneralMy vote of 3 Pinmemberashish_ips0719-Jun-12 23:11 
QuestionAre the MRUs registered in a way that Windows can recognize and work with? PinmemberDannyStaten25-Apr-11 17:05 
AnswerRe: Are the MRUs registered in a way that Windows can recognize and work with? PinmemberAlex Fr26-Apr-11 7:17 
GeneralThank You! PinmemberMyCodeWorks1-Dec-09 9:30 
GeneralPathCompactPathEx replacement PinmemberMatthew Pilgrim8-Sep-09 23:21 
QuestionAbout license Pinmemberkimikazu12-Aug-07 21:28 
GeneralCleaning MRU from registry Pinmembergeff_chang4-Jun-07 23:24 
QuestionFind all applications? PinmemberGerhard200718-Mar-06 8:16 
General.NET 2.0 compatiblity Pinmembergdmang28-Dec-05 7:59 
AnswerRe: .NET 2.0 compatiblity Pinmembergdmang28-Dec-05 10:38 
GeneralRe: .NET 2.0 compatiblity Pinmemberwatterman12-Apr-07 20:23 
GeneralRe: .NET 2.0 compatiblity PinmemberSteve_Harris13-May-08 4:48 
GeneralRe: .NET 2.0 compatiblity PinmemberLPlateDeveloper15-Aug-08 22:10 
GeneralRe: .NET 2.0 compatiblity PinmemberAndy Quay20-Jan-10 7:58 
GeneralRe: .NET 2.0 compatiblity PinmemberProshuto23-Sep-12 2:52 
GeneralTrouble with MDI Child Forms and Merged Menus PinmemberCraig Eddy7-Oct-03 4:03 
GeneralClass Library PinmemberJerry Maguire7-Feb-03 5:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140827.1 | Last Updated 7 Feb 2003
Article Copyright 2003 by Alex Fr
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid