Introduction
In an effort to learn C#, I started to redevelop an application program that I had written in Java. I quickly realized, like many others, that there was no Most Recently Used functionality built into the .NET framework.
The MRUHandler
component displays a most recently used (MRU) menu list. Multiple MRUHanlder
components can be used in the same application without colliding. For example, the image above depicts the demo program supporting an MRU history for files and a MRU history for viewed URLs. As well, it will work within the context of a Multiple Document Interface (MDI) application.
Background
After searching CodeProject, I found two reference articles: Joe Woodury's Most Recently Used (MRU) Menu class in C# and Alex Farber's Add Most Recently Used Files (MRU) List to Windows Applications. Unfortunately, neither of these appealed to me, as I was looking for an approach that would allow me to "plug-in" the MRU functionality. After reading Philip Davis� article Extended Interface for Status Message, I decided to try developing this functionality as a custom Component.
MRUHandler Overview
Properties
The properties can only be set from the Designer or during the framework initialization. I was unsuccessful in finding a way to serialize the properties from the designer into the runtime, as you were able to do in Visual Basic 6.0. What I ended up doing was implementing the ISupportInitialize
interface. This interface allows the MRUHandler
to trap the start and end of the initialization and set an initializing flag. If anyone knows how to get the framework to do this, I would sure appreciate the input.
public void BeginInit() {_isInitializing = true;}
public void EndInit() {
_isInitializing = false;
if (!DesignMode) {
if (_mruItem == null) {
throw new Exception("The property 'MruItem' item cannot be set to null");
}
RebuildMenu();
}
}
Each of the properties then throws an exception if the _isInitializing
flag is not set or the component is not in Design mode.
public MenuItem MruItem {
get{ return _mruItem; }
set{
if (!DesignMode && !_isInitializing) {
throw new Exception("The 'MruItem' can be only set in Design Mode");
}
_mruItem = value;
}
}
Available Properties
DisplayLength
: The maximum length of the MRU entry file name when displayed.
MaxFiles
: The maximum number of files to be maintained in the MRU History.
MRUItem
: The menu item that is the place holder for the history list (when the style is Inline
) or the parent menu item (when the style is Popup
).
MRUStyle
: Enumerated type to indicate if the MRU history should be displayed inline or as a popup menu.
ShowShortItem
: Boolean value to indicate whether the display name should be compressed. If this is false
, then the DisplayLength
property is ignored.
StorageName
: Provides a name for the file or registry key that is used to persist the MRU history. If the StorageType
property is File
or IsolatedStorage
, the complete file name is generated using the product name (Application.ProductName
) and this property, and the extension of the file is .mru. If the StorageType
is Registry
, this property is used to create a subkey under the application user data registry (Application.UserAppDataRegistry
).
StorageStyle
: Enumerated property to determine the location of the persistent storage used to save the MRU history information. Values are:
IsolatedStorage
: The MRU history is stored using the .NET Isolated Storage facilities.
File
: The MRU history is stored on the file system in the executable path (Application.ExecutablePath
).
Registry
: The MRU history is stored using the Win32 Registry. The values are added to the key specified by the StorageName
property.
Public Methods
AddRecentlyUsedFile
:
- The maximum length of the MRU entry file name when displayed.
Events
MRUItemClicked
: The maximum length of the MRU entry file name when displayed.
Using the MRUHandler Component
Once the MRUHandler
is installed in Visual Studio, adding MRU support to your application is as simple as dragging the component on to the main window and choosing which menu item is to be used as the MRU history placeholder. It is a good idea to have named this menu item something meaningful in order to assist in finding it in the list.
Once you have chosen the menu item, then create the MRUItemClicked
handler.
private void OnMRUItemClicked(object sender,
MostRecentlyUsedHandler.MRUItemClickedEventArgs e) {
DoFileOpen(e.File);
}
The MRUItemClickedEventArgs
has a property File
that contains the path name of the file that corresponds to be opened from the MRU history. In the demo program, DoFileOpen
reads the file and opens the child window to display the contents.
The last step is to provide the mechanism that allows the application to tell the MRUHandler
which file paths to keep track of and display. This is done by calling the AddRecentlyUsedFile
method and passing the file path name that was just opened.
private void DoFileOpen(string fileName) {
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
Child form = new Child(fs);
form.MdiParent = this;
form.Show();
mruHandler1.AddRecentlyUsedFile(fileName);
}
Points of Interest
Setting the modifier of the DataSet
When persisting the MRU to the file system, I had used a DataSet
object to define an XML schema and do the actual transfer of information between the file system and the MRUHandler
. I found that the default implementation provided by Visual Studio sets the access modifier of the generated class to public
. The result was that MRUListFile
would show up on the Toolbar as a component. It took a while, but I was able to change the access modifier of the generated class by going to the class view and selecting the properties of MRUListFile.xsd. I was then able to change the modifier to internal
, which effectively hid this class in the designer.
Build Versioning
I found that each time I rebuilt the application that contains the MRUHandler
, the MRU history would be reset to empty when I was using a StorageType
with a value of Registry
. It turns out that the Application.UserAppDataRegistry
refers to an application subkey that contains the Visual Studio generated version number. If this is a problem, it would be quite simple to modify the code to remove the version number from the key.