tags:

views:

73

answers:

3

What is good practice and good code hygiene? Putting code in Modules or Sheets?

I have this Excel Workbook, with user interfaces in each sheet. Each sheet within the workbook does a different part of some overall task. Should I place the code relevant to each sheet inside the Sheet objects, or in Modules? Group into one module, or separate modules?

I'm using Excel 2003.

+5  A: 

Definitely in Modules.

  • Sheets can be deleted, copied and moved with surprising results.
  • You can't call code in sheet "code-behind" from other modules without fully qualifying the reference. This will lead to coupling of the sheet and the code in other modules/sheets.
  • Modules can be exported and imported into other workbooks, and put under version control
  • Code in split logically into modules (data access, utilities, spreadsheet formatting etc.) can be reused as units, and are easier to manage if your macros get large.

Since the tooling is so poor in primitive systems such as Excel VBA, best practices, obsessive code hygiene and religious following of conventions are important, especially if you're trying to do anything remotely complex with it.

This article explains the intended usages of different types of code containers. It doesn't qualify why these distinctions should be made, but I believe most developers trying to develop serious applications on the Excel platform follow them.

There's also a list of VBA coding conventions I've found helpful, although they're not directly related to Excel VBA. Please ignore the crazy naming conventions they have on that site, it's all crazy hungarian.

fencliff
Hungarian notation as implemented in the Reddick Naming Convention has more or less been the standard for variable naming for Visual Basic for Applications and VB6. While I would certainly avoid its use in .NET where the tooling is very strong, it doesn't hurt to use it in VBA, where the tooling is older.
Ben McCormack
@Ben - "Crazy hungarian" was said tongue-in-cheek :) While hungarian does help with lack of static typing and the tooling around "what's this again", I think it falls short when you go more towards object-oriented VBA and start creating your own domain objects.My biggest complain, however, is that hungarian really messes with the flow of actually *reading* code. Each to their own though, not a religious point for me. But if someone asks, I won't recommend it.
fencliff
@fen Good point. I agree that when constructing classes in VBA, hungarian can make your objects look ugly. Maybe I still use it in VB6 to remind myself that I'm working in VB6 :-).
Ben McCormack
+2  A: 

In my experience it's best to put as much code as you can into well-named modules, and only put as much code as you need to into the actual worksheet objects.

Example: Any code that uses worksheet events like Worksheet_SelectionChange or Worksheet_Calculate.

Michael
That's my general policy aswell.
Lunatik
+3  A: 

I would suggest separating your code based on the functionality and purpose specific to each sheet or module. In this manner, you would only put code relative to a sheet's UI inside the sheet's module and only put code related to modules in respective modules. Also, use separate modules to encapsulate code that is shared or reused among several different sheets.

For example, let's say you multiple sheets that are responsible for displaying data from a database in a special way. What kinds of functionality do we have in this situation? We have functionality related to each specific sheet, tasks related to getting data from the database, and tasks related to populating a sheet with data. In this case, I might start with a module for the data access, a module for populating a sheet with data, and within each sheet I'd have code for accessing code in those modules.

It might be laid out like this.

Module: DataAccess:

Function GetData(strTableName As String, strCondition1 As String) As Recordset
    'Code Related to getting data from the database'
End Function

Module: PopulateSheet:

Sub PopulateASheet(wsSheet As Worksheet, rs As Recordset)
    'Code to populate a worksheet '
End Function

Sheet: Sheet1 Code:

Sub GetDataAndPopulate()
    'Sample Code'
     Dim rs As New Recordset
     Dim ws As Worksheet
     Dim strParam As String
     Set ws = ActiveSheet
     strParam = ws.Range("A1").Value

     Set rs = GetData("Orders",strParam)

     PopulateASheet ws, rs
End Sub

Sub Button1_Click()
    Call GetDataAndPopulate
End Sub
Ben McCormack