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.
Introduction
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.
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):
public partial class Form1 : Form
{
private readonly MRUManager manager;
public Form1()
{
InitializeComponent();
MRUItemFileStorage storage = new MRUItemFileStorage("demo_mru_storage.xml");
manager = new MRUManager();
manager.Initialize(storage);
manager.MRUItemSelected += Manager_MRUItemSelected;
mruItemsControl1.Initialize(manager, new MRUGuiLocalization());
MRUItemsMenu itemsMenu = new MRUItemsMenu();
itemsMenu.Initialize(manager, new MRUGuiLocalization());
itemsMenu.AttachToMenu(recentFilesToolStripMenuItem);
MRUItemsMenu itemsMenu2 = new MRUItemsMenu();
itemsMenu2.Initialize(manager, new MRUGuiLocalization());
itemsMenu2.AttachToMenu(recentcustomToolStripMenuItem,
"%FileName% - [%Path%] - [%AccessDate%]");
}
private void Manager_MRUItemSelected(string path)
{
MessageBox.Show("Select file from MRU: " + path);
}
private void ButtonOpen_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
if (ofd.ShowDialog() == DialogResult.OK)
{
string path = ofd.FileName;
manager.AddFile(path);
}
}
}
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
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
{
public string AllowDeleteItem { get; set; }
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
.
OpenMRU.WinForm
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).
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:
Points of Interest
Thank you for your attention! I hope that this suite will be useful for you!
Happy coding!
History
1.3.0
- New features:
- Get own images from MRU items
- 'Clear all' menu item
- Caption of 'last access date' group as menu separator
1.2.0
- New features:
- Menu-like MRU items control
- Adjustable amount of MRU items to track
- Fixed issues:
- Layout re-building
- Correct order of MRU items
1.1.0
- New features:
- MRU items filtration
- Show date on MRU item control
- Add grouping by date ranges
- Add possibility of filtration by MRU file name
1.0.0
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.