Today I received 10 copies of Excel 2007 Power Programming With VBA.

Six copies are up for grabs. Just tell me what you’ll trade for an autographed copy. To keep things simple, post your item-for-trade offer at my blog.

Skip to content
# Daily Dose of Excel

## Haphazardly Posted Excel Information and Other Stuff

# Author: John Walkenbach

# Still More Free Stuff (Almost)

# Excel MVP Web Sites

# Excel 2007 Likeability

# Musical Excel

# Dynamic Sorting With a UDF

# Beta Testers For PUP v7?

# Modifying Shapes (and Charts) With UDFs

# Weekend Music Post

Today I received 10 copies of Excel 2007 Power Programming With VBA.

Six copies are up for grabs. Just tell me what you’ll trade for an autographed copy. To keep things simple, post your item-for-trade offer at my blog.

Just for fun, here’s a list of Excel MVPs who have a Web site.

- Rob Bovey – Application Professionals
- Stephen Bullen – Office Automation
- Fernando Cinquegrani – Pagine Personali Office (Italian)
- Debra Dalgleish – Contextures
- Ron de Bruin – Ron’s Excel Page
- Andrew Engwirda – Andrew’s Excel Tips
- Curt Frye – That Excel Guy
- Nick Hodge – Nick Hodge’s Excel Website
- Kisung Hwang – Excel Love (Korean)
- Bill Jelen – Mr. Excel
- Masaru Kaji (Colo) – Colo’s Excel Junk Room
- Dick Kusleika – Daily Dose of Excel
- Charley Kyd – Excel User
- Laurent Longre – X-Cell (French)
- Damon Longworth – Excel User Conference
- Ashish Mathur – Excel Enthusiasts
- Rodney Powell – Beyond Technology
- David McRitchie – My Excel Pages
- Tushar Mehta – Tushar Mehta Consulting
- Chip Pearson – Pearson Software Consulting, LLC
- John Peltier – Peltier Technical Services, Inc.
- Jan Karel Pieterse – JKP Application Development
- Andy Pope – AJP Excel Information
- Ken Puls – Excel Guru
- John Walkenbach – The Spreadsheet Page
- Allen Wyatt – Excel Tips

** **

I’m sure this list isn’t complete. If I’m missing someone, please add the name and URL in a comment. I’ll update the list later.

Here’s a list of all 76 Excel MVPs. About 30 of them will be attending the MVP Summit next week, where they expect a huge crowd – 1,800 people.

Today I realized that it was about 17 months ago when I saw the first demo of Excel 2007. Like just about everyone else in the room, I was very impressed with what I saw — even though it was little more than a rough draft at the time. Fact is, Excel 2007 demos very well.

Then, when I actually started using it, my initial enthusiasm waned — primarily because I couldn’t find the familiar commands. As I dug deeper, I discovered a few things that just didn’t work like they used to.

During the beta phase, I got very discouraged and really began to hate it. But, I was forced to use it because I was writing books about it. Over time, I actually began to like Excel 2007. By the time the final version was released, I got to the point where I dreaded having to use Excel 2003 rather than 2007. Now that I’m familiar with just about every nook and cranny of Excel 2007, I have a difficult time remembering where the Excel 2003 commands are.

The chart here roughly depicts my past 17 months with Excel 2007 in terms of “likeability.” I don’t know how typical this curve is. I suspect that it will be linear for some; they’ll start out hating it, and gradually learn to love it.

But I do think that anyone who uses this product for any length of time will *not* want to go back to a previous version. Sure, it has some problems. But its benefits outweigh the problems by a large margin as far as I’m concerned.

I found some code that uses API functions to play MIDI music at a French site. I adapted the code so it’s easy to use. Copy the code below and paste it into a VBA module.

Option Explicit

Private Declare Function midiOutOpen Lib “winmm.dll” _

(lphMidiOut As Long, _

ByVal uDeviceID As Long, _

ByVal dwCallback As Long, _

ByVal dwInstance As Long, _

ByVal dwFlags As Long) As Long

Private Declare Function midiOutClose Lib “winmm.dll” _

(ByVal hMidiOut As Long) As Long

Private Declare Function midiOutShortMsg Lib “winmm.dll” _

(ByVal hMidiOut As Long, _

ByVal dwMsg As Long) As Long

Private Declare Sub Sleep Lib “Kernel32” (ByVal dwMilliseconds As Long)

Dim hMidiOut As Long

Public lanote As Long

Sub PlayMIDI(voiceNum, noteNum, Duration)

Dim Note As Long

On Error Resume Next

midiOutClose hMidiOut

midiOutOpen hMidiOut, 0, 0, 0, 0

midiOutShortMsg hMidiOut, RGB(192, voiceNum – 1, 127)

lanote = 12 + CLng(noteNum)

Note = RGB(144, lanote, 127)

midiOutShortMsg hMidiOut, Note

Sleep (Duration)

midiOutClose hMidiOut

End Sub

Private Declare Function midiOutOpen Lib “winmm.dll” _

(lphMidiOut As Long, _

ByVal uDeviceID As Long, _

ByVal dwCallback As Long, _

ByVal dwInstance As Long, _

ByVal dwFlags As Long) As Long

Private Declare Function midiOutClose Lib “winmm.dll” _

(ByVal hMidiOut As Long) As Long

Private Declare Function midiOutShortMsg Lib “winmm.dll” _

(ByVal hMidiOut As Long, _

ByVal dwMsg As Long) As Long

Private Declare Sub Sleep Lib “Kernel32” (ByVal dwMilliseconds As Long)

Dim hMidiOut As Long

Public lanote As Long

Sub PlayMIDI(voiceNum, noteNum, Duration)

Dim Note As Long

On Error Resume Next

midiOutClose hMidiOut

midiOutOpen hMidiOut, 0, 0, 0, 0

midiOutShortMsg hMidiOut, RGB(192, voiceNum – 1, 127)

lanote = 12 + CLng(noteNum)

Note = RGB(144, lanote, 127)

midiOutShortMsg hMidiOut, Note

Sleep (Duration)

midiOutClose hMidiOut

End Sub

The PlayMIDI Sub procedure accepts three arguments, and plays a single note. The argument are:

**voiceNum:**A number from 1-128 that represents the instrument sound. Here’s a list of the MIDI voice numbers.**noteNum:**A number that indicates the note to play. For reference, C is 0, 12, 24, 36, etc. C# is 1, 13, 25, 37, etc.**Duration:**A number that indicates how long to play the note, in milliseconds (1,000 equals 1 second).

To play around with this, I set up a worksheet that has a 4-column list of notes. A lookup table provides the actual note letters for the values in column B. In the figure, I have it set up to generate random notes and durations. Then, a simple macro plays the song.

Then, a simple macro plays the song represented by the worksheet data.

Sub TestMidi()

Dim r As Long

ActiveSheet.Calculate

For r = 2 To Application.CountA(Range(“A:A”))

Call PlayMIDI(Cells(r, 1), Cells(r, 2), Cells(r, 3))

Next r

End Sub

Dim r As Long

ActiveSheet.Calculate

For r = 2 To Application.CountA(Range(“A:A”))

Call PlayMIDI(Cells(r, 1), Cells(r, 2), Cells(r, 3))

Next r

End Sub

By the way, I have no idea how this code works. Using the RGB function is a mystery to me. One final note. Avoid stopping the code by pressing Ctrl+Break. If you do that, you may get a stuck note that requires closing Excel.

Coincidentally, this post relates to two previous posts — which happen to be unrelated to each other:

- Dynamic Text Sorting – An array formula that sorts data in a range as it’s entered.
- Modifying Shapes (and Charts) With UDFs – A user-defined function that modifies objects on a worksheet.

Excel 2007 has a new Sort object. I discovered that this object does its thing even when it’s called by a UDF function used in a formula. In other words, yet another example of a UDF that changes the spreadsheet.

The figure below shows an example. The data in columns A and B is sorted immediately (by column B) whenever a change is made within the range A1:B50. So, if I change Anne’s score to 71, her row would move down between Paul and Oscar’s data.

Cell D2 contains a formula that uses the SortRange function:

Function SortRange(rngToSort As Range, Optional order)

‘ Excel 2007 only

‘ Order: 1=ascending, 2=descending

If IsMissing(order) Then order = 1

With rngToSort.Parent.Sort

.SortFields.Clear

.SortFields.Add Key:=rngToSort.Cells(1, 2), order:=order

.SetRange rngToSort

.Header = xlYes

.Apply

SortRange = .Rng.Address(False, False) ‘Range actually sorted

End With

End Function

‘ Excel 2007 only

‘ Order: 1=ascending, 2=descending

If IsMissing(order) Then order = 1

With rngToSort.Parent.Sort

.SortFields.Clear

.SortFields.Add Key:=rngToSort.Cells(1, 2), order:=order

.SetRange rngToSort

.Header = xlYes

.Apply

SortRange = .Rng.Address(False, False) ‘Range actually sorted

End With

End Function

This function takes two arguments: The range to sort, and the sort order (an optional argument). The Sort object is a member of the Worksheet, so I use the Parent property to get that object, and enclose all of the object manipulations inside of a With-End With structure.

The function does this…

- Clears the SortFields (which is a collection)
- Adds a key. In this case, the sort key is the second column of the data, and it’s sorted in the order specified by the second argument.
- Gets the range to be sorted, using the SetRange method.
- Specifies that the range has a header row, via the the Header property.
- Does the sorting, via the Apply method
- Gets the address of the range that was
*actually*sorted, and assigns it to the function (this is the text returned by the formula). The function is passing a 50-row range, but it only has 13 rows of data.

You can accomplish the same effect with a WorksheetChange event, but this technique allows the user to specify some options without modifying the code. The function, of course, could include more arguments that control how the sorting is done.

Practical applications, anyone?

Assuming I work non-stop all weekend, I should have a beta version of my PUP v7 add-in ready to go next week. This version works only with Excel 2007.

If you’re interested in doing some informal beta testing, please let me know.

You’ll need a copy of Excel 2007, of course. If you’re able to devote a few hours trying out the utilities (and reporting the bugs to me), I’d appreciate your help. I’m especially interested in recruiting someone who can test it using a non-English version of Excel.

Here’s a tiny image of the PUP v7 ribbon (click to enlarge):

You can view a list of the PUP changes here: What’s New in PUP v7.

Creating that ribbon UI was the biggest challenge. It even includes a dynamicMenu control (for the PUP Bookmarks) which took me about 4-5 hours to figure out and debug. Doing the same thing with a CommandBar control would have taken about 30 minutes.

Do you know that you can write VBA worksheet functions that modify shapes on a worksheet? I didn’t know this until about an hour ago.

Paste the following UDF into a VBA module:

Function ModifyShape(ShapeNumber, ShapeType, Vis)

With ActiveSheet.Shapes(ShapeNumber)

.AutoShapeType = ShapeType

.Visible = Vis

End With

End Function

With ActiveSheet.Shapes(ShapeNumber)

.AutoShapeType = ShapeType

.Visible = Vis

End With

End Function

Then, add a shape to the worksheet and enter this formula into any cell:

=ModifyShape(1,55,TRUE)

The first argument is the shape’s index number. The second argument is a value that represents the shape’s type (values from 1-138 are supported). The last argument determines whether the shape is visible. The arguments, of course, could use cell references. Change the second argument and watch the shape change its shape. Change the third argument to FALSE and watch the shape disappear.

This sort of thing is a lot more useful in Excel 2007, because embedded charts are contained in shapes. Therefore, you can write formulas that manipulate the chart object properties such as size, position, and visibility. Even better, your UDF can even access the Chart object contained in the shape, and manipulate that. Here’s a simple example:

Function ChangeChartType(CName, CType)

‘ Excel 2007 only

ActiveSheet.Shapes(CName).Chart.ChartType = CType

End Function

‘ Excel 2007 only

ActiveSheet.Shapes(CName).Chart.ChartType = CType

End Function

This function assumes an embedded chart. It uses two arguments: the ChartObject’s name, and the chart type (e.g., 5 is xlPie, -4100 is xl3DColumn, etc.).

Even better — a function that allows you to specify a chart’s min and max scale values:

Function ChangeChartAxisScale(CName, lower, upper)

‘ Excel 2007 only

With ActiveSheet.Shapes(CName).Chart.Axes(xlValue)

.MinimumScale = lower

.MaximumScale = upper

End With

End Function

‘ Excel 2007 only

With ActiveSheet.Shapes(CName).Chart.Axes(xlValue)

.MinimumScale = lower

.MaximumScale = upper

End With

End Function

The ability to adjust a chart’s axes based on calculations has long been on the Excel wish list. I wonder if Microsoft even knows this is now possible?

Dick forgot to post a weekend free-for-all-thing last Friday, so I’m stepping in and taking over.

Let’s talk about music.

Who plays an instrument? Dick hasn’t talked about his piano playing in quite a while. How’s it coming, Dick?

If you’d visit my house, you’d find about 12 guitars, five banjos, a mandolin, two ukuleles, an electronic keyboard, a dulcimer, a fiddle, and a few harmonicas. I dabble a lot, but I’m a master of nothing. But I enjoy it, and that’s what it’s all about.

What type of music do you like to listen to?

Is there anyone who has *no* interest in music?