Wednesday, November 29, 2006

Improving Performance in Excel 2007

http://msdn2.microsoft.com/en-us/library/aa730921.aspx

Summary: Learn about the increased worksheet capacity in Microsoft Office Excel 2007 and techniques that you can use as you design and create worksheets to improve calculation performance. (40 printed pages)

Charles Williams, Decision Models Limited

October 2006

Applies to: Microsoft Office Excel 2007, Microsoft Office Excel 2003, Microsoft Excel 2002, Microsoft Excel 2000

Contents

Filename in cell

http://www.ozgrid.com/forum/showthread.php?t=53472

=REPLACE(LEFT(CELL("filename",A2),FIND("]",CELL("filename",A2))-5),1,FIND("[",CELL("filename",A2)),"")
or possibly better

=MID(CELL("filename"),SEARCH("[",CELL("filename"))+1,SEARCH("]",CELL("filename"))-5-SEARCH("[",CELL("filename")))

Showing worksheet tab name in cell

=MID(CELL("filename",A1),FIND("]",CELL("filename",A1))+1,256)

New Custom Functions (work with highlighted cells)

http://blog.livedoor.jp/andrewe/archives/cat_415504.html

'Last night I decided to make some custom functions to work with Conditional Formatting. They've been added to Andrew's Custom Functions which you can download here.

1. GetColor has been replaced by GetColorIndex.
2. GetFontIndex has been added.
3. ColorName has been added.

'Here's a picture of ColorName used to distinguish whether colors are Fill colors or Conditional Format colors. The "1" in Column F formulas refers to the 1st Conditional Format, if left out it refers to the Fill color. (Use "2" or "3" for the second or third Conditional Formats respectively).



'In the case of cell B12, the Fill color and the first Conditional Format color are the same, something that might be missed if not checked carefully.

'Just keep in mind that a) this function only works for the standard 56 colors and b) some color names are duplicated. Even so, it may come in handy depending on what you are using it for."

Row Highlight

http://blog.livedoor.jp/andrewe/archives/cat_415504.html

"Some time ago, I was experimenting with automatic row highlighting using VBA and thought a separate post might be in order just in case the previous comments got missed.

'Using the Visual Basic Editor, place the following code in ThisWorkbook.

Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
Application.ScreenUpdating = True
End Sub

'Then, select your range with the mouse and open Conditional Formatting from the Format menu to enter this formula.

=CELL("row")=ROW()



'This is how it looks.



'Quite simple and easy to remember. (The screen flickers a bit with larger files, it still seems to work without problems).

'Ivan F Moala of the famed Xcel Files was kind enough to show me a good alternative. It does have some drawbacks but the highlighting factor itself is improved.

Note: It was also suggested to use =OR(CELL("row")=ROW(), CELL("col")=COLUMN()) to get both active row and column on my Japanese site. The choice is yours ;-)"

Displaying AutoFilter criteria

http://j-walk.com/ss/excel/usertips/tip044.htm

"Excel's AutoFilter feature definitely ranks right up there when it comes to handy tools. This feature, which you access with the Data, Filter, AutoFilter command, works with a range of cells set up as a database or list. When AutoFiltering is turned on, the row headers display drop-down arrows that let you specify criteria (such as "Age greater than 30"). Rows that don't match your criteria are hidden, but they are redisplayed when you turn off AutoFiltering.

'One problem with AutoFiltering is that you can't tell which criteria are in effect. Stephen Bullen developed a custom VBA worksheet function that displays the current AutoFilter criteria in a cell. The instructions that follow are for Excel 97 or later.

'Press Alt+F11 and insert a new module for the active workbook. Then enter the VBA code for the FilterCriteria shown below.

Function FilterCriteria(Rng As Range) As String
'By Stephen Bullen
Dim Filter As String
Filter = ""
On Error GoTo Finish
With Rng.Parent.AutoFilter
If Intersect(Rng, .Range) Is Nothing Then GoTo Finish
With .Filters(Rng.Column - .Range.Column + 1)
If Not .On Then GoTo Finish
Filter = .Criteria1
Select Case .Operator
Case xlAnd
Filter = Filter & " AND " & .Criteria2
Case xlOr
Filter = Filter & " OR " & .Criteria2
End Select
End With
End With
Finish:
FilterCriteria = Filter
End Function

'After you've entered the VBA code, you can use the function in your formulas. The single-cell argument for the FilterCriteria function can refer to any cell within the column of interest. The formula will return the current AutoFilter criteria (if any) for the specified column. When you turn AutoFiltering off, the formulas don't display anything.

'The figure below shows the FilterCriteria in action. The function is used in the cells in row 1. For example, cell A1 contains this formula:

=FilterCriteria(A3)

'As you can see, the list is currently filtered to show rows in which column A contains January, column C contains a code of A or B, and column D contains a value greater than 125 (column B is not filtered, so the formula in cell B1 displays nothing). The rows that don't match these criteria are hidden."

Filter Highlight

http://blog.livedoor.jp/andrewe/archives/cat_415504.html

"When using AutoFilter, it's quite hard to see which column is being filtered.



'This Custom Function can be used to highlight them. Enter the following code in a standard module. (Alt + F11, then Insert, Module from the top menu)

Function FilterOn(myCell As Range) As Boolean
On Error Resume Next
With myCell.Parent.AutoFilter
With .Filters(myCell.Column - .Range.Column + 1)
If .On Then FilterOn = True
End With
End With
End Function

'Then using Conditional Formatting, enter =filteron(your cell reference). (Format, Conditional Formatting, Formula Is).

'The result...



'I should mention I made the above from tinkering with this custom function by Stephen Bullen (found on the Spreadsheet Page by John Walkenbach).

Always pays to tinker... ;-)"

change a range of formulas from Absolute to Relative

http://www.ozgrid.com/News/IndirectBlanksConverFormula.htm

"This month I thought I would show you the ConvertFormula method! This method allows us to change a range of formulas from Absolute to Relative, Relative to Absolute, Relative Row to Absolute Row and so on... In fact we can change any relative or absolute aspect of any formula. The Excel help for the ConvertFormula is written as below:

'Converts cell references in a formula between the A1 and R1C1 reference styles, between relative and absolute references, or both.


Syntax

expression.ConvertFormula(Formula, FromReferenceStyle, ToReferenceStyle, ToAbsolute, RelativeTo)

expression Required. An expression that returns an Application object.

Formula Required Variant. A string that contains the formula you want to convert. This must be a valid formula, and it must begin with an equal sign.

FromReferenceStyle Required Long. The reference style of the formula. Can be one of the following XLReferenceStyle constants: xlA1 or xlR1C1.

ToReferenceStyle Optional Variant. The reference style you want returned. Can be one of the following XLReferenceStyle constants: xlA1 or xlR1C1. If this argument is omitted, the reference style isn't changed; the formula stays in the style specified by FromReferenceStyle.

ToAbsolute Optional Variant. Specifies the converted reference type. Can be one of the following XLReferenceType constants: xlAbsolute, xlAbsRowRelColumn, xlRelRowAbsColumn, or xlRelative. If this argument is omitted, the reference type isn't changed.

RelativeTo Optional Variant. A Range object that contains one cell. Relative references relate to this cell.


So with the above in mind we can easily write a simple Procedure that will change our formulas for us

Sub MakeAbsoluteorRelative()
'Written by OzGrid Business Applications
'www.ozgrid.com

Dim RdoRange As Range
Dim i As Integer
Dim Reply As String
'Ask whether Relative or Absolute
Reply = InputBox("Change formulas to?" & Chr(13) & Chr(13) _
& "Relative row/Absolute column = 1" & Chr(13) _
& "Absolute row/Relative column = 2" & Chr(13) _
& "Absolute all = 3" & Chr(13) _
& "Relative all = 4", "OzGrid Business Applications")
'They cancelled
If Reply = "" Then Exit Sub

On Error Resume Next
'Set Range variable to formula cells only
Set RdoRange = Selection.SpecialCells(Type:=xlFormulas)
'determine the change type
Select Case Reply
Case 1 'Relative row/Absolute column

For i = 1 To RdoRange.Areas.Count
RdoRange.Areas(i).Formula = _
Application.ConvertFormula _
(Formula:=RdoRange.Areas(i).Formula, _
FromReferenceStyle:=xlA1, _
ToReferenceStyle:=xlA1, ToAbsolute:=xlRelRowAbsColumn)
Next i

Case 2 'Absolute row/Relative column

For i = 1 To RdoRange.Areas.Count
RdoRange.Areas(i).Formula = _
Application.ConvertFormula _
(Formula:=RdoRange.Areas(i).Formula, _
FromReferenceStyle:=xlA1, _
ToReferenceStyle:=xlA1, ToAbsolute:=xlAbsRowRelColumn)
Next i

Case 3 'Absolute all

For i = 1 To RdoRange.Areas.Count
RdoRange.Areas(i).Formula = _
Application.ConvertFormula _
(Formula:=RdoRange.Areas(i).Formula, _
FromReferenceStyle:=xlA1, _
ToReferenceStyle:=xlA1, ToAbsolute:=xlAbsolute)
Next i

Case
4 'Relative all

For i = 1 To RdoRange.Areas.Count
RdoRange.Areas(i).Formula = _
Application.ConvertFormula _
(Formula:=RdoRange.Areas(i).Formula, _
FromReferenceStyle:=xlA1, _
ToReferenceStyle:=xlA1, ToAbsolute:=xlRelative)
Next i


Case Else 'Typo
MsgBox "Change type not recognised!", vbCritical, _
"OzGrid Business Applications"
End Select

'Clear memory
Set RdoRange = Nothing
End Sub

'The important part to note in the above Procedure is that we do not loop through all cells in the selection looking for formulas, we simply set a range variable to the SpecialCells method using xlFormulas as the Type argument. This way we are not:
  1. Looping through potentially thousands of cells we are not interested in.
  2. Forcing the user to select only formula cells.
I hope you can all find a use for this one, I know I certainly can and do!"

Comments Made Simple(r)

http://blog.livedoor.jp/andrewe/archives/cat_415504.html

"Doug Glancy kindly suggested making my code run more like a regular userform in the comments of my last post.

'So this is what I came up with. Enter the text as before by pushing the OK button.



'Then choose to add another line or continue by inserting the comment.



'You can then choose to keep the comment visible or hide it from view. With a bit of practice you can enter lines and comments quite quickly. A shortcut would be a really good idea if you use it a lot.

'Here's the new code.

Sub AddComment()
On Error Resume Next
Dim cmtMsg As String, nwLne As String, LneCnt As Long, Dscn As Long
If TypeName(Selection) <> "Range" Then Exit Sub
AddLine:
nwLne = InputBox("Enter line " & LneCnt + 1 & " of your comment text." & vbNewLine & vbNewLine & _
"Leave blank or push Cancel to exit.", "Please write your comment")
If LneCnt > 0 Then cmtMsg = cmtMsg & Chr(10) & nwLne Else cmtMsg = nwLne
LneCnt = LneCnt + 1
If nwLne = "" Then
Exit Sub
Else
Dscn = MsgBox("Do you want to add another line? " & vbNewLine & vbNewLine _
& "Push No to insert the comment.", vbYesNo, "Add another line?")
If Dscn = vbYes Then GoTo AddLine
Application.ScreenUpdating = False
With ActiveCell
.ClearComments
.AddComment
With .Comment
.Visible = True
.Shape.AutoShapeType = msoShapeRoundedRectangle
.Shape.Shadow.Visible = msoFalse
.Shape.Select True
.Text Text:=cmtMsg
With Selection.ShapeRange
.Shadow.Visible = msoFalse
.ScaleHeight 0.3, msoFalse, msoScaleFromTopLeft
.Adjustments.Item(1) = 0.25
End With
.Shape.TextFrame.Autosize = True
Dscn = MsgBox("Do you want the comment to remain visible? ", _
vbYesNo, "Keep comment visible?")
If Dscn = vbNo Then .Visible = False
End With
.Select
End With
Application.ScreenUpdating = True
End If
End Sub




'Hmm...me like :-)

'Update: Andy Pope sent me a file to show an even better job can be done with a Multiline Textbox. There's no need to push buttons to add new lines, just use the Enter key. This also has a great advantage in that you see all of the text, not just one line at a time) When finished you can enter the text, by pushing Tab, Enter (This enables the Ok button to insert the comment)

'Here's a picture of it in action.



'You can download his file here.

More good code for me to study. Thanks also to Jon Peltier for suggesting the same thing."

Splashscreen

http://blog.livedoor.jp/andrewe/archives/cat_415504.html

"Sooner or later, everyone wants to try making a splashscreen. Most that I seen so far seem be made with a userform, but I wanted to try making a very simple one using AutoShapes.



'Actually, this is a picture of two Autoshapes that have been grouped together. The Fill effects are Papyrus and Medium Wood, obtained from doube clicking the shapes while they are still separate and selecting Format AutoShape, Color and Lines, Fill Color, Fill Effects, Texture.

'Here's the code I used.

Sub SplashscreenShape()
Dim Ctr As Long
ThisWorkbook.Sheets("Sheet3").Activate
Range("A1:R41").Select
ActiveWindow.Zoom = True
Do While Ctr < 5
ActiveSheet.Shapes("Message").Select
Select Case Ctr
Case 0
Selection.Characters.Text = "CHANGE" & Chr(10) & "IT" & Chr(10) & "A" & Chr(10) & "BIT"
Case 1
Selection.Characters.Text = "WRITE" & Chr(10) & "ANYTHING" & Chr(10) & "YOU" & Chr(10) & "LIKE"
Case 2
Selection.Characters.Text = "DO" & Chr(10) & "IT" & Chr(10) & "FOR" & Chr(10) & "FUN"
Case 3
Selection.Characters.Text = "THIS" & Chr(10) & "IS" & Chr(10) & "THE" & Chr(10) & "END"
End Select
Ctr = Ctr + 1
Application.Wait Now + TimeSerial(0, 0, 2)
Loop
Application.ScreenUpdating = False
ActiveSheet.Shapes("Message").Select
Selection.Characters.Text = "PUT" & Chr(10) & "SOME" & Chr(10) & "TEXT" & Chr(10) & "HERE"
Range("IV65536").Select
ThisWorkbook.Sheets("Sheet1").Select
Application.ScreenUpdating = True
End Sub

Sub Auto_Open()
SplashscreenShape
End Sub

Sub Auto_Close()
ThisWorkbook.Close SaveChanges:=False
End Sub

'Note that I've used "ActiveWindow.Zoom". This is so the range A1:R41 is zoomed in or zoomed out automatically which helps the dimensions of the AutoShape to retain their proportions on different size screens.

Here's a sample workbook to download. Nothing brilliant, just something for my own amusement ;-)"

Timelines in Excel

http://blog.livedoor.jp/andrewe/archives/cat_415504.html

"Jon Wittwer from the The Excel Nexus contacted me the other day regarding techniques he developed to make timelines using Excel graphs.

'This is a picture of a method used to create them very quickly. I had a look at the download file, it was quite simple and easy to use.



Here is a link to the download page. There is also some other material he wrote here. Definitely worth a look! "

More On Automatic Highlighting

http://blog.livedoor.jp/andrewe/archives/cat_415504.html

"Earlier this week I was experimenting again with highlighting using the CELL function.

'Highlight the Active Cell
The syntax of the CELL function is CELL(info_type, [reference]), the "reference" part meaning a cell such as A1 etc.

'When the reference is omitted, the CELL function refers to the active cell. This can be readily used to highlight with some Conditional Formatting and Screen Updating.

'First navigate to the Visual Basic Editor (push Alt + F11), locate your file, then select Microsoft Excel Objects, ThisWorkbook. At the top, you will see 2 dropdown lists. Use the one on the left to select Workbook, then the one on the right to choose SheetSelectionChange. Add "Application.ScreenUpdating = True" like this.

Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
Application.ScreenUpdating = True
End Sub

'Go back to Excel, select your highlight range, then from the top menu choose Format, Conditional Formatting, select Formula Is from the dropdown list and enter this formula.

=CELL("address")=ADDRESS(ROW(),COLUMN())

'Push the Format button, make a color selection from the Patterns tab, then push OK twice.

'You should get something like this.



'The address of the active cell (using CELL) equals that of the address obtained with the ROW and COLUMN functions. TRUE!

'Note: You can use Worksheet_SelectionChange instead if you want to work with just one particular sheet, enter the code in the appropriate sheet instead of This Workbook.

'Highlight the Row and Column
Last time I mentioned highlighting both the row and column like this.

=OR(CELL("row")=ROW(), CELL("col")=COLUMN())

'This results in a cross shape where the row and column overlap. I didn't feel this was the best as a visual aid (the whole purpose of the exercise) so after some experimentation, I came to the conclusion that a reverse "L" was the best (same as seen on Ivan Moala's site, The Xcel Files), in that it seems a lot easier to work with.

'Here is the appropriate conditional format (use the same steps as above)

=OR(AND(CELL("row")=ROW(),CELL("col")+1>COLUMN()),AND(CELL("col")=COLUMN(),CELL("row")+1>ROW()))



While I mention it, Ivan told me of his Hex File Reader that contains an example of the code he uses. You can study his code and gain a valuable download at the same time, far too good an opprtunity to miss ;-)"

Bar Graphs In Cells

http://blog.livedoor.jp/andrewe/archives/cat_415504.html

"I just saw a future conditional format coming up in Excel 12 via the J-Walk Blog and thought I would try to imitate it. Note: This should work fine with Excel 2002 and 2003, I'm not sure about earlier versions but you can give it a try. (The transparency feature might not work as well though)

'Here's the real McCoy... (picture from David Gainer's Microsoft Excel blog)



'And here's the code I used with Autoshapes...

Sub BarGraphsInCells()
On Error Resume Next
Dim LoopCount As Long, BarWidth As Long, Cell As Range, theRange As Range
Set theRange = Selection
LoopCount = 1
For Each Cell In theRange
BarWidth = (ActiveCell.Value / Application.Max(theRange)) * ActiveCell.Width * 0.7 ' Adjust to suit
ActiveSheet.Shapes.AddShape(msoShapeRectangle, ActiveCell.Left, ActiveCell.Top, BarWidth, ActiveCell.Height). _
Select
With Selection
.ShapeRange.Fill.ForeColor.SchemeColor = 12
.ShapeRange.Line.Visible = msoFalse
.ShapeRange.Fill.Transparency = 0.8
.Name = "The Bars" & LoopCount
End With
ActiveSheet.Shapes.Range(Array("The Bars", "The Bars" & LoopCount)).Select
Selection.ShapeRange.Group.Select
Selection.Name = "The Bars"
Cell.Offset(1).Select
LoopCount = LoopCount + 1
Next
theRange.Select
End Sub

'And the result...



Hmm, not too bad considering it's way past my bedtime. Night!"

Bar Graphs In Cells 2

http://blog.livedoor.jp/andrewe/archives/cat_415504.html

"Continuing from my last post - I had a little spare time today so I decided to experiment a little more with the appearance. Here's my improved code to get the same kind of gradient effect as shown below (rather than just transparency)

Sub BarGraphsInCells2()
On Error Resume Next
Dim LoopCount As Long, BarWidth As Long, Cell As Range, theRange As Range
Set theRange = Selection
LoopCount = 1
For Each Cell In theRange
BarWidth = (ActiveCell.Value / Application.Max(theRange)) * ActiveCell.Width * 0.9
ActiveSheet.Shapes.AddShape(msoShapeRectangle, ActiveCell.Left, ActiveCell.Top, BarWidth, ActiveCell.Height). _
Select
With Selection
.ShapeRange.Line.Visible = msoFalse
.ShapeRange.Fill.Transparency = 0.8
.ShapeRange.Fill.ForeColor.SchemeColor = 48
.ShapeRange.Fill.OneColorGradient msoGradientVertical, 2, 0.5
.Name = "The Bars" & LoopCount
End With
ActiveSheet.Shapes.Range(Array("The Bars", "The Bars" & LoopCount)).Select
Selection.ShapeRange.Group.Select
Selection.Name = "The Bars"
Cell.Offset(1).Select
LoopCount = LoopCount + 1
Next
theRange.Select
End Sub

'This seems to be getting close.



'The next trick was to add a little extra in the way of conditioning. I used CBool to get a TRUE condition, otherwise skip the part where AutoShapes are added, then go to the next step, which I very orginally named "NextStep" :-)

'Here's an example that looks for the 5 highest values using RANK (be warned that this works a little strange in the case of duplicates).

Sub BarGraphsInCells3()
On Error Resume Next
Dim LoopCount As Long, BarWidth As Long, Cell As Range, theRange As Range
Set theRange = Selection
LoopCount = 1
For Each Cell In theRange
If Not CBool(Application.Rank(Cell, theRange) <= 5) Then GoTo NextStep
BarWidth = (ActiveCell.Value / Application.Max(theRange)) * ActiveCell.Width * 0.9
ActiveSheet.Shapes.AddShape(msoShapeRectangle, ActiveCell.Left, ActiveCell.Top, BarWidth, ActiveCell.Height). _
Select
With Selection
.ShapeRange.Line.Visible = msoFalse
.ShapeRange.Fill.Transparency = 0.8
.ShapeRange.Fill.ForeColor.SchemeColor = 10 'change the color here!
.ShapeRange.Fill.OneColorGradient msoGradientVertical, 2, 0.5
.Name = "The Bars" & LoopCount
End With
NextStep:
ActiveSheet.Shapes.Range(Array("The Bars", "The Bars" & LoopCount)).Select
Selection.ShapeRange.Group.Select
Selection.Name = "The Bars"
Cell.Offset(1).Select
LoopCount = LoopCount + 1
Next
theRange.Select
End Sub

'If you wanted to run the code without any extra conditions, just place an apostrophe in front of the CBool line to "deactivate" it (making it a comment instead of working code)

'Okay, let's take it a step further...first run the code to include all cells with a .ShapeRange.Fill.ForeColor.SchemeColor property of 10, then go to the Name Box and enter a new name for the grouped AutoShapes (eg "All The Bars") and push Enter. Then run the code again using a different SchemeColor (48) and use CBool to find the top 5 values as above. Enter a new name, say "The Top 5 Bars". Then let's try it again changing the CBool part to select all all values equal to or over 20 with a SchemeColor of 11. Rename to suit, "All Bars Over 20". (This renaming part is real hard work)

'If obscured, right click the last group and select Order, Send Backward. You should end up with something like this.



'Not bad for a hack (the code, not me) but as has been mentioned in my former post, not nearly as good as built-in Conditional Formatting where the colors change automatically (no code necessary). Then again the you can keep the grouped AutoShapes as "hardcopies", writing the condition may be a little simpler too depending on what you are trying to achieve. Then again, this is just one imitation format, I can only wait in anticipation to see what kind of tricks can be performed with Excel 12 ;-)

PS. Thanks Rembo for letting me know about the different row heights!"

Bar Graphs In Cells 3

http://blog.livedoor.jp/andrewe/archives/cat_415504.html

"Today I was trying to think a way to get around the difference in transparency between Excel versions. This is a possible solution - try something else ;-)

'As you can see I simply adjusted the height - on the left, using a single color, on the right using a gradient with two colors.



'Here's the code with some comments shown to get different effects.

Sub BarGraphsInCells4()
On Error Resume Next
Dim LoopCount As Long, BarWidth As Long, Cell As Range, theRange As Range
Set theRange = Selection
LoopCount = 1
For Each Cell In theRange
BarWidth = (ActiveCell.Value / Application.Max(theRange)) * ActiveCell.Width * 0.9 ' Adjust width
ActiveSheet.Shapes.AddShape(msoShapeRectangle, ActiveCell.Left, ActiveCell.Offset(1).Top - 1.5, BarWidth, 1.5). _
Select
With Selection
.ShapeRange.Line.Visible = msoFalse ' remove to show lines
.ShapeRange.Fill.ForeColor.SchemeColor = 48 ' change color to suit
.ShapeRange.Fill.BackColor.SchemeColor = 11 'remove for a single color, change color to suit
.ShapeRange.Fill.TwoColorGradient msoGradientVertical, 1 'remove for no gradient
.ShapeRange.Height = 2
.Name = "The Bars" & LoopCount
End With
NextStep:
ActiveSheet.Shapes.Range(Array("The Bars", "The Bars" & LoopCount)).Select
Selection.ShapeRange.Group.Select
Selection.Name = "The Bars"
Cell.Offset(1).Select
LoopCount = LoopCount + 1
Next
theRange.Select
End Sub

'One advantage is you can enter into the cells a lot easier than before. If you want to see the code run automatically, select a range, type a name in the Name Box and push Enter (I've used "MyRange" in this case). Then enter this code in the appropriate sheet under Microsoft Excel Objects in the Visual Basic Editor.

Private Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
Application.ScreenUpdating = False
If Not Intersect(ActiveCell, Range("MyRange")) Is Nothing Then
Shapes("The Bars").Delete
Range("MyRange").Select
BarGraphsInCells4
Target.Select
End If
Application.ScreenUpdating = True
End Sub

Keep in mind calculation may be slowed down somewhat. Use accordingly :-)"

Putting file drive location in the title bar

http://blog.livedoor.jp/andrewe/archives/cat_415504.html

"Colo showed me how to use an Application Event when I was making Cell Spotter. Rather than apply to just one worksheet or workbook, the application level DoubleClick event that Cell Spotter uses applies to all open workbooks.

'So I was thinking the other day how I could use this to show file locations in the title bar when I open workbooks. (The Full Name gets a bit long and won't always fit inside the title bar, so I just settled for the drive where the file is stored)

'Here's to how to do it.

'First go to the Visual Basic Editor. (Push the Alt + F11 keys) Then add a standard module and insert this code.

Public XL As New ShowDrive

Private Sub Auto_Open()
Set XL.App = Application
End Sub

'Then add a Class Module and add this code. (The Class Module name must be the same as after where it says "New" in the code above - in this case "ShowDrive")

Public WithEvents App As Excel.Application

Private Sub App_WorkbookOpen(ByVal Wb As Excel.Workbook)
On Error Resume Next
If Wb.Name <> "PERSONAL.XLS" Then _
ActiveWindow.Caption = Left(Wb.FullName, 2) & " " & Wb.Name
End Sub

'Note this code only works if the workbook is saved (New workbooks don't really have full names until they are saved, also the Personal.xls file has been coded out using If...Then...)

'Hmm, this works fine but a problem occurs when the file is saved. If the drive is different I won't have any way of checking if the drive has changed because a BeforeSave event can't be used in this case, not as far as I know anyway :-)

'So I added this SelectionChange code instead. This works when other cells are selected so it should work assuming you select some cells after saving.

'Make sure it's placed in the class module too.

Private Sub App_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Excel.Range)
On Error Resume Next
If Len(Application.Substitute(Sh.Parent.Name, ".xls", "")) <> Len(Sh.Parent.Name) Then _
ActiveWindow.Caption = Left(Sh.Parent.FullName, 2) & " " & Sh.Parent.Name
End Sub

'Now for the final step. Close the Visual Basic Editor, then save the workbook as an add-in by selecting Save As from the File Menu, then Microsoft Excel Add-In from the "Save as type:" drop down list under where it says "File name:".

'Go to Tools, Add-Ins and install it. (Use the Browse button to find add-ins not shown in the list)

Keep in mind the ShowDrive class module is where the event code works, you can adjust it to do whatever you like ;-)"

BeforeSave only with Save, not Save As

http://blog.livedoor.jp/andrewe/archives/cat_415504.html

I see this question asked a few times and just as often answered...anyway I figure I'd include it here too.

Q. When you use a BeforeSave event, is there a way to stop it from firing when using Save As?

A. Yep, use a variation of this code. This example asks the user whether they really want to save, ie they have pushed Save, not Save As which you use to give a workbook a (new) filename.

Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
If SaveAsUI = False Then
If MsgBox("Do you really want to save this Workbook? ", vbYesNo) = vbNo Then
Cancel = True
Exit Sub
End If
End If
End Sub

It's the SaveAsUI part that exits the sub. Keep in mind that the code also exits when working with unsaved workbooks, saving them would involve using a new filename (ie Save As).

Quick Write: Me: 1, Registry: 0

http://blog.livedoor.jp/andrewe/archives/cat_415504.html

I've been practicing using the registry over the last couple of days. What I've done is make a new version of Quick Paste called Quick Write, it's essentially the same thing except that Quick Write does not use values in cells, and does not save when uninstallling or quitting Excel. Rather, the values are stored between sessions in the registry.



I've found this to be a much better method overall, not to mention some nice practice in learning some new code.

Here's the link. You can launch it from the Right Click menu.

Update: I found a potential bug that has hopefully been fixed. Please download the new version from the above link.

New Cell Spotter

http://blog.livedoor.jp/andrewe/archives/cat_415504.html

Today I modified the code for Cell Spotter.



As you can see some extra items have been added to the menu. In addition to the lines that appear when you double-click cells, you can also use "Highlight" to make the same type of lines appear around multiple selections - the cells are determined by selecting them with the mouse. Unlike the regular Cell Spotter lines, these lines don't disappear automatically unless you choose to delete them.



Also, the line color(s) and thickness can be altered by changing "Settings".

And finally, you can choose to turn Cell Spotter on or off as convenient. Any changes made (including "Settings") are recorded in the registry.

A little early for Christmas but I hope you like it. The link is here.

Day and Date Calculator

http://blog.livedoor.jp/andrewe/archives/cat_415504.html

Here's something I was playing with over the break. (Thanks to my trusty tester Remco :-))

Day and Date Calculator
You can subtract dates. (The difference between the 2 dates is shown under the "Years, Month, Day" and "Weeks, Day" labels which adjust automatically. Note: The Upper Date must be higher than the Lower Date)



Or calculate dates. (Enter either positive or negative numbers in the textboxes where applicable. The "Calculated Date" label also updates automatically)



Pre-1900 Dates
Notice the lower date from the first image that is shown is Year 100. Dates from Year 100 January 1 until Year 9999 November 30 can be used for both Subtract Dates and Calculate Dates.

Here's a link to download from.

Tuesday, November 28, 2006

Visibility In Excel Part 1 - Dynamic Comments

http://blog.livedoor.jp/andrewe/archives/cat_415504.html

It occurred to me the other day that visibility in spreadsheet design is not always the best. This may be due to several reasons, the fact is key data may not be readily accessible when needed - it could be in hidden rows or columns, not visible on the current screen or perhaps in another sheet or workbook.

This is a possible solution using Input Messages from Data Validation combined with a Selection Change event. An example workbook is attached for your reference here. And below is an image of a "Dynamic Comment" in action. (By "Dynamic Comments", I mean comments that update automatically. Select the cell where they show, and the latest data will be updated instantly)



A bit of explanation on how it is set up. Here is the example code.

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
On Error Resume Next
If Cells(1, 1) = False Then Exit Sub
With Target
If .Column = 2 And _
.Row > 2 And _
.Row < ActiveSheet.UsedRange.Row + _
ActiveSheet.UsedRange.Rows.Count Then
.EntireColumn.Validation.Delete
With .Validation
.Add Type:=xlValidateInputOnly
.InputTitle = "" ' Optional
.InputMessage = "Customer: " & Cells(Target.Row, 2) & Chr(10) & _
"Invoice Number: " & Cells(Target.Row, 14) & Chr(10) & _
"Item Ordered: " & Cells(Target.Row, 6) & Chr(10) & _
"Quantity: " & Format(Cells(Target.Row, 12), "#,##0") & Chr(10) & _
"Amount: " & Format(Cells(Target.Row, 16), "$ #,##0.00") & Chr(10) & _
"Total Sales: " & Format(Application.Sum(Range("TotalSales")), "$ #,##0.00")
End With
End If
End With
End Sub

1. Automatic Update Mode vs. Edit Mode
First, you will notice that if Cells(1,1) (Range A1) is False, the subroutine is exited. This added to make it possible to edit the worksheet without the SelectionChange event interfering with Undo. The Forms Toolbar checkbox you can see is linked to Range A1, the font is not visible as I have changed the color to White but you can see whether the code is enabled or not by looking at the Checkbox. When checked, A1 is TRUE (Automatic Update Mode), otherwise it is FALSE (Edit Mode). If you are just referring to data rather than editing (or you feel comfortable without Undo), you might want to just leave it in Automatic Update Mode all of the time.

2. Set the Range
I've limited the range to just once cell in one column (In this case Column B). It might be tempting to add more columns or rows and there is nothing to stop you, I prefer to limit the validation to keep the file size down.

3. The Input Message
This is where it all happens. I've made a string to join data from several cells. The input message in this case is just an example. It probably would not be necessary to show data from cells that are already visible. So you can use the "comments" to refer to where you can't see... as mentioned above, hidden rows or columns, not visible on the current screen or in another sheet or workbook, even outside of Excel (for example, the Registry). Not only that, you can use VBA to make calculations to add to the string and also format it as you like.

A few things to keep in mind,

There is a limit of characters that can be used in the Input Messages (254 by my testing)

I've used Chr(10) to force linebreaks. It doesn't always work as expected, but you can add an extra row of characters at the bottom to help. The number of of characters used in this case should exceed the maximum number of characters used in any other row.

An easy way to get the Column numbers is to go to Tools, Options, General, then check R1C1 reference style to show the numbers in the Column Headers. Uncheck when finished.

Why not use regular comments? I experimented using them - I found this way to work better.

Visibility In Excel Part 3 User Friendly Rows

http://blog.livedoor.jp/andrewe/archives/cat_415504.html

Excel is vertical-centric (Okay, I made this word up)

Don't believe me? The user interface is mostly at the top or bottom - Main menu at the top, Autofilter at the top, Name Box and Formula Bar at the top, Status Bar at the bottom, Sheet tabs at the bottom...why is it so? I guess the reason is that it is a lot easier to work "vertically" than "horizontally", even mouse wheels are designed to work this way.

Which leads to a problem with rows. Looking at several rows with data, it's easy to lose track of which row you are supposed to be looking at. So here are a few tips to make life easier.

1. Increase the row height
If this does not interfere with how your files are set up, changing row heights to somewhere between 18 and 24 points will help reduce eyestrain.

Before (14.25 points)


After (18.00 points)


Easier to see with just a minor change in row height.

2. Row Shading
Changing the color of every other row helps too. You can just change the Fill color, or use Conditional Formatting. (This has the advantage of chaning the row color if you decide to insert more rows later on)

Here's a link to one of my old posts.

My preferred formats are

=ODD(ROW())=ROW()

or

=EVEN(ROW())=ROW()

mainly because they are simple to remember...

The difference?



Getting better again.

3. Automatic row highlight
This is some code I wrote a week or so ago. Place it in the appropiate sheet module and change the top left cell and bottom right cell of your range to suit where indicated.

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Range("A1") = False Then Exit Sub ' Optional
On Error Resume Next
Dim myRange As Range
Dim myTopLeftCell As Range
Dim myBottomRightCell As Range

' Set your Top Left Cell and Bottom Right Cell, (Range Names can be used also)
' ****************************************************************
Set myTopLeftCell = Range("B2")
Set myBottomRightCell = Range("H20")
' ****************************************************************

If Target.Row >= myTopLeftCell.Row And _
Target.Offset(Selection.Rows.Count - 1).Row <= myBottomRightCell.Row And _ Target.Column >= myTopLeftCell.Column And _
Target.Offset(, Selection.Columns.Count - 1).Column <= myBottomRightCell.Column Then Set myRange = Selection If ActiveSheet.Shapes("hSelection") Is Nothing Then ActiveSheet.Shapes.AddShape(msoShapeRectangle, myTopLeftCell.Left, Selection.Top, _ myBottomRightCell.Offset(, 1).Left - _ myTopLeftCell.Left, Selection.Height).Select With Selection With .ShapeRange .Fill.Visible = msoFalse .Line.ForeColor.SchemeColor = 12 ' Change Color Here .Line.Weight = 2.25 ' Change Line Weight (Thickness) Here .ZOrder msoSendToBack .Shadow.Visible = msoFalse End With .Name = "hSelection" .PrintObject = False End With Else With ActiveSheet.Shapes("hSelection") .Left = myTopLeftCell.Left .Top = Selection.Top .Width = myBottomRightCell.Offset(, 1).Left - myTopLeftCell.Left .Height = Selection.Height .ShapeRange.Shadow.Visible = msoFalse End With End If myRange.Select End If Set myTopLeftCell = Nothing Set myBottomRightCell = Nothing Set myRange = Nothing End Sub

Like my previous Data Validation code, it assumes that there is a Forms Toolbar checkbox in Cell A1 (Turn it the checkbox on and off to stop the code from running, it tends to interfere with Undo)

Here's some code to quickly insert a Checkbox.

Sub AddCheckbox()
On Error Resume Next
ActiveSheet.CheckBoxes.Add(0, 0, 0, 0).Select
With Selection
.Characters.Text = ""
.Value = xlOn
.LinkedCell = "A1"
End With
Range("A1").Select
Selection.Font.ColorIndex = 2
End Sub

With the Checkbox turned on, you are ready to go. Select any cell within the range and the row is highlighted accordingly.

Here's a pic.



I've also been working on a new version of my Cell Spotter to work in a similar way. Just now I'm testing it at work, I'll upload it when it's ready :-)

A Progress Bar

http://blog.livedoor.jp/andrewe/#top

I did quite a lot of programming work last week but took a little time off to have some fun.

Here is a progress bar made from a userform with no title bar.



And a link to see how it works.

A couple of things that may be of interest.

The code to remove the title bar comes Colo's Excel Junk Room who has quite a lot of other useful vba tips that involve userforms and other things.

Andy Pope of AJP Excel Information has some very interesting examples of progress bars in his collection of userform downloads and a lot of other great stuff to look at as well.

Colo's Excel Junk Room

AJP Excel Information

Well worth the visit ;-)

Marlett checkboxes

http://blog.livedoor.jp/andrewe/#top

The new version of our utilities is almost complete. In the meantime, I have been working on some new stuff. Something that I had fun with was a way to make working with Marlett checkboxes easier, a new addin.



Select a range and push Add Formatted Checkboxes to Range and the colors shown above will appear. Choose a color and the checkboxes will be added to the selected cells. At the same time, the code to make the checkboxes work will be added to your workbook so it will be okay even if you uninstall the addin at a later date, the code inside the workbook will run independently. You can also change the checkbox colors by selecting the same cells and choosing a new color.

Other options - Select, Delete, Tick and Untick require you to select cells that contain checkboxes as shown below (Don't select the shapes above the checkbox cells, they merely act as a way to activate the code to show the checkoxes as ticked or unticked)



Note: I've tested on Excel 97, 2003 and 2007 and it works okay. Depending on your security settings, you many need to ensure access to the Visual Basic Project is enabled. Instructions to do so are shown in the addin Read Me file.

Code for the addin is unlocked so you can see how things work. If you want to experiment working with the Visual Basic Editor, you will need to set a reference to the Visual Basic Project - make sure the project you are working on is selected and go to Tools, References and check Microsoft Visual Basic for Applications Extensibility on the VBE, (not the Tools Menu within Excel itself, the Visual Basic Editor!)

You can download it here :-)

Thursday, November 09, 2006

CUSTOM FUNCTION APPROACH (Autofilter)

http://www.ozgrid.com/News/nov-2006.htm

CUSTOM FUNCTION APPROACH

The 1st one we will use is a custom Excel function. This will not only flag the correct criteria field but also tell us what the criteria is. Let's assume the table to filter occupies A1:E1000 with A1:E1 being headings. First we should always have at least 3 rows above any table in Excel. Select rows 1:3 and go to Insert>Rows. This will insert 3 rows, or as many as you select. We will use at least 1 row to display the criteria in the appropriate column. Why at least 3 rows then? Good practice is the answer. This way we can set up criteria, if need be, for Advanced Filter.

CRITERIA FUNCTION CODE

Below is the code that must be added to the Workbook, or an Excel Add-in. To add the code to a Workbook go to Tools>Macro>Visual Basic Editor (Alt+F11) then to Insert>Module and paste in the code below;

Function AutoFilter_Criteria(Header As Range) As String
Dim strCri1 As
String, strCri2 As String
Application.Volatile
With
Header.Parent.AutoFilter
With .Filters(Header.Column - .Range.Column + 1)
If Not .On Then Exit Function
strCri1 = .Criteria1
If .Operator =
xlAnd Then
strCri2 = " AND " & .Criteria2
ElseIf .Operator = xlOr
Then
strCri2 = " OR " & .Criteria2
End If
End With
End With

AutoFilter_Criteria = _
UCase(Header) & ": " & strCri1 &
strCri2
End Function

Note the use of Application.Volatile in the code. This will ensure our function updates whenever the AutoFilter criteria changes. Ok, ensure you have AutoFilter applied to your table. If you don't, the function will return #VALUE! Now, in, say A1 Enter =AutoFilter_Criteria(A4) (A4 is the 1st heading) and copy across as many columns as you have headings. Now simply filter by any criteria and the function will display your criteria in the relative column cell.

COLOR CODE WITH CALCULATE EVENT (Autofilter)

COLOR CODE WITH CALCULATE EVENT

This one can be used in addition to the custom function above, or on its own. However, you really should have at least 1 volatile function on the Worksheet it is used in. To ensure this simply Enter =TODAY() in any cell. Right click on the Worksheets name tab, choose View Code and in here paste the exact code below.

Private Sub Worksheet_Calculate()
Dim lFilt As Long, lFiltArrows As Long
Dim lFiltRow As Long
On Error Resume Next
Application.EnableEvents =
False
lFiltRow = Me.AutoFilter.Range.Row
lFiltArrows =
Me.AutoFilter.Filters.Count

Range(Cells(lFiltRow, 1), Cells(lFiltRow, _
lFiltArrows)).Interior.ColorIndex = xlNone
If Me.FilterMode = True Then
For lFilt = 1 To lFiltArrows
If Me.AutoFilter.Filters.Item(lFilt).On
Then
Cells(lFiltRow, lFilt).Interior.ColorIndex = 46
End If
Next
lFilt
End If
Application.EnableEvents = True
On Error GoTo 0
End
Sub

Come back to Excel and again filter your table. You will note that this one will automatically detect your headings which have AutoFilter applied. When no criteria is set, in others words not filtering, no color will change.

Wednesday, November 01, 2006

http://www.cpearson.com/excel/vbe.htm

http://www.cpearson.com/excel/vbe.htm

Getting A Reference To An Object
The first step in programming to the VBE is to get a reference to object you need to work with.
VBProject Dim VBProj As VBProjectSet VBProj = ThisWorkbook.VBProject VBComponent Dim VBComp As VBComponentSet VBComp = ThisWorkbook.VBProject.VBComponents("Module1") CodeModule Dim VBCodeMod As CodeModuleSet VBCodeMod = ThisWorkbook.VBProject.VBComponents("Module1").CodeModule
In all of the examples in this page, we'll be working with the ThisWorkbook object -- working with the VBA components in the workbook which contains the code. Of course, you can work with any open workbook, by using ActiveWorkbook or Workbooks("SomeBook.xls").
Adding A Module To A Workbook
The procedure below will add a new module named "NewModule" to ThisWorkbook.
Sub AddModule()Dim VBComp As VBComponentSet VBComp = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_StdModule)VBComp.Name = "NewModule"Application.Visible = TrueEnd Sub
When you run this code from Excel while the VBE is open, you will be taken to the new module's code module, and the macro will terminate. When you run this code while the VBE is not open, your Excel application will be visible, but will not have focus. The statement Application.Visible = True returns focus back to the Excel application.
Deleting A Module From A Workbook
The procedure below will delete the module named "NewModule" from ThisWorkbook.
Sub DeleteModule()Dim VBComp As VBComponentSet VBComp = ThisWorkbook.VBProject.VBComponents("NewModule")ThisWorkbook.VBProject.VBComponents.Remove VBCompEnd Sub
You cannot delete the ThisWorkbook object module, or a sheet object module, or a chart object module.

Adding A Procedure To A Module
The procedure below will add a new procedure called "MyNewProcedure" to the module named "NewModule" in ThisWorkbook.
Sub AddProcedure()Dim VBCodeMod As CodeModuleDim LineNum As LongSet VBCodeMod = ThisWorkbook.VBProject.VBComponents("NewModule").CodeModuleWith VBCodeMod LineNum = .CountOfLines + 1 .InsertLines LineNum, _"Sub MyNewProcedure()" & Chr(13) & _" Msgbox ""Here is the new procedure"" " & Chr(13) & _"End Sub"End WithApplication.Run "MyNewProcedure"End Sub
Pay attention to the way in which the .InsertLines method is called. The entire procedure is passed as one argument -- a string with embedded Chr(13) characters for the line breaks. The code statement
Application.Run "MyNewProcedure"
will run the new procedure. You must use Application.Run rather than calling the procedure directly in order to prevent compile-time errors. This method will work only if you are adding code to another code module. If you are adding code a the same code module, you must use an Application.OnTime method, so that control is returned to Excel, and the module can be recompiled and reloaded. Using Application.OnTime may have some synchronizations problems, so you should avoid calling a procedure that you've just added to the same code module without allowing all VBA procedures to come to an end.
Application.OnTime Now,"NewProcedureName"
Creating An Event Procedure
The CodeModule object has a method called CreateEventProc that you can use to create an event procedure in and class module, a sheet object module, or the ThisWorkbook object module. The advantage of CreateEventProc over InsertLines is that CreateEventProc will automatically insert the complete procedure declaration, including all of the correct parameters. CreateEventProc returns the line number on which the procedure begins, so once you've called CreateEventProc , add one to the result and use this with InsertLines to insert the body of the event procedure. For example, the code below creates a Workbook_Open procedure containing a Msgbox statement in the ThisWorkbook module of the Active Workbook.
Dim StartLine As LongWith ActiveWorkbook.VBProject.VBComponents("ThisWorkbook").CodeModule StartLine = .CreateEventProc("Open", "Workbook") + 1 .InsertLines StartLine, _ "Msgbox ""Hello World"",vbOkOnly"End With
Deleting A Procedure From A Module
The procedure below will delete the procedure called "MyNewProcedure" from the module named "NewModule" in ThisWorkbook.
Sub DeleteProcedure()Dim VBCodeMod As CodeModuleDim StartLine As LongDim HowManyLines As LongSet VBCodeMod = ThisWorkbook.VBProject.VBComponents("NewModule").CodeModuleWith VBCodeMod StartLine = .ProcStartLine("MyNewProcedure", vbext_pk_Proc) HowManyLines = .ProcCountLines("MyNewProcedure", vbext_pk_Proc) .DeleteLines StartLine, HowManyLinesEnd WithEnd Sub
Deleting All Code From A Module
The procedure below will delete all code from a module name "NewModule".
Sub DeleteAllCodeInModule()Dim VBCodeMod As CodeModuleDim StartLine As LongDim HowManyLines As LongSet VBCodeMod = ThisWorkbook.VBProject.VBComponents("NewModule").CodeModuleWith VBCodeMod StartLine = 1 HowManyLines = .CountOfLines .DeleteLines StartLine, HowManyLinesEnd WithEnd Sub

Listing All Modules In A Workbook
The procedure below will list, in a message box, all of the modules in ThisWorkbook. It uses a function called CompTypeToName to get a string describing the type of module. The function CompTypeToName is listed below.
Sub ListModules()Dim VBComp As VBComponentDim Msg As StringFor Each VBComp In ThisWorkbook.VBProject.VBComponents Msg = Msg & VBComp.Name &amp; " Type: " & CompTypeToName(VBComp) & Chr(13)Next VBCompMsgBox MsgEnd Sub
Function CompTypeToName(VBComp As VBComponent) As StringSelect Case VBComp.Type Case vbext_ct_ActiveXDesigner CompTypeToName = "ActiveX Designer" Case vbext_ct_ClassModule CompTypeToName = "Class Module" Case vbext_ct_Document CompTypeToName = "Document" Case vbext_ct_MSForm CompTypeToName = "MS Form" Case vbext_ct_StdModule CompTypeToName = "Standard Module" Case ElseEnd SelectEnd Function
Listing All Procedures In A Module
The procedure below will list, in a message box, all of the procedures in a standard code module called "SaveModule" in ThisWorkbook. Procedures are listed in the order in which they appear in the CodeModule object.
Sub ListProcedures()Dim VBCodeMod As CodeModuleDim StartLine As LongDim Msg As StringDim ProcName As StringSet VBCodeMod = ThisWorkbook.VBProject.VBComponents("SaveModule").CodeModuleWith VBCodeMod StartLine = .CountOfDeclarationLines + 1 Do Until StartLine >= .CountOfLines Msg = Msg & .ProcOfLine(StartLine, vbext_pk_Proc) & Chr(13) StartLine = StartLine + _ .ProcCountLines(.ProcOfLine(StartLine, _ vbext_pk_Proc), vbext_pk_Proc) LoopEnd WithMsgBox MsgEnd Sub
Also see Code Modules And Code Names for more information about the CodeName property of VBComponents.
Exporting All Modules In A Project
The procedure below will list export all of the modules in a workbook to text files. It will save the files in the same folder as the workbook. This can be useful for saving a backup copy of your VBA, or for transferring VBA code from one project to another.
Sub ExportAllVBA()Dim VBComp As VBIDE.VBComponentDim Sfx As StringFor Each VBComp In ActiveWorkbook.VBProject.VBComponents Select Case VBComp.Type Case vbext_ct_ClassModule, vbext_ct_Document Sfx = ".cls" Case vbext_ct_MSForm Sfx = ".frm" Case vbext_ct_StdModule Sfx = ".bas" Case Else Sfx = "" End Select If Sfx <> "" Then VBComp.Export _ Filename:=ActiveWorkbook.Path & "\" & VBComp.Name & Sfx End IfNext VBCompEnd Sub
Deleting All VBA Code In A Project
The procedure below will delete all the VBA code in a project. You should use this procedure with care, as it will permanently delete the code. Standard modules, user forms, and class modules will be removed, and code within the ThisWorkbook module and the sheet modules will be deleted. You may want to export the VBA code, using the procedure above, before deleting the VBA code.
Sub DeleteAllVBA()Dim VBComp As VBIDE.VBComponentDim VBComps As VBIDE.VBComponentsSet VBComps = ActiveWorkbook.VBProject.VBComponentsFor Each VBComp In VBComps Select Case VBComp.Type Case vbext_ct_StdModule, vbext_ct_MSForm, _ vbext_ct_ClassModule VBComps.Remove VBComp Case Else With VBComp.CodeModule .DeleteLines 1, .CountOfLines End With End SelectNext VBCompEnd Sub
Copying Modules Between Projects
There isn't a single method to copy modules from one VBProject to another. Instead, you have to export the module from one project, and then import it into another. The following procedure will copy Module1 from Book2 to Book1.
Sub CopyOneModule()Dim FName As StringWith Workbooks("Book2") FName = .Path & "\code.txt" .VBProject.VBComponents("Module1").Export FNameEnd WithWorkbooks("book1").VBProject.VBComponents.Import FNameEnd Sub
Just change "Module1" to the name of the module you want to copy. If you want to copy all modules (except the ThisWorkbook and Sheet modules), you can use the following code.
Sub CopyAllModules()Dim FName As StringDim VBComp As VBIDE.VBComponentWith Workbooks("Book2") FName = .Path & "\code.txt" If Dir(FName) <> "" Then Kill FName End If For Each VBComp In .VBProject.VBComponents If VBComp.Type <> vbext_ct_Document Then VBComp.Export FName Workbooks("book1").VBProject.VBComponents.Import FName Kill FName End If Next VBCompEnd WithEnd Sub

Testing Existence Of A Module Or Procedure
You can use the VBA Extensibility tools to determine whether a module exists, or a procedure exists in a module.
Function ModuleExists(ModuleName As String) As BooleanOn Error Resume NextModuleExists = Len( _ThisWorkbook.VBProject.VBComponents(ModuleName).Name) <> 0End Function
Function ProcedureExists(ProcedureName As String, _ ModuleName As String) As BooleanOn Error Resume NextIf ModuleExists(ModuleName) = True Then ProcedureExists = ThisWorkbook.VBProject.VBComponents(ModuleName) _ .CodeModule.ProcStartLine(ProcedureName, vbext_pk_Proc) <> 0End IfEnd Function
Renaming Code Modules
You can rename VBA's code modules with code like ThisWorkbook.VBProject.VBComponents("Module1").Name = "NewModule"
This code will work with any VBComponent, including the built-in components such as the sheet modules and the ThisWorkbook module:
ThisWorkbook.VBProject.VBComponents("ThisWorkbook").Name = "MyWorkbook"
Eliminating Screen FlickeringWhen you use code to write code, the VBA Editor displays itself. Broadly speaking, this is undesirable. You can reduce this to a flicker by using code like
Application.VBE.MainWindow.Visible = False'' your code to add code'Application.VBE.MainWindow.Visible = True
This will close the VBA Editor, but you will still see the editor appear momentarily and then hide itself. To prevent this screen flickering, you need to use the LockWindowUpdate API function. Put the following function declares at the top of your code module, before and outside of any procedures.
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _ (ByVal ClassName As String, ByVal WindowName As String) As LongPrivate Declare Function LockWindowUpdate Lib "user32" _ (ByVal hWndLock As Long) As Long
Then, in your code, use code like the following:
Dim VBEHwnd As LongOn Error Goto ErrH:Application.VBE.MainWindow.Visible = FalseVBEHwnd = FindWindow("wndclass_desked_gsk", _ Application.VBE.MainWindow.Caption)If VBEHwnd Then LockWindowUpdate VBEHwndEnd If'' your code to write code'Application.VBE.MainWindow.Visible = FalseErrH:LockWindowUpdate 0&
You may still see the title bar of Excel momentarily dim, but the VBA Editor will not be visible at all. If you already have error handling code in your procedure that writes the VBA code, you want to be sure to call LockWindowUpdate 0&.The code above will work in Excel 2000 and later. It has not been tested in Excel97.