I love what Excel has done to the list of recently opened files. It went from up to nine files to up to 50 files. You can pin certain files so they never leave the list. Every item on the list has a keyboard shortcut. But is it every good enough? No. No it’s not.
I have my options set to show 50 recent files, buy they don’t all fit on the screen. And 50 is a bit much. In fact, 25 (the default setting) can be hard to look through. All of the recent files have keyboard shortcuts and all of the push pins for the files have keyboard shortcuts. By the time you get to about the 20th file, the keyboard shortcut is YY2. It gets a little unwieldy, I think.
What if we could filter the list by typing in a search box? What if indeed.
And I filter on 201303 (that’s the third quarter of 2013 for you non-accountants).
Now that’s a list I can get my head around. Let’s see how it works. First I fill the listboxes based on the Application.RecentFiles collection.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
|
Public Sub FillFilesAndPlaces(Optional sFilter As String = vbNullString) Dim dcPlaces As Scripting.Dictionary Dim rf As RecentFile Dim sPlace As String Dim sFile As String Dim aFiles() As String Dim lCnt As Long Set dcPlaces = New Scripting.Dictionary ReDim aFiles(1 To 2, 1 To 1) For Each rf In Application.RecentFiles sFile = Dir(rf.Name) sPlace = Replace(rf.Path, Dir(rf.Path), vbNullString) 'If file isn't found, don't include it If Len(sFile) > 0 Then 'Put file names and paths in an array If UCase(sFile) Like "*" & UCase(sFilter) & "*" Then lCnt = lCnt + 1 ReDim Preserve aFiles(1 To 2, 1 To lCnt) aFiles(1, lCnt) = rf.Path aFiles(2, lCnt) = sFile End If 'Put unique paths in a dictionary If UCase(sPlace) Like "*" & UCase(sFilter) & "*" Then If Not dcPlaces.Exists(sPlace) Then dcPlaces.Add sPlace, sPlace End If End If End If Next rf 'If files match, put to listbox, otherwise clear If lCnt = 1 Then Me.lbxFiles.Clear Me.lbxFiles.AddItem aFiles(1, 1) Me.lbxFiles.List(Me.lbxFiles.ListCount - 1, 1) = aFiles(2, 1) ElseIf lCnt > 1 Then Me.lbxFiles.List = Application.WorksheetFunction.Transpose(aFiles) Else Me.lbxFiles.Clear End If 'Write keys array to places listbox Me.lbxPlaces.List = dcPlaces.Keys End Sub |
When the userform is initialized, this procedure is called without the argument. That ensures that every file and place gets picked. I loop through the RecentFiles collection and capture the file name (using Dir) and the folder. The file names and fullnames go into a 2d array. The paths go into a dictionary so I can get a unique list. I never used to use dictionaries because I didn’t like the reference dependency. But that’s just stupid because every computer I use will have Scripting Runtime installed. So now I’ve embraced them. If I could use For Each with a dictionary in a custom class module, I’d quit using collections altogether.
Recent files can have the same name, which causes a problem. I included a textbox at the bottom of the form so I could get the whole path. I didn’t want the whole path in the listbox because it looks too cluttered. When something is selected in the files listbox, the textbox gets updated with the full path and filename.
|
Private Sub lbxFiles_Change() If Me.lbxFiles.ListIndex > -1 Then Me.tbxFullname.Text = Me.lbxFiles.Value Me.lbxPlaces.ListIndex = -1 Else Me.tbxFullname.Text = vbNullString End If EnableOpen End Sub |
I’m using the Enter and Change events of the listbox to make sure that only a file or a place is selected and not both. I originally had a button for each listbox, but that seemed silly. Now I’m wondering if I should have files and places all in the same listbox.
The Open button’s Enabled property is updated based on what’s selected.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
Public Sub EnableOpen() Dim wb As Workbook Me.cmdOpen.Enabled = False If Me.lbxPlaces.ListIndex > -1 Then Me.cmdOpen.Enabled = True ElseIf Me.lbxFiles.ListIndex > -1 Then On Error Resume Next Set wb = Workbooks(Me.lbxFiles.Column(1)) On Error GoTo 0 If wb Is Nothing Then Me.cmdOpen.Enabled = True Else If wb.FullName = Me.lbxFiles.Value Then Me.cmdOpen.Enabled = True Else Me.tbxFullname.Text = Me.tbxFullname.Text & Space(1) & "(naming conflict)" End If End If End If End Sub |
I start with disabling the button. Then if a place is selected, it becomes enabled. Places is a unique list, so there’s no conflicts that can happen or any other error checking that’s needed. If a file is selected, it gets a bit more complicated. You can’t have two files with the same name open at the same time, even if they are in different folders. I try to set a variable to an open workbook, wrapped in On Error. If wb is Nothing, that means there are no open workbooks with that same name, so I enable the Open button. If wb is something, I check to see if the Fullname is the same. If it is, I enable Open, but in reality Open will just activate the workbook. Finally, if there’s an open workbook with the same Name, but a different Fullname, I keep Open disabled and put a message in the textbox. It’s not a great system, but it’s what I’ve got so far.
The only other code of interest is actually opening the file or place.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
Private Sub cmdOpen_Click() Dim wb As Workbook If Me.lbxFiles.ListIndex > -1 Then On Error Resume Next Set wb = Workbooks(Me.lbxFiles.Column(1)) On Error GoTo 0 If wb Is Nothing Then Workbooks.Open Me.lbxFiles.Value Else wb.Activate End If Else ChDrive Left$(Me.lbxPlaces.Value, 3) ChDir Me.lbxPlaces.Value Application.Dialogs(xlDialogOpen).Show End If Unload Me End Sub |
If it’s a file, I do the same workbook variable trick to see if it’s already open. If it is, I activate it. I don’t have to worry about it being a different file with the same name, because if that was a problem, Open would be disabled. If it’s not open, open it. If it is open, activate it.
If it’s a place, I change the drive, change the directory, and show the File Open dialog. I just know that ChDrive is going to screw up at some point, like if I have UNC path in there. But this is just a first draft, so I’ll deal with those problems later.
Right now, I press Alt+F+R and look down the recent file list. If I don’t see the file, I ESC out and press Ctrl+O to navigate the old fashioned way. I’m thinking that this userform should replace all of that. I would press Ctrl+O and this userform would show up. I would start typing and the list gets filtered. If the list is filtered, I would click Open and it lets me navigate to the file – kind of like selecting a Recent Place that happens to be the current directory. That means I have to keep Open enabled all of the time and prevent workbook name conflicts some other way. Alternatively, I could just add another button to the form to navigate. So Ctrl+O to open the form then Alt+N to skip the recent part and just navigate to the file.
Update
I’ve been using this for a week and, with a few minor exceptions, it’s worked wonderfully. I added the Navigate button to go directly to File Open. And I assigned Ctrl+O to open this form. It’s a little disconcerting at first – expecting the file open dialog and getting this userform – but I quickly got used to it. I’ve only had to ‘navigate’ once and that’s because I wasn’t thinking. The folder I wanted was in the recent places list, but I tend to be thinking about file names when searching, not folders.
You can download URecent.zip