All you have to do is select the table range, then run the **ExportRangeToXML **procedure. You'll be prompted for a filename and location to save the XML file. You can customise the XML Element names used in the XML structure for the table and rows. The table headers are used for Element names in each row of data.

```
'*****************************************************
' Purpose: Exports a selected range (table) to an xml
' format using table headers.
' Notes: Requires entry of file name. Uses table headers
' as Element names for each row.
'
' Developer: Date: Description:
' Chris Read 24/04/14 Created.
'*****************************************************
Public Sub ExportRangeToXML()
Dim strXML As String
Dim varTable As Variant
Dim intRow As Integer
Dim intCol As Integer
Dim intFileNum As Integer
Dim strFilePath As String
Dim strRowElementName As String
Dim strTableElementName As String
Dim varColumnHeaders As Variant
'Set custom names
strTableElementName = "Table"
strRowElementName = "Row"
'Set file path
strFilePath = Application.GetSaveAsFilename(, "(*.xml),*.xml", , "Save As...")
If strFilePath = vbNullString Then Exit Sub
'Get table data
varTable = Selection.Value
varColumnHeaders = Selection.Rows(1).Value
'Build xml
strXML = ""
strXML = strXML & "<" & strTableElementName & ">"
For intRow = 2 To UBound(varTable, 1)
strXML = strXML & "<" & strRowElementName & ">"
For intCol = 1 To UBound(varTable, 2)
strXML = strXML & "<" & varColumnHeaders(1, intCol) & ">" & _
varTable(intRow, intCol) & ""
Next
strXML = strXML & ""
Next
strXML = strXML & ""
'Get next file number
intFileNum = FreeFile
'Open the file, write output, then close file
Open strFilePath For Output As #intFileNum
Print #intFileNum, strXML
Close #intFileNum
End Sub
```

]]>A reader of my blog was after some code to do something a little different to a previous post which looked at replacing the Excel 2007 **IFERROR** function with IF and ISERROR.

The slight twist on this was when you wanted to get rid of the error trapping (IF and ISERROR) and just have the original formula, warts (errors) and all!

In the VBA code below, I have developed a handy procedure (ReplaceIFISERRORCellFormulas) that will convert a range of selected cells that contain **IF(ISERROR())** formulas to instead display the formula used in the 'value_if_false' argument of the original **IF(****ISERROR()) **function.

Hope you all find it useful!

```
'*******************************************************************
'Developer: Chris Read
'Objective: Replaces all formulas in a selected range that contain
' IF(ISERROR()) formulas with the 'value_if_false' argument of the IF(ISERROR()).
' formulas.
'Parameters:
'Returns:
'Change log:
' Developer: Date: Description:
' CR 23/04/13 Created
'*******************************************************************
Public Sub ReplaceIFISERRORCellFormulas()
Dim rngSelection As Range
Dim rngCell As Range
Dim strFormula As String
On Error GoTo ErrHandler
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Set rngSelection = Application.Selection
For Each rngCell In rngSelection
strFormula = rngCell.Formula
Do Until InStr(1, strFormula, "=IF(ISERROR") = 0
strFormula = RemoveIFISERROR(strFormula)
Loop
rngCell.Formula = strFormula
Next
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Application.CalculateFullRebuild
ErrHandler:
If Err.Number <> 0 Then
MsgBox "Error in ReplaceIFISERRORCellFormulas:" & vbCrLf & vbCrLf & Err.Number & ": " & Err.Description
End If
End Sub
'*******************************************************************
'Developer: Chris Read
'Objective: Gets the end position of the IF(ISERROR()) formula within
' the formula string.
'Parameters: strFormula
'Returns: GetIFISERROREnd
'Change log:
' Developer: Date: Description:
' CR 23/04/13 Created
'*******************************************************************
Public Function GetIFISERROREnd(ByVal strFormula) As Long
Dim lngBracketsCount As Long
Dim lngPosition As Long
On Error GoTo ErrHandler
lngPosition = 1
lngBracketsCount = 0
Do Until lngPosition = Len(strFormula) + 1
If Mid(strFormula, lngPosition, 1) = "(" Then lngBracketsCount = lngBracketsCount + 1
If Mid(strFormula, lngPosition, 1) = ")" Then
lngBracketsCount = lngBracketsCount - 1
If lngBracketsCount = 0 Then Exit Do
End If
lngPosition = lngPosition + 1
Loop
GetIFISERROREnd = lngPosition
ErrHandler:
If Err.Number <> 0 Then
MsgBox "Error in GetIFISERROREnd:" & vbCrLf & vbCrLf & Err.Number & ": " & Err.Description
End If
End Function
'*******************************************************************
'Developer: Chris Read
'Objective: Gets the start position of the 'value_if_false' argument
' of the IF(ISERROR()).
'Parameters: strFormula
'Returns: GetIFISERRORFalseArgumentStart
'Change log:
' Developer: Date: Description:
' CR 23/04/13 Created
'*******************************************************************
Public Function GetIFISERRORFalseArgumentStart(ByVal strFormula) As Long
Dim lngBracketsCount As Long
Dim lngPosition As Long
On Error GoTo ErrHandler
lngPosition = Len(strFormula)
lngBracketsCount = 0
Do Until lngPosition = 0
If Mid(strFormula, lngPosition, 1) = ")" Then lngBracketsCount = lngBracketsCount + 1
If Mid(strFormula, lngPosition, 1) = "(" Then lngBracketsCount = lngBracketsCount - 1
If Mid(strFormula, lngPosition, 1) = "," And lngBracketsCount = 0 Then Exit Do
lngPosition = lngPosition - 1
Loop
GetIFISERRORFalseArgumentStart = lngPosition + 1
ErrHandler:
If Err.Number <> 0 Then
MsgBox "Error in GetIFISERRORFalseArgumentStart:" & vbCrLf & vbCrLf & Err.Number & ": " & Err.Description
End If
End Function
'*******************************************************************
'Developer: Chris Read
'Objective: Replaces an IFERROR formula with a combination of
' IF and ISERROR formulas.
'Parameters: strFormula
'Returns: RemoveIFISERROR
'Change log:
' Developer: Date: Description:
' CR 23/04/13 Created
'*******************************************************************
Public Function RemoveIFISERROR(strFormula) As String
Dim strIFISERRORFunctionResult As String
Dim lngIFISERRORFunctionResultStart As Long
Dim lngIFISERRORFunctionResultEnd As Long
Dim strReplacementFunction As String
Dim lngStart As Long
Dim lngEnd As Long
On Error GoTo ErrHandler
lngStart = InStr(1, strFormula, "IF(ISERROR")
lngEnd = GetIFISERROREnd(Mid(strFormula, lngStart, Len(strFormula))) + lngStart - 1
lngIFISERRORFunctionResultEnd = lngEnd - 1
lngIFISERRORFunctionResultStart = GetIFISERRORFalseArgumentStart(Mid(strFormula, lngStart, lngEnd - lngStart)) + lngStart - 1
strIFISERRORFunctionResult = Mid(strFormula, lngIFISERRORFunctionResultStart, lngIFISERRORFunctionResultEnd - lngIFISERRORFunctionResultStart + 1)
strReplacementFunction = strIFISERRORFunctionResult
RemoveIFISERROR = Mid(strFormula, 1, lngStart - 1) & strReplacementFunction & Mid(strFormula, lngEnd + 1, Len(strFormula))
ErrHandler:
If Err.Number <> 0 Then
MsgBox "Error in RemoveIFISERROR:" & vbCrLf & vbCrLf & Err.Number & ": " & Err.Description
End If
End Function
```

]]>The trouble is, you can't always guarantee that the users PC has an internet connection. So, what's the best approach to the problem? Well, check the internet connection using the handy bit if VBA code below!

```
Private Declare Function InternetGetConnectedStateEx Lib "wininet.dll" (ByRef lpdwFlags As Long, ByVal lpszConnectionName As String, ByVal dwNameLen As Integer, ByVal dwReserved As Long) As Long
'Testing for internet connection
Public Function IsInternetConnected() As Boolean
IsInternetConnected = InternetGetConnectedStateEx(0, "", 254, 0)
End Function
```

]]>This tip was sparked by a colleague's question the other day, and since I'd recently used the technique in a risk register tool, I thought there might be many others out there wondering how best to lookup matrix values.

The premise is that you have a two dimensional matrix of values and you want to retrieve a value from the matrix, given that you know the input values for the two dimesions of the matrix.

So, here's your matrix..

and here's what your after in your results table..

The method uses a combination of the INDEX and MATCH functions to give:

=INDEX(MatrixRange,MATCH(RowValueLookup,RowCategories,0),MATCH(ColumnValueLookup,ColumnCategories,0))

You also might have the situation where you have a numerical scale that you classify with banded categories (instead of fixed categories). In this situation your matrix might look like this..

For the solution to the second problem check out the attached file below. Looking at how both types of matrix lookup are implemented will explain it far better than me rabbiting on about it... i.e. I'm being lazy.

]]>Here we have some data I made earlier... a table of order data and a list of orders.

What we’re aiming to do is create a magical Scrolling Dynamic Lookup window to add to our equally awesome report, like so:

In order to make our life that little bit easier – and to help you understand the formulas – I first created some Named Ranges, just like the ones below.

The next step is to create the structure for our scrolling ‘window’. This repeats the column headings from the order data table and adds an additional Item column, which the dynamic lookup formulas will reference.

In D2 we need to create a Data Validation rule that provides the drop down filter to select the particular order that will be displayed. As you can see below, this references one of the Named Ranges we already setup:

In case you’re wondering where the vertical scroll bar came from, this is a Form control that was added using the developer tab.

NB: If you can’t see the developer tab, you need to turn it on by opening the **Excel Options** form (Office Button > Excel Options). In the **Popular** tab you need to check **Show Developer tab in the Ribbon**.

In order to complete the setup of the vertical scrollbar, right click on the scrollbar object and select **Format Control**; you should now see the following form:

The key parameter is the **Cell link**, which needs to refer to B4. You can also modify other parameters that will affect how the scrollbar operates. For example, I set a **Maximum value** of 50, but if I had any orders that had more than 54 products (50 plus the 4 rows below the first row), they wouldn’t all be displayed. To make this truly automated, the next step would be to add some VBA code to update the **Maximum value **based on the maximum order size, but that’s for another time.

Now we have the scrolling window created, all we have to do is throw in the magical Array formula in C4 and copy to all remaining cells (remembering to hold CTRL + SHIFT while we hit ENTER!):

={IFERROR(INDEX(DataTable,SMALL(IF(DataTable=$D$2,ROW(DataTable)),$B4),MATCH(C$3,DataTableHeaders,0)),"")}

**Some explanation...**

The MATCH function returns the column number in the order data table

MATCH(C$3,DataTableHeaders,0)

The SMALL function returns the column number in the order data table. This utilises the Order ID filter that we created in D2 and helps to make the formula dynamic. This is the Array part of the formula requiring CTRL+SHIFT+ENTER.

SMALL(IF(DataTable=$D$2,ROW(DataTable)),$B4)

The index function returns the value from the order data table using the column and row number that the SMALL and MATCH functions have returned.

INDEX(DataTable,SMALL(...),MATCH(...))

Finally surrounding our INDEX function with the IFERROR function, allows us to show nothing if the INDEX function returns an error when no value exists.

IFERROR(INDEX(...),"")

Depending on how you need to implement this in a report, there are a number of tweaks you can perform from simple formatting, to removing unnecessary columns or increasing the number of rows in the Dynamic Scrolling Lookup.

Now, download and get learning!

]]>The key technique employed utilises **Array formulas**, which I won’t go into detail about, mainly because plenty before me have (see links below). The quick 'how-to' is that you need to hold down the CTRL & SHIFT keys when pressing ENTER to commit the formula, instead of just hitting ENTER. Their other common name is a ‘**CSE formula**’ (**C**ontrol, **S**hift, **E**nter). When committed, the formula is surrounded by curly {brackets}.

There are plenty of useful guides out there for Array formulas, including:

- http://www.cpearson.com/excel/ArrayFormulas.aspx
- http://www.mrexcel.com/articles/CSE-array-formulas-excel.php
- http://www.ozgrid.com/Excel/arrays.htm

So, here’s the sample data to show you the technique (not necessarily sorted by Supplier, as is the case below)...

And here’s what we want to end up with in our complex transpose result...

In order to obtain this result we need to throw the following functions into the mixing bowl...

- IFERROR – for cleaning up when we run out of product IDs.
- INDEX – for returning the Product ID from a specified row.
- SMALL – for returning the k-th smallest number in an array.
- ROW – for returning the row number of each Supplier.
- COLUMN – for calculating the column number in the output table.

Throwing them into cell E2 - beating with a whisk while adding water - gives the following scary combination...

={IFERROR(INDEX($B$2:$B$11,SMALL(IF($A$2:$A$11=$D2,ROW($A$2:$A$11)-1),COLUMN()-COLUMN($D2))),"")}

Time to dissect and see what we have!

**This part gets the row number we're after in the Supplier Range
**SMALL(IF(SupplierRange=SupplierName,ROW(SupplierRange)-1),COLUMN()-COLUMN(SupplierName))

**The row number is then used to get the Product ID for that row
**INDEX(ProductRange, SMALL(...))

**We then trap any errors when a Product ID doesn't exist for the given column of the results**

IFERROR(INDEX(...),"")

NB: The IF function is the array formula part, which requires CSE.

Simple right? Well, probably not at first glance. I find the best way to learn is to get your hands dirty, so download the sample file below and get learning!

I’ve included versions for Excel 2003 and 2007, because the IFERROR function didn’t exist in Excel 2003. Therefore the 2003 version utilises the IF(ISERROR()) workaround. See my last post about replacing these if you’re interested.

]]>In Excel 2007, trapping and handling errors in a formula was made easier with the introduction of the **IFERROR** function, which removed the need to specify your formula twice, e.g. =IFERROR(MyFormula,ReturnSomethingElse).

With organisations upgrading to Office (Excel) 2007 at different times, compatibility can become an issue. Although installing the Microsoft Office Compatibility Pack will enable users of previous versions of Excel to open an Excel 2007 file, you can't rely on this being installed. Also, you may want to convert an Excel 2007 file to a previous version for people to use / modify; In Excel 2007, simply 'saving as' an older version (e.g. saving an 'xlsx' as an 'xls' file) will often bring up a load of compatibility warning messages, which aren't fixed automatically.

Through my need to develop in all versions of Excel, I developed a handy procedure (ReplaceIFERRORCellFormulas) that will convert a range of selected cells that contain **IFERROR** formulas to instead use a combination of the **IF** and **ISERROR **functions. There's also a procedure to check all Named Ranges (ReplaceIFERRORNamedRangeFormulas), in case you use them to store formulas.

Excel 2002 VBA: Programmers Reference

This was the first book I bought on Excel VBA and it has served me well. It covers a broad subject base for both beginner and expert Excel programmer, and for a reference book, has a detailed index for subject lookup.

Professional Excel Development: The Definitive Guide to Developing Applications Using Microsoft Excel and VBA

This book is more for the expert developer and looks at creating professional applications in Excel. The book shows Excel as more than just a spreadsheet tool, its a development platform. I would say it's a must for the serious Excel developer.

Professional Excel Development (2nd Edition): The Definitive Guide to Developing Applications Using Microsoft Excel and VBA

An updated version of the best book on the market for Excel developers. Extremely useful for the latest versions (2007 onwards) of Excel.