Office 2013 VBA help files

Hi All,

Microsoft announced today that the Help files for Office 2013 VBA have been made available for download.
The help files are for:

Access
Excel
Office Shared
OneNote
Outlook
PowerPoint
Publisher
Word
Visio

Note that these files do not offer context sensitive help (F1), they are presented as stand-alone help files which you have to open manually.

I invite you all to give your opinions about VBA help. Make sure you include the Office version with your comments.

Writing Tests Instead of Comments

Jon commented

Unit tests are the best comments you can leave a developer or your future self.

Well, these aren’t unit test exactly, but they are tests. The question is, do they adequately document the code?

First, the code

And, the tests.

Recent Files and Places Search

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.

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.

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.

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.

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

Looping through PivotItems that Don’t Exist

I have some code that creates a journal entry import file based on a pivot table. It loops through the PivotItems and reads the DataRange.Value2 property. It’s nothing fancy or particularly complicated, but it gets the job done. Or it used to. You know that old problem with pivot items hanging around?. Yeah, that got me.

Unable to get the DataRange property of the PivotItem class

Once I realized that lCnt equaled 41 and I only had 40 GL Codes, I immediately knew the problem. But how to fix it. I checked the locals window to see if there was anything identifiable about the ghost pivot item.

It looks like when the RecordCount property is zero, it’s a pivot item that can be ignored. I considered code like this:

I also read Debra’s site on the matter and considering just setting the Retain Items to “None” at design time. The problem with that is that I may redo the pivot table or reuse the code on a different table and forget to set that property. I ended up setting that property in code. Instead of checking RecordCount in the loop, I just set the MissingItemsLimit property to xlMissingItemsNone before I refresh and it gets rid of any stray pivot items.

File Path and Name for Links

Unlike many people with whom I work, I almost never email a workbook. Instead, I save it to an appropriate place on the server and email the link. This seems to confuse the hell out of some people, but if I’m going to change the way things are done, it has to start with me and I have to take a hard line. But that’s not the point of this post. The point is that I need to get the path to these workbooks I want to email. I’m sure many of you remember this gem of a post from 2006 about getting the UNC path from the Web toolbar. That technique found its way into this utility:

That worked like a peach at my old job where mapped drives were the wild west. At my new job, it is guaranteed that everyone has certain mapped drives. In those cases, I don’t want to confuse them further by giving them a UNC path. So I send them the mapped drive path.

Works great most of the time. One of our mapped drive is considered an “internet location” by Excel. I really don’t know how are servers work around here, but I do know that when I open something from that particular mapped drive, I have to click the Enable Content banner at the top.

This is called Protected View and it’s a pain in the ass. When a workbook is in Protected View and is active, the ActiveWorkbook object is Nothing. The above code fails. The good news is that Application.ActiveProtectedViewWindow is Nothing if that’s not the case.

Do you want to know what else doesn’t work when Application.ActiveProtectedView is not Nothing? Application.Wait. That’s right, the Wait method fails with Method 'Wait' of object'_Application' failed. That’s just super. Anybody know what else fails in Protected View?

VBA Best Practices That I’ll Never Do

I don’t know if these are really “best practices” or if I’m just making them up. I know I heard them somewhere, but that doesn’t make them universal by any means. Also, I generally agree with all of these best practices, although I have no intention of changing my ways.

Best Practice:Never use lowercase ‘i’ or lowercase ‘l’ as variables.

Reasoning:Both look too much like the number 1 and makes the code very difficult to debug.

Justification:I’ve been bitten by the 1 vs. i problem myself. But i, j, k will always be my For Next control variables. I’ve accepted the fact that I’m never going to change that.


Best Practice:Don’t reuse variables

Reasoning:When you reuse variables, you have to be careful to reinitialize the variable and you won’t be as careful as you think. Also, how could you have a well-named variable that you use in two places?

Justification:If I’m coding two loops in a procedure, I reuse the control variable. I think that’s a reasonable exception because it’s a control variable and it will initialize itself in the loop. However, I’m probably also guilty of using a variable like lCnt in two places. That’s one of those generically named variables that should really identify what it’s counting. If it was lWorkbookCnt, then it would be a lot harder to justify reusing it later when counting worksheets.


Best Practice:Align data types in Dim statements. Like

Reasoning:The block of declared variables is easier to read.

Justification:I can’t imagine a worse fate than having to adjust all of my Dim statements when I add a new variable whose name is longer than the rest.


Best Practice:Declare each variable on its own line

Reasoning:It prevents the problem of not assigning a data type. A variable declaration like

is really declaring sFile as a Variant (because omitted data types are Variants) and sPath as a String. By giving each variable its own line, you are less likely to make that mistake.

Justification:Bah. I’m a careful programmer. Not unusually careful, just averagely careful, and I don’t make that mistake. The only people who make that mistake are people that don’t understand that you have to declare a data type for every variable on the same line. Once you know how it works, it’s just not a mistake you’re going to make. Most of my variables are on their own line, but closely related variables often end up sharing a line. In my code you’ll often see

(although let’s hope I don’t have three nested For Each loop too often)


Best Practice:Comment, comment, comment

Reasoning:You and everyone else that reads your code will find it easier to understand if you include comments.

Justification:Most comments suck. If 25% of your code lines are comments, you and everyone else that reads your code will skip over them anyway (the VBE makes them a different color so that it’s super easy to skip over them). This best practice stems from the fact that most people don’t comment enough. In reality, most people don’t write readable enough code, which is far superior to commenting. When I’m writing code to teach a beginner, I comment every line. But teaching is different. If you feel you need a comment, consider rewriting your code. If you consider it and still need a comment, you probably really need a comment.


Best Practice:Avoid Exit For and Exit Do in loops

Reasoning:Exit For and Exit Do are just like GoTo. And we all know that GoTo is the spawn of Satan.

Justification:I abhor GoTo so deeply that I want to be on the other side of this argument. But I can’t justify continuing a loop when I’ve found what I need. Here’s a crappy example of what I mean.

Of course I would never hardcode a string or have domain specific data in a variable name, but you get the point. The main differences between Exit For/Do and GoTo is that the Exit statements only flow in one direction (only forward, while GoTo can go forward or backward) and the Exit statement flow to a specific place (the line below Next or Loop, while GoTo can go wherever you choose).


Best Practice:Use named parameters

Reasoning:Named parameters make the code more readable and don’t enforce an order to the parameters, reducing bugs.

Justification:I hate named arguments. They just clutter everything up and make it ugly. I want my code to be pretty. I don’t absolutely value prettiness over readability, but I don’t absolutely discount it either. I don’t need to write code for a complete beginner to read (unless I know that’s my audience). If you are reading my code, you should have a pretty good working knowledge of the Find method and what it’s arguments are. If you’re reading this post and don’t have the exact order of the parameters memorized, you probably could figure it out by the constants used. And failing that, you could just look it up. Which is what you should do if you don’t know. Don’t write code for your non-programmer supervisor to read – he won’t get it even with named arguments and copious comments.

If there is a best practice you knowingly avoid, leave a comment. You don’t have to justify it. There is no justification for using ‘i’ as a variable, but I still do it.

Going Home with Frozen Panes

I like to split a worksheet and freeze the panes so I can see the row and column headings when I scroll. Like me, I’m sure you’ve done it a million times. In that state, the Ctrl+Home shortcut will take you to the intersection of the split panes rather than A1 (where it takes you if you don’t have frozen panes). Most of the time that’s just fine with me. Sometimes, however, I have some header information that drives the worksheet like a dropdown and I’d like Ctrl+Home to go to A1 – maybe because it’s closer to the dropdown or maybe because it just feels right.

I would rather go to A1 than C5. But really, I want to go to B2. In my UIHelpers.xlsm add-in (that’s a Personal Macro Workbook for you if you haven’t converted yours to an add-in yet), I put the following lines in the Auto_Open and Auto_Close procedures.

That hooks up the keystroke to a procedure called FrozenHome. I have a sneaking suspicion that hijacking Ctrl+Home is going to have some unintended consequences, but I can’t think of what they might be. The FrozenHome proc looks like this.

This will cause Ctrl+Home to toggle between the split cell and A1. If the selection is anywhere in the worksheet that’s not the split cell, it selects the split cell. If the selection is already at the split cell, then it select A1. Except that I had to fancy that part up a bit. If I have defined a range named rngHome, it will select that instead of A1. In the screen shot above I defined B2 as rngHome so that pressing Ctrl+Home twice will take me to the validation box.

Importing Fixed Width Text Files with ActiveX Data Objects

A few weeks ago I was struggling with fixed width text files. Remember 1995? Me neither, but I’m living it every day. I happened upon a stackoverflow.com post that dealt with using ADO for this purpose. I don’t remember the post, so no link. It did, however, lead me to this MSDN article, which was very helpful.

If you haven’t been following along, I wrote a post about creating a sample fixed-width file and one about importing said file. This post is about importing that file with ADO. You may remember ADO from such database objects as Connection, Command, and Recordset. But, like me, you may never have considered using to import text files; or even knew that it could.

Before I get into the specifics, there are two things that endeared me to this method. First, it allows me to only import the data I want and easily ignores headers, totals, and other non-record data. Second, it’s super fast. I had a huge text file that took several minutes to read using the Input$ function. I got it down to 90 seconds using ADO. Ninety seconds still stinks, but it beats having to get a coffee every time the code runs. The bonus third reason I love ADO is that I can replicate it for different text files easily. Usually, fixed width text files are not arranged as normalized data, so there are some challenges. But I went from setting this up for one specific report to a dozen reports very quickly. Let’s see how it’s done.

Go read the MSDN article if you want the specifics, but basically you need a file called Schema.ini that tells ADO what’s what in your file. I don’t take a crap without a class module, so we’ll be using class modules here. Didn’t this use to be a family friendly blog?

If you couldn’t tell, the text file in question contains transactions from a general ledger. That means I need a Transaction class to hold each of them. Using the column headers from the file, I create a CTransaction class module.

My VBHelpers add-in quickly converts those to properties and creates a CTransactions parent class. Next, I create an MEntryPoints standard module and insert the following code.

I don’t have a FillFromFile method or a WriteToRange method, but I like to write my main procedure as if I already had those. You’ll need a reference to Microsoft ActiveX Data Objects 2.8 Library (although any version close to that will do). The FillFromFile method is pretty simple. It creates and ADO Connection and an ADO Recordset, then loops through the recordset adding CTransaction instances as it reads them in. It’s treating our text file as if it’s a database with field names and everything.

We’ll hold off on how to create the Schema file for now. The Connection string is created with this little utility. You pass in the path and returns an array of strings ready to be joined.

I’ve recently starting using arrays and Joins to concatenate strings of any length. I find it makes the code much more readable and manageable once you get used to it. Let’s talk about that SQL statement. In Schema.ini, I’ve defined column names and column widths. We’ll look at it in a moment. The SQL statement selects all the columns from the text file based on some criteria. The first section of the SQL statement is the SELECT keyword. For the second section, I have a property that returns an array of columns. You could just as easily use “SELECT * FROM”, but I was recently shown the benefit of following the never-select-astrisk rule, so I’m trying to be good.

Just an array of column names used in the SQL statment and in Schema.ini. The third section of the SQL statement is the FROM keyword followed by the file name in brackets. The Dir$ function strips the path out of the fullname and returns only the file name. You don’t need the path in the SQL statement because Schema.ini is in the same directory as the text file. It has to be, so it’s not looking anywhere else.

The final section of the SQL statement is the WHERE clause. This is where you have to get a little creative. As I scan down my text file, I need to find some characteristic of “good” rows that is not present in “bad” rows. For this example, it was pretty easy. Every row that I want has a real date in PostDate and every row that I don’t want doesn’t. They aren’t all that easy. Would you like to see some examples of WHERE clauses I’ve used? Well, would you?

Some of those are pretty gnarly. Our example file has lots of options, not just PostDate. We have three Yes/No fields and we could use any of those. It’s pretty unlikely that header information or totals rows are going to have a Yes or No in that same position. The idea is only get the rows you want. For some of the dates I used "__/__/____" and I think it’s pretty safe. But for this example, I used "[0-1][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]" which is a little more specific.

With a good connection string and SQL statement, I open the connection, open the recordset, and start looping. There’s not much to filling the CTransaction class via the FillFromRecordset method. It uses an Nz function that I wrote to avoid Null problems and return a default. For numbers and dates, I specify that I want to return a zero in place of a Null. For strings, it automatically returns vbNullString. The FillFromRecordset procedure is a method in CTransaction. The Nz function is in a standard module.

Now on to Schema.ini, at long last. I have a MakeSchema procedure in a standard module that simply creates the file where it’s supposed to. One of the arguments to MakeSchema is a string for the contents of the file. That comes from the Schema property of the CTranscations class (shown as Me.Schema in the FillFromFile method above). The Schema property takes the columns from the Columns property and puts them together with column widths to create the string.

The widths array is simply how many characters wide each column is. The lines in my file are 132 characters long. Counting them is a pain. Usually, I grab a couple of representative lines from the text file and put them in a spreadsheet. Here’s how I came up with the column widths for this file.

Lines 2-5 are Courier New 9pt and the first two lines are typed – no fancy formula to get those numbers. I like to get a couple of representative lines so I don’t miss anything. Then I go put pipes where I want the column breaks to be and put this formula in A7

=FIND("|",$A$4,A6+1)

and fill down until I get an error. Column B is just the difference. Finally, the MakeSchema utility takes that string and puts into a file.

And the Schema.ini file looks like this:

The final piece is writing all of the CTransaction objects to a range. The CTransactions collection class has a WriteToRange method that calls an OutputRange property. It’s pretty straightforward.

I put apostrophes in front of the strings so they don’t get converted just because they look like a number or a date. And when I’m done, I sum up the Debit column and compare it to the text file.

It’s a match! I must have done something right. No comments for you accountants about how my debits don’t equal my credits. You try to make fake data this internally consistent. :)

You can download FixedWidthADO.zip