Click here to Skip to main content
15,119,322 members
Articles / Desktop Programming / Windows Forms
Posted 11 Oct 2020

Tagged as


4 bookmarked

OpenMRUSuite - Flexible Controls for MRU Functionality in .NET Apps

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
11 Oct 2020MIT5 min read
An overview of logic and GUI controls for MRU functionality
This is a tutorial to show you the description of OpenMRUSuite and how to use it in your .NET app for providing MRU functionality on example of WinForm app.


Recently, I was faced with the task of adding MRU (Most Recently Used) functionality into WinForm app. And moreover, it had to be done in two ways: as conventional menu and as GUI control, similar to Visual Studio starting page. After some searching, I decided to create my own MRU project, bearing in mind flexibility and re-usability of its logic.

Image 1

So, let me introduce to you OpenMRUSuite – project for adding ‘MRU files’ functionality into .NET applications. The main feature of this project is great flexibility. For achieving this, the suite consists of two assemblies: OpenMRU.Core (responsible for ‘logic’ part, no dependencies on GUI frameworks) and OpenMRU.WinForm (GUI controls for WinForm apps, that uses OpenMRU.Core as ‘back-end’). All of them are available via NuGet. Also, you can check project on GitHub.

Using the Code

Quick Start

If you want to just use GUI controls without deep dive into details of realization, please take a look at the code snippet below. (Consider you have: OpenMru.Core and OpenMRU.WinForm libraries referenced, a form 'Form1', MRUItemsControl 'mruItemsControl1', Button 'ButtonOpen' and two ToolStripMenuItems 'recentFilesToolStripMenuItem' and 'recentcustomToolStripMenuItem' on it):

Image 2

public partial class Form1 : Form
    // link to manager.
    private readonly MRUManager manager;

    public Form1()
        // create IMRUItemStorage implementation using 
        // default xml-based storage from OpenMRU.Core
        MRUItemFileStorage storage = new MRUItemFileStorage("demo_mru_storage.xml");
        // create default manager from OpenMRU.Core
        manager = new MRUManager();
        // init manager with storage
        // subscribe to 'item selected' event
        manager.MRUItemSelected += Manager_MRUItemSelected;
        // init GUI control with created manager and default (eng) localization
        mruItemsControl1.Initialize(manager, new MRUGuiLocalization());

        // init menu items
        MRUItemsMenu itemsMenu = new MRUItemsMenu();
        itemsMenu.Initialize(manager, new MRUGuiLocalization());

        // init menu items - custom appearance
        MRUItemsMenu itemsMenu2 = new MRUItemsMenu();
        itemsMenu2.Initialize(manager, new MRUGuiLocalization());
                                "%FileName% - [%Path%] - [%AccessDate%]");

    private void Manager_MRUItemSelected(string path)
        MessageBox.Show("Select file from MRU: " + path);

    // handle open file event
    private void ButtonOpen_Click(object sender, EventArgs e)
        OpenFileDialog ofd = new OpenFileDialog();
        if (ofd.ShowDialog() == DialogResult.OK)
            string path = ofd.FileName;
            // add file that was selected by user to manager 
            // (begin track it and display on GUI)

As you can see from the code above, all your logic that should react on MRU item selection event or should add new file to MRU items, have to refer an IMRUManager implementation (in this example, it is MRUManager – default implementation from OpenMRU.Core). Thus, it makes your logic independent from GUI realization – just inject same IMRUManager instance, that was used for GUI initialization, and you are good to go!

If you are still here, let’s take a look at the suite in detail. :-)


OpenMRU.Core serves as a base (or some kind a ‘back-end’) for "MRU files" functionality. It contains interfaces and their default implementations to manage MRU items, interfaces for GUI part, logic that works with this GUI's interface and MRU item models. OpenMRU.Core separated into two logic parts ‘Common’ (contains interfaces/classes that do not deal with GUI at all) and ‘View’ (contains interfaces for ‘view’, logic for management of ‘view’, localization classes).

Common Part Provides Next Interfaces/ Classes

  • IMRUItemStorage - interface for the part that is responsible to read/write MRU items to persistent storage
  • IMRUManager – interface for MRU items manager. It is responsible for adding/removing MRU items to storage (so it has dependency on IMRUItemStorage), changing state of MRU items and notifying clients about changes in MRU items / MRU item selection. It is the key part of all suites.
  • MRUItemFileStorage – default implementation of IMRUItemStorage, that uses xml-file for storage of MRU items
  • MRUManger – default implementation of IMRUManager, depends on IMRUItemStorage implementation
  • MRUItem – model of MRU item
  • MRUItemsContainer – model of MRU items logic container. Container serves for grouping MRU items by last access date.

As you can notice at this point, you can provide your own realization of IMRUItemStorage (for example, your realization may use database for storing of MRU items), or even IMRUManager (if you want to provide extra functionality).

View Part Provides Next Interfaces / Classes

  • IMRUItemView – interface for single MRU item presentation (please see below)
  • IMRUItemsView – interface for MRUitems GUI control (please see below)
  • *Localization – classes that represent localization models. Each property of these classes store corresponding localized resource. By default, provided only English localization. For example, take a look on MRUMessages class (stores localized messages):
public class MRUMessages
    // Confirmation message for item deleting
    // Default value is [This action will delete item from list. Continue?]
    public string AllowDeleteItem { get; set; }

    // Confirmation message for items clearing
    // Default value is [This action will delete all items from list. Continue?]
    public string AllowClearList { get; set; }

    public MRUMessages()
        AllowDeleteItem = "This action will delete item from list. Continue?";
        AllowClearList = "This action will delete all items from list. Continue?";

By setting localized values to corresponding properties of localization class instance, you can provide own localization for MRU GUI controls! And you can use any localization datasource (.resx files, ini-files, etc.) for getting values for properties of localization class instance.

  • MRUGuiLogic – logic that drives ‘view’ of MRU items (implementation of IMRUItemsView). MRUGuiLogic class binds IMRUItemsView and IMRUManager implementations using given localization object. It handles MRU items selection/filtering/updating which makes it possible to have ‘views’ implementation very simple.

Let's take a look on ‘views’ interfaces:

public interface IMRUItemView
    event Action<string> PinItemRequested;
    event Action<string> DeleteItemRequested;
    event Action<string> ItemSelected;
    void Initialize(MRUItem item, MRUGuiItemLocalization localization);
    void Initialize(MRUItem item, MRUGuiItemLocalization localization,
                    string imagePathForItem);

public interface IMRUItemsView
    event Action ClearMRUItemsRequested;
    bool IsActionAllowed(string actionDescription);
    void Initialize(IMRUManager manager, MRUGuiLocalization localization);
    void ShowMRUItems(List<mruitemscontainer> containers);
    List<imruitemview> ItemViews { get; }

As you can see, it is quite simple requirements for ‘views’. IMRUItemView implementation should fire corresponding events when user tries to pin item, delete or select it. Also, should support initialization with given MRUItem model, localization and path to image. (i.e., use them as datasource for rendering corresponding visual elements). IMRUItemsView implementation should fire corresponding event when user tries to clear MRU items, should be able to ask user about certain action allowance (i.e., show dialog window for example), render MRU item containers and expose list of ‘item view’ as list of IMRUItemView.

Thus, you can create your own GUI controls that implements IMRUItemView and IMRUItemsView, focusing only on visual presentation leaving deal with logic on OpenMRU.Core.


Or, if you are developing WinForm app, you can use OpenMRU.WinForm – package that contains components for MRU items displaying: as WinForm GUI control (similar to one on Visual Studio start window).

Image 3

and as conventional menu. These controls implement IMRUItemView and IMRUItemsView and rely on OpenMRU.Core. Please see example of usage in Quick start section. Please note, that image for certain MRU item will be determined in next order: if image file specified by client of suite, than this image will be used, otherwise, system will try to fetch image from file, associated with MRU item. If all methods, mentioned above fails, than default image will be used (from embedded resource file).

For customizing of presentation of menu items, use next patterns:

// string that represent appearance
// available placeholders:
// %FileName% - just file name (w/o path)
// %Path% - path to file (excluding file name)
// %FullFileName% - path to file + file name
// %AccessDate% - last access date of MRU item
// default is: %FullFileName%

Image 4

Image 5

Points of Interest

Thank you for your attention! I hope that this suite will be useful for you!

Happy coding!



  • New features:
    • Get own images from MRU items
    • 'Clear all' menu item
    • Caption of 'last access date' group as menu separator


  • New features:
    • Menu-like MRU items control
    • Adjustable amount of MRU items to track
  • Fixed issues:
    • Layout re-building
    • Correct order of MRU items


  • New features:
    • MRU items filtration
    • Show date on MRU item control
    • Add grouping by date ranges
    • Add possibility of filtration by MRU file name


  • Initial version


This article, along with any associated source code and files, is licensed under The MIT License


About the Author

Dmitriy Sobeshchanskiy
Software Developer
Ukraine Ukraine
No Biography provided

Comments and Discussions

GeneralMy vote of 5 Pin
LightTempler12-Oct-20 11:18
MemberLightTempler12-Oct-20 11:18 
GeneralMy vote of 5 Pin
Franc Morales11-Oct-20 11:54
MemberFranc Morales11-Oct-20 11:54 
Easy 5. Thanks for sharing.
GeneralRe: My vote of 5 Pin
Dmitriy Sobeshchanskiy13-Oct-20 10:27
MemberDmitriy Sobeshchanskiy13-Oct-20 10:27 
QuestionFew things Pin
Издислав Издиславов11-Oct-20 11:15
MemberИздислав Издиславов11-Oct-20 11:15 
AnswerRe: Few things Pin
Dmitriy Sobeshchanskiy13-Oct-20 11:16
MemberDmitriy Sobeshchanskiy13-Oct-20 11:16 
GeneralRe: Few things Pin
Издислав Издиславов14-Oct-20 6:33
MemberИздислав Издиславов14-Oct-20 6:33 

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

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