Introduction
For a project I work on, I decided I needed the possibility to open recent files. (Also called a MRU or Most Recently Used.) I first searched for it on CodeProject but I seemed to be the first ;).
So I thought of the following requirements. I needed a menu item (called Recent
) with the recently opened files as sub menu-items. Persistence is not needed in my case because I use my own setting manager. (Perhaps a later article.) My menu-item should signal the change in the MRU list so I could persist it myself. Clicking by the user should also be signaled. Of course, I have to tell the menu item when a file is actually opened, so the list can be updated. I don't need it yet (so it is not in there) but when a file does not exist anymore, it should be removed from the list.
Crafting the Class
I started with MenuItem
as base class and a private
string
array for the four items.
public class MRUMenuItem : MenuItem {
string[] mru=new string[4];
I needed access to this list (to be able to persist it) so I added a property.
public string[] MRUFiles {
get {return mru;}
}
Next, I need initialization. It is not done in the creator
method because I want to integrate with Visual Studio (VS). When I want to use this MRUMenuItem
, I start off by adding a normal MenuItem
and I change the generated declaration and initialization to my own menu item. This is not changed by VS, but when you add extra creator parameters, they are removed.
The parameters are saved in the string
array and for every string
that is not "", a menu item is added. This menu item is added as a sub menu-item. Also, an event handler is added to handle the clicks by the user.
public void Initialize(string file1, string file2,
string file3, string file4) {
mru[0]=file1;
mru[1]=file2;
mru[2]=file3;
mru[3]=file4;
for (int i= 0;i<4;i++) {
if (""!=mru[i]) {
MenuItem mmru = new MenuItem(mru[i],
new EventHandler(OnMRUClick));
this.MenuItems.Add(mmru);
}
}
}
The FileOpened
method takes care of all changes. First, I search for the string
, to see if it is in the list. If so, it should be removed and reinserted at the first position. If it is not in the list, the last item is removed. So, after the first four lines shown below, found
has the index to remove. The next two lines take care of the moving up of the item to remove and insert the new item at the first position. Then, the new sub menu set is built. Finally, if an event handler is attached by the class-user, the MRUChanged
event is fired.
public void FileOpened(string file) {
int found=3;
for (int j= 0;j<4;j++) {
if (file== mru[j]) {
found=j;
break;
}
}
while (found>0) mru[found]= mru[--found];
mru[0]=file;
this.MenuItems.Clear();
for (int i= 0;i<4;i++) {
if (""!=mru[i]) {
MenuItem mmru = new MenuItem(mru[i],
new EventHandler(OnMRUClick));
this.MenuItems.Add(mmru);
}
}
if (MRUChanged != null) {
MRUChanged(this, new EventArgs());
}
}
The class furthermore holds the definition of the events.
Using the Code
First, you create a new instance of the class. The easiest way to do that is to use VS as explained before. Then you initialize it with four string
s and attach the event handlers.
MRUMenuItem mRecent=new MRUMenuItem();
this.mMain.MenuItems.Add(mRecent);
mRecent.Initialize("File 1", "File 2", "File 3", "File 4");
mRecent.MRUClicked+=new EventHandler(MRUClick);
mRecent.MRUChanged+=new EventHandler(MRUChanged);
The two event handlers take care of the clicks and the changes. Be sure to use FileOpened
.
private void MRUClick(object sender, System.EventArgs e) {
MenuItem mSender = sender as MenuItem;
MRUMenuItem mParent = mSender.Parent as MRUMenuItem;
mParent.FileOpened(mSender.Text);
}
private void MRUChanged(object sender, System.EventArgs e) {
MRUMenuItem m=sender as MRUMenuItem;
}
Finally
I think this shows how you can extend the functionality of the .NET framework. The only thing left is a tighter integration in Visual Studio. I would like to use the MRUMenuItem
in VS like the normal MenuItem
. Initialize could be changed then in an event that can be chosen from the property box, along with MRUClick
and MRUChanged
. Any help and comment is much appreciated!
Changes
Marc Clifton mentioned the better option to have a variable amount of files. I changed the code as follows:
string[] mru;
int _count;
public void Initialize(string[] files) {
mru=files;
_count=files.Length;
Usage of the Initialize
becomes something like this:
mRecent.Initialize(new string[] {"File 1", "File 2", "File 3", "File 4"});
But other sizes are now also possible.
License
This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.
A list of licenses authors might use can be found here.