Le réseau pour les développeurs >
Forums - Accueil
>
Visual Basic General
>
How to get a "Recent" menu option in my application
How to get a "Recent" menu option in my application
- Hi,This may have a simple answer but here goes....I am developing a desktop application with a "File" menu option. In it there is an Open/Close/Exit etc. I would also like a "Recent" option (similar to visual studios "Recent Projects" option), that when you hover over it, it shows me the last 4 or 5 files opened within the application. How should i implement this?Thanks in advance for any advice!
Rob McCabe
Réponses
- Hi Rob,
there's tons of samples on the web regarding "MRU" (= Most Recently Used) lists. I.e. this one . If that doesn't help, try google with >>vb.net mru<<
Cheers,
Olaf- Marqué comme réponseRob McCabe vendredi 30 octobre 2009 22:50
- Proposé comme réponseRudedog2 vendredi 30 octobre 2009 16:59
Toutes les réponses
- Hi Rob,
there's tons of samples on the web regarding "MRU" (= Most Recently Used) lists. I.e. this one . If that doesn't help, try google with >>vb.net mru<<
Cheers,
Olaf- Marqué comme réponseRob McCabe vendredi 30 octobre 2009 22:50
- Proposé comme réponseRudedog2 vendredi 30 octobre 2009 16:59
- every time the user open a file, save the name of the file in string(5) you can have five strings to save the first five files opened or open latter. And asign it to your Recent.
something like :
Recent=str(string1)....+ str(Srting2)
something like that.
Don't judge me, just Upgrade me. Thanks!
Create a project Property Setting with the type "StringCollection".
Give it the name "MostRecentlyUsedFiles" or something.
Mark the best replies as answers. "Fooling computers since 1971."- Thanks for the reply guys. A string collection wouldnt do, i was looking for something that would last when the application has been shut down. I guess the registry is one place to store the values. I was also thinking of my app.config as well. But was wondering if there was any nice easy built in ways. Thanks for the help guys!
Rob McCabe - Hi Rob,
app.config is not the place to store user-defined settings, but rather user.config, since this is a per-user setting as opposed to a setting for every user that uses your application. In order to define a setting that is defined per user, you can use the Settings-dialog of your project (to see this dialog, double-click the My Project node in your SolutionExplorer and go to the Settings tab). Here, you can also set the scope for each setting that you define. If you set this to Application , your setting will be placed in app.config , meaning this is a setting that applies to everyone that runs your app. However, you can also set this to User , in which case the setting is saved in user.config , meaning each user has her/his personal setting . FWIW - you can find the user.config, i.e. under WinXP, here: [SysDrive]:\Documents and Settings\[User]\Local Settings\Application Data\[CompanyName]\[EXE-name]._Url_[key]\[version]\
Now, if you want to store your MRU-list in user.config, there again is tons of possible approaches. First of all, a MRU-list actually needs to consider two aspects: not only does it need the filename in question, but there also is an order of appearance associated with the items (i.e. which one has been used last). Hence, you'll need to use a collection that can not only store the filenames, but also a key that allows for ordering the list accordingly so that the "youngest" is the first in your list (or menu). Also, you probably don't want to fiddle around with replacing the order or replacing/moving the items in your collection yourself.
That said, you'll probably use a Collections.Specialized.OrderedDictionary or a Collections.SortedList, or (you name it). Taking a Collections.SortedList, for instance, you could use the date/time at which a file was added to your MRU list, using a KeyValuePair(of date, string) for each item, in which case the date could be used for sorting the collection from youngest to oldest date. These collections can be used in your settings-file; to create such a setting, choose Browse... from the Type-combobox for a setting, then navigate to i.e. System.Collections.SortedList ). However, you'll quickly notice that, even though you can actually define and use such a setting, saving it will not work (even though it won't trigger a runtime error - it'll just stay empty), that is, you won't be able to save values from your code out of the box. The reason for this is that VS has no information on how to serialize your collection in order to save it as XML. In order to do this "the right way", you'd have to write your own TypeConverter .
KISS, you might say, so here's something easy enough so it doesn't get too lengthy and assuming that, as noted before, your MRU-list is to contain a timestamp plus the filename for each entry:
Go ahead and define a setting named "MRU". As for the Type, choose System.Collections.Specialized.StringCollection (we'll be storing plain strings, doing our own conversion), for Scope , choose User . You can confirm that you got it right by going to some code-window and entering My.Settings. there - IntelliSense should give you the MRU -setting. Now, somewhere in your code (probably the form that assembles the MRU-MenuItems), paste the following two functions:
Public Sub MRU_SaveList(ByVal colMRU As Collections.SortedList) Dim colValues As New System.Collections.Specialized.StringCollection For intCounter As Integer = 0 To (colMRU.Count - 1) colValues.Add( _ colMRU.GetKey(intCounter).ToString & _ "|" & _ colMRU.GetByIndex(intCounter).ToString _ ) Next My.Settings.MRU = colValues My.Settings.Save() End Sub Public Function MRU_GetList() As SortedList Dim colMRU As New Collections.SortedList(My.Settings.MRU.Count) For Each strItem As String In My.Settings.MRU colMRU.Add( _ Date.Parse(Split(strItem, "|"c)(0)), _ Split(strItem, "|"c)(1) _ ) Next Return colMRU End Function
This gives you two functions that, instead of creating and applying a converter-class to your setting, handle conversions directly from your code. MRU_GetList will give you a list of the MRU files and MRU_SaveList will persist the collection passed to it in your settings-file.
With this in place, you can use code like ...
Dim col As Collections.SortedList = MRU_GetList() For intCounter As Integer = (col.Count - 1) To 0 Step -1 Dim strFilename As String = col.GetByIndex(intCounter) 'Create and add a menu item here ... Next
... when you are creating i.e. your menu-items. Likewise, you can save the list by calling MRU_SaveList with your collection. You'll probably need something that'll help you to easily add items to the list at runtime (i.e. when a file is opened) and maybe another setting to have something like a "number of recently used files to store" setting plus some code to simply remove all items beyond that number.
With the above, this should be fairly easy though, so I'll leave that up to you.
Cheers,
Olaf Thanks for the reply guys. A string collection wouldnt do, i was looking for something that would last when the application has been shut down. I guess the registry is one place to store the values. I was also thinking of my app.config as well. But was wondering if there was any nice easy built in ways. Thanks for the help guys!
Rob McCabe
Rob,
Don't like a string collection, huh. Think it through.
That object is pre-defined for Properties.Settings. It can be found in the drop-down list for "Type" in the Designer. Simply add the strings in the order of files being opened, and maintain an updated list. More on how to do that in a minute. Save the collectioin to user settings by changing the "Scope" setting from "Application" to "User".
As far as maintaining an 'ordered' list, I would suggest the Queue class. Use an instance of the Queue class to maintain the MRU during the lifetime of the application, not the user.setting string collection. A Queue collection represents what is known as a "first-in, first-out" behavior. That refers to how objects are added and removed from the collection. Think of a tube that can hold a fixed number of balls. Balls are added at one end, and removed from the other.
Once the limit is reached on how many 'balls'---or objects---that the 'queue' can hold, as new objects are added on the one end, older objects are dropped out of the other end and disposed. The queue collection will always hold the the most recently added objects, in the order in which they were added.
When your form or application closes, that would be the time to call the Dequeue() method to remove string objects from the collection and write the to user.setting collection.
Hope this helps.
Rudy =8^D
Mark the best replies as answers. "Fooling computers since 1971."- Hi guys,Thanks again for the advice. I had never really used user settings before, so this was new to me. I found the arraylist sufficed for what i needed but thanks for the different approaches offered.I have one last set of questions with regards MRU. Now i've defined my user scoped setting, in code i have to reference it like this:
My.Settings.Default.MRU
This uses the Default property. Does this matter?I found that when I load my application, if i dont have this piece of code i get an exception "Object reference not set to an instance of an object.":If My.Settings.Default.MRU Is Nothing Then My.Settings.Default.MRU = New ArrayList End If
Also, although i have not built an install version of my application, i find when debugging, every time I re-run my application i find its lost the settings from last time. I save my settings when i add them with the following code:Also, i get aMy.Settings.Default.MRU.Add(pNewFile) My.Settings.Default.Save()
Any ideas of what im doing wrong?
Rob McCabe- ModifiéRob McCabe mercredi 4 novembre 2009 12:17Spacing issue
- Hi Rob,
actually you raise a good point there - I don't have much of an idea what the My.Settings.Default.* is good for. I presume that it might be used if you have several sets of My.Settings and define a default set, but that's just my personal assumption - I haven't ever used it.
Regarding the values in a (single) set of settings, these can also provide default values (i.e. My.Settings.MRU.Default) which uses a predefined value, if set. For a MRU-list, this obviously doesn't make much sense as, presumably, there won't be any recently opened file when the app is started for the very first time. :-)
That said, you should be skipping the .Default in your expressions and use My.Settings.MRU, so the answer to ...
... is "yes!". Without the .Default, it should be working.This uses the Default property. Does this matter?
Cheers,
Olaf - For some reason the only options intellisense gives me for the My.Settings are:Default (shared property)Equals (shared method)ReferenceEquals (shared method)Synchronized (shared method)and have to go into the intellisense of default to find the MRU property i defined.
Rob McCabe - Create an additional setting that keeps track of how many items the user wants in their MRU list.
Initialize it to 0. The first time the program executes in the user's enviroment set it to a different number if you wish.
Give the user the option to change it within a fixed range. A range of 0 - 16?
Mark the best replies as answers. "Fooling computers since 1971." For some reason the only options intellisense gives me for the My.Settings are:
Default (shared property)Equals (shared method)ReferenceEquals (shared method)Synchronized (shared method)and have to go into the intellisense of default to find the MRU property i defined.
Rob McCabe
If you look at the code behind a Settings.settings file (Settings.Designer.vb) you'll discover that the whole concept is moderately convoluted. The instance structure runs like this:
Partial Class MySettings INHERITS ApplicationSettingsBase
-> Shared defaultInstance As MySettings which creates a new MySettings on Instantiation
-> Shared Property Default As MySettings which returns defaultInstance
So MySettings is the class, its primary variable is a shared instance of itself, and its primary accessor property is Default which returns a Shared instance of itself. What this means is that when you type MySettings you're referencing a non-instance Class and when you type MySettings.Default you're referencing an Instance of the Class that's created when you call the property; the Instance itself (in this case MySettings.Default) is what you really want/need.
Because of this mess (and some additional messy behaviors exhibited by Visual Studio and built-in Settings stuff, like the fact that you the programmer can't get any amount or kind of control over where the files get parked when a six year old could tell you they ought to be in the same directory as the application) I've written a basic XML-Serializing settings class that actually works in a way a sane person would expect and can understand without a few dozen hours of trial-and-error combined with high-decibel expulsion of profanity (the instance of the Settings class you reference actually IS the instance of the Settings class you create, and you can define your own storage locations). If you're interested in that, you can find the code here .
It never hurts to try. In a worst case scenario, you'll learn from it.- Andrew is correct.
The convoluted appearance of the designer generated code follows what is known as the Singleton Pattern .
The Singleton Pattern allows only one instance of a class to be created.
It can even do so across multiple threads.
Rudy =8^D
Mark the best replies as answers. "Fooling computers since 1971." - That actually makes a lot more sense than just doing it that way for the sake of doing it that way, but there still seems to be little or no good reason to do it that way when an individual Class Instance can be constructed some way that actually makes sense to a low-to-moderately experienced human and then passed ByRef from a Parent to a Child.
It never hurts to try. In a worst case scenario, you'll learn from it.
Correction, I don't see anything in there to make it thread safe.
The implementation of the Singleton Design Pattern by the Wizard/Designer will ensure that any thread that references the loaded assembly should always get the same instance. If another copy of the assembly is loaded, then another instance would exist for just that loaded assembly. But, there are no mechanisms to ensure that two threads are not attempting to modify the object at the same time.
The generated code is not what I would call a best practice.
Singleton's should always ensure thread safety.
All it takes is 5-6 more lines of code.
Mark the best replies as answers. "Fooling computers since 1971."The implementation of the Singleton Design Pattern by the Wizard/Designer will ensure that any thread that references the loaded assembly should always get the same instance. If another copy of the assembly is loaded, then another instance would exist for just that loaded assembly.
Not sure if I follow this. Are you saying that loading two instances of the same assembly uses the same Singleton Instance, or generates its own Singleton Instance just for that assembly?
Because if the former, there's no such thing as any software that's capable of this without some kind of direct API communication, in which case the second assembly is still working with its own instances and just passing parameter data back and forth to the first which modifies its own instances based on the I/O stream.
If the latter, then declaring any variable at the highest-possible inheritance level or declaring the variable outside the inheritance structure would do the exact same thing without the convolution and unreadable declarations.
As for Thread Safety, I call it a myth. There's no such thing as a Thread Safe Object; there is such a thing as a Thread Safe Reference.
It never hurts to try. In a worst case scenario, you'll learn from it.- The latter case.
Each loaded assembly would have its' own instance.
The comment was not directed specifically towards you.
Some folks can get confused on stuff like that.
Didn't want anyone newcomer to think that anytime the assembly is loaded, the same instance is returned no matter what.
Mark the best replies as answers. "Fooling computers since 1971." The latter case.
Each loaded assembly would have its' own instance.
The comment was not directed specifically towards you.
Some folks can get confused on stuff like that.
Didn't want anyone newcomer to think that anytime the assembly is loaded, the same instance is returned no matter what.
Mark the best replies as answers. "Fooling computers since 1971."
I was actually confused, and trying to get clarification for myself.
It never hurts to try. In a worst case scenario, you'll learn from it.- Ok, thanks for that guys.So that makes sense that they use a singleton for this as only one instance is needed throughout the application lifetime but seeing as i need the settings i save to survive after my application is closed and reopened, im wondering if this settings class now seems like the right choice for me?Maybe I should just stick to the old reliable app.config instead of the settings.settings file? (Settings.settings was automatically generated when i created my setting by right clicking on the project in visual studio, going to properties and then settings)
Rob McCabe - Hi Rob,
despite what Andy and Rudy have written about the behind-the-curtain stuff :-P, your true problem IMHO is that your setting isn't defined right. Would it be possible that you actually added a new settings-class instead of using the predefined one (this could be the reason for the problem you're experiencing)?
To check, open the My Project node in your SolutionExplorer. There, you should see a file called Settings.settings . Double-click that file in order to bring up the settings-editor. Do you see your MRU-setting in there? This dialog is actually the one that you get when you double click the My Project node and click the Settings tab. (Note that you can't have both the file and the settings-tab be opened at the same time.)
If MRU is defined in there, you really should be able to use it like My.Settings.MRU and, also, IntelliSense should let you choose it ...
Cheers,
Olaf Ok, thanks for that guys.
So that makes sense that they use a singleton for this as only one instance is needed throughout the application lifetime but seeing as i need the settings i save to survive after my application is closed and reopened, im wondering if this settings class now seems like the right choice for me?Maybe I should just stick to the old reliable app.config instead of the settings.settings file? (Settings.settings was automatically generated when i created my setting by right clicking on the project in visual studio, going to properties and then settings)
Rob McCabe
Hi, Rob. Sorry for the derailment.
The Settings.Settings file actually does persist over multiple runs, because when you call the .Save method the Settings object serializes its variables and writes its data to an XML File on your hard drive, then when you call the .Load method it loads them back up. So you can ship your product with a set of pre-defined defaults and you can offer a simple UI for users to reconfigure the application, and then when they exit (or you call .Save explicitly) it writes back to disk for next time.
I think the problem here was that Rudy and I got a little carried away with what Olaf calls the 'behind-the-curtain' stuff, which it seems like you don't have enough experience with Settings to use yet. So Settings is in fact what you want to be using for now; after you've worked with Settings awhile (and especially if/when you start working with Settings designed to suit more specific purposes for individual objects or types), you're going to start noticing some things about it... all this stuff might come in handy for you at that point.
It never hurts to try. In a worst case scenario, you'll learn from it.

