Title | Provide an MRU list in VB .NET |
Description | This example shows how to provide an MRU list in VB .NET. It saves and loads most recently used file data in the Registry, and creates and deletes menu items as needed. |
Keywords | MRU, MRU list, VB.NET, most recently used file list |
Categories | VB.NET, Files and Directories, Software Engineering, Controls, Tips and Tricks |
|
|
This example demonstrates two important techniques.
This example builds its MRU list in a class. You could easily move it into a component so the developer could assign its properties at design time.
The class's constructor saves several values such as the application name, a reference to the File menu that will contain the MRU list, and the maximum number of entries to display in the list. It then calls LoadMruList to load the saved MRU data and DisplayMruList to display the list.
|
|
Public Sub New(ByVal application_name As String, ByVal _
file_menu As Menu, ByVal num_entries As Integer)
m_ApplicationName = application_name
m_FileMenu = file_menu
m_NumEntries = num_entries
m_FileNames = New Collection
m_MenuItems = New Collection
' Load saved file names from the Registry.
LoadMruList()
' Display the MRU list.
DisplayMruList()
End Sub
|
|
Subroutine LoadMruList uses GetSetting to get the names of files previously saved in the MRU list and it saves the names in the m_FileNames collection.
Subroutine SaveMruList removes any current entries and then uses SaveSetting to save the file names currently in the m_FileNames collection.
|
|
' Load previously saved file names from the Registry.
Private Sub LoadMruList()
Dim file_name As String
For i As Integer = 1 To m_NumEntries
' Get the next file name and title.
file_name = GetSetting(m_ApplicationName, _
"MruList", "FileName" & i, "")
' See if we got anything.
If file_name.Length > 0 Then
' Save this file name.
m_FileNames.Add(file_name, file_name)
End If
Next i
End Sub
' Save the MRU list into the Registry.
Private Sub SaveMruList()
' Remove previous entries.
If GetSetting(m_ApplicationName, "MruList", _
"FileName1", "").Length > 0 Then
DeleteSetting(m_ApplicationName, "MruList")
End If
' Make the new entries.
For i As Integer = 1 To m_FileNames.Count
SaveSetting(m_ApplicationName, _
"MruList", "FileName" & i, _
m_FileNames(i).ToString)
Next i
End Sub
|
|
Subroutine DisplayMruList starts by removing any previously created MRU menu items. References to those menu items are stored in the m_MenuItems collection.
If m_FileNames is not empty, then the routine creates a separator menu item at the end of the File menu. It then loops through the file names, making their menu entries and adding references to them to the m_MenuItems collection. When it creates the menu items, it gives each the MruItem_Click event handler.
|
|
' Display the MRU list.
Private Sub DisplayMruList()
' Remove old menu items from the File menu.
For Each mnu As MenuItem In m_MenuItems
m_FileMenu.MenuItems.Remove(mnu)
Next mnu
m_MenuItems = New Collection
' See if we have any file names.
If m_FileNames.Count > 0 Then
' Make the separator.
Dim mnu As New MenuItem
mnu.Text = "-"
m_MenuItems.Add(mnu)
m_FileMenu.MenuItems.Add(mnu)
' Make the other menu items.
For i As Integer = 1 To m_FileNames.Count
mnu = New MenuItem( _
"&" & i & " " & _
FileTitle(m_FileNames(i).ToString), _
New System.EventHandler(AddressOf _
MruItem_Click))
m_MenuItems.Add(mnu)
m_FileMenu.MenuItems.Add(mnu)
Next i
End If
End Sub
|
|
When the user clicks one of the MRU menu items, the MruItem_Click executes. The sender parameter is a reference to the control that raised the event. The prorgam casts this value into the MenuItem that the user clicked and then looks for a reference to the same control in the m_MenuItems collection. It uses the index of that item to find the corresponding file name and raises its OpenFile event. It adds 1 to the index in m_MenuItems to skip over the separator object stores in the first position.
|
|
' MRU menu item event handler.
Private Sub MruItem_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs)
Dim mnu As MenuItem = DirectCast(sender, MenuItem)
' Find the menu item that raised this event.
For i As Integer = 1 To m_FileNames.Count
' See if this is the item. (Add 1 for the
' separator.)
If m_MenuItems(i + 1) Is mnu Then
' This is the item. Raise the OpenFile
' event for its file name.
RaiseEvent OpenFile(m_FileNames(i).ToString)
Exit For
End If
Next i
End Sub
|
|
Subroutine Add adds a file name to the MRU list. It removes the file name if it is already in the list and then adds it at the beginning of the list. If the list contains too many items, the code removes the last item. It finishes by calling DisplayMruList to update the menu and SaveMruList to save the changes into the Registry.
The FileNameIndex helper function finds a file name's index in the m_FileNames collection.
|
|
' Add a file to the MRU list.
Public Sub Add(ByVal file_name As String)
' Remove this file from the MRU list
' if it is present.
Dim i As Integer = FileNameIndex(file_name)
If i > 0 Then m_FileNames.Remove(i)
' Add the item to the begining of the list.
If m_FileNames.Count > 0 Then
m_FileNames.Add(file_name, file_name, _
m_FileNames.Item(1))
Else
m_FileNames.Add(file_name, file_name)
End If
' If the list is too long, remove the last item.
If m_FileNames.Count > m_NumEntries Then
m_FileNames.Remove(m_NumEntries + 1)
End If
' Display the list.
DisplayMruList()
' Save the updated list.
SaveMruList()
End Sub
' Return the index of this file in the list.
Private Function FileNameIndex(ByVal file_name As String) _
As Integer
For i As Integer = 1 To m_FileNames.Count
If m_FileNames(i).ToString = file_name Then Return i
Next i
Return 0
End Function
|
|
Finally the Remove subroutine removes a file from the MRU list. It uses FileNameIndex to find the file name's index. If the file is present, the routine removes it, updates the menu items, and saves the changes.
|
|
' Remove a file from the MRU list.
Public Sub Remove(ByVal file_name As String)
' See if the file is present.
Dim i As Integer = FileNameIndex(file_name)
If i > 0 Then
' Remove the file.
m_FileNames.Remove(i)
' Display the list.
DisplayMruList()
' Save the updated list.
SaveMruList()
End If
End Sub
|
|
The main program declares an MruList object with the WithEvents keyword so it can catch the object's events.
|
|
Private WithEvents m_MruList As MruList
|
|
When the program opens a file or saves a file with a new name, it calls this object's Add method to add the file to the MRU list.
|
|
m_MruList.Add(m_FileName)
|
|
If the program tries and fails to open a file, it calls the MruList object's Remove method to remove it from the list.
|
|
m_MruList.Remove(file_name)
|
|
When the MruList object raises its OpenFile method, the program calls its LoadData subroutine, passing it the name of the file.
|
|
' Open a file selected from the MRU list.
Private Sub m_MruList_OpenFile(ByVal file_name As String) _
Handles m_MruList.OpenFile
LoadData(file_name)
End Sub
|
|
The main program uses the variable m_DataDirty to keep track of whether the data has been modified since it was last loaded or saved. Its TextBox's TextChanged event handler sets m_DataDirty to True, updates the form's caption, and enables the Save and Save As menu items.
|
|
' Mark the data as modified.
Private Sub rchFile_TextChanged(...) Handles _
rchFile.TextChanged
If Not m_DataDirty Then
Me.Text = APP_NAME & "*[" & FileTitle(m_FileName) & _
"]"
m_DataDirty = True
mnuFileSave.Enabled = True
mnuFileSaveAs.Enabled = True
End If
End Sub
|
|
Before the program loads a file, starts a new document, or closes, it calls subroutine DataSafe to see if it is safe to discard the current data. If m_DataDirty is False, then DataSafe simply returns True to indicate that the current data need not be saved.
If m_DataDirty is True, then DataSafe asks the user whether it should save the data. If the user clicks Cancel, then the user wants to cancel whatever operation is about to discard the current data. DataSafe returns False to indicate that it is not okay to discard the current data.
If the user clicks No, then the user does not want to save the changes. DataSafe returns True to indicate that it is okay to discard the data.
Finally, if the user clicks Yes, then DataSafe calls subroutine SaveData to try to save the data. SaveData may fail. For example, if the current data is new rather than loaded from a file, then SaveData will let the user select a file in which to save. If the user cancels that file selection, then the file is not saved. After the call to SaveData, the DataSafe function checks m_DataDirty to see if the save succeeded and returns an appropriate value.
|
|
' Return True if it is safe to discard the current data.
Private Function DataSafe() As Boolean
If Not m_DataDirty Then Return True
Select Case MessageBox.Show( _
"The data has been modified. Do you want to " & _
"save the changes?", _
"Save Changes?", MessageBoxButtons.YesNoCancel)
Case DialogResult.Cancel
' The user is canceling the operation.
' Don't discard the changes.
Return False
Case DialogResult.No
' The user wants to discard the changes.
Return True
Case DialogResult.Yes
' Try to save the data.
SaveData(m_FileName)
' See if the data was saved.
Return (Not m_DataDirty)
End Select
End Function
|
|
See the code for the details about how the LoadData and SaveData routines work. The last really interesting piece of code is the form's Closing event handler. It simply sets its e.Cancel parameter to Not DataSafe(). If the data is safe (either saved or the user wants to discard it), then e.Cancel is False so the form closes. If the data is not safe (there are unsaved changes), then e.Cancel is True so the program remains open.
|
|
' Don't close if the data's not safe.
Private Sub Form1_Closing(...) Handles MyBase.Closing
e.Cancel = Not DataSafe()
End Sub
|
|
|
|