Working with Tables in Excel (VBA)

Introduction

In Working with Tables in Excel I promised to add a page about working with those tables in VBA too. Well, here you go.

It's a ListObject!

On the VBA side there seems to be nothing new about Tables. They are addressed as ListObjects, a collection that was introduced with Excel 2003. But there are significant changes to this part of the object model and I am only going to touch on the basic parts here.

Creating a table

Converting a range to a table is simple enough:

 Sub CreateTable()
    ActiveSheet.ListObjects.Add(xlSrcRange, Range("$B$1:$D$16"), , xlYes).Name = _
        "Table1"
    ActiveSheet.ListObjects("Table1").TableStyle = "TableStyleLight2"
End Sub

Table formatting is determined by TableStyles. A collection of objects which are a member of the Workbook object. This gives rise to some oddities. You can change the formatting of a tableStyle, e.g. like this:

Sub ChangeTableStyles()
    ActiveWorkbook.TableStyles(2).TableStyleElements(xlWholeTable) _
        .Borders(xlEdgeBottom).LineStyle = xlDash
End Sub

This changes the linestyle of the bottom of your table. But hold your horses! If you have any other workbook open, all tables with the same tablestyle now use your changed style! But if you save your file, close Excel and open Excel again with the file, the changes are gone. This is because you've just changed a built-in tablestyle. If you ask me, I find it strange that the Workbook is a tablestyles' parent, whereas built-in table styles behave as if being bound to the Application object.

If you want full control over your table style, you'd better duplicate a built-in style and modify and apply that style to your table.

Listing the tables

Let's start with finding all tables on the active worksheet:

Sub FindAllTablesOnSheet()
    Dim oSh As Worksheet
    Dim oLo As ListObject
    Set oSh = ActiveSheet
    For Each oLo In oSh.ListObjects
        Application.Goto oLo.Range
        MsgBox "Table found: " & oLo.Name & ", " & oLo.Range.Address
    Next
End Sub

Selecting parts of tables

You might need to work with specific parts of a table. Here is a couple of examples on how to achieve that.

Sub SelectingPartOfTable()
    Dim oSh As Worksheet
    Set oSh = ActiveSheet
    '1: with the listobject
    With oSh.ListObjects("Table1")
        MsgBox .Name
        'Select entire table
        .Range.Select
        'Select just the data of the entire table
        .DataBodyRange.Select
        'Select third column
        .ListColumns(3).Range.Select
        'Select only data of first column
        .ListColumns(1).DataBodyRange.Select
        'Select just row 4 (header row doesn't count!)
        .ListRows(4).Range.Select
    End With
   
    '2: with the range object
    'select an entire column (data only)
    oSh.Range("Table1[Column2]").Select
    'select an entire column (data plus header)
    oSh.Range("Table1[[#All],[Column1]]").Select
    'select entire data section of table
    oSh.Range("Table1").Select
    'select entire table
    oSh.Range("Table1[#All]").Select
    'Select one row in table
    oSh.Range("A5:F5").Select
End Sub

As you may have spotted, current Excel versions handle tables like they are range names. Well, that is exactly what is going on. After inserting a table, a range name is defined automatically. These range names are special though. Excel controls them entirely. You cannot delete them and they get renamed automatically when you change a table's name. Remove a table (convert back to range) and the defined name is removed as well.

Inserting rows and columns

Another part in which lists already had most of the functionality. Just a few new things have been added, like the "AlwaysInsert" argument to the ListRows.Add method:

Sub TableInsertingExamples()
'insert at specific position
    Selection.ListObject.ListColumns.Add Position:=4
'insert right
    Selection.ListObject.ListColumns.Add
'insert above
    Selection.ListObject.ListRows.Add (11)
'insert below
    Selection.ListObject.ListRows.Add AlwaysInsert:=True
End Sub

If you need to do something with a newly inserted row, you can set an object variable to the new row:

     Dim oNewRow As ListRow
    Set oNewRow = Selection.ListObject.ListRows.Add(AlwaysInsert:=True)

If you then want to write something in the first cell of the new row you can use:

oNewRow.Range.Cells(1,1).Value = "Value For New cell"

Adding a comment to a table

Adding a comment to a table through the UI is a challenge, because you have to go to the Name Manager to do that. In VBA the syntax is:

Sub AddComment2Table()
    Dim oSh As Worksheet
    Set oSh = ActiveSheet
    'add a comment to the table (shows as a comment to
    'the rangename that a table is associated with automatically)
    'Note that such a range name cannot be deleted!!
    'The range name is removed as soon as the table is converted to a range
    oSh.ListObjects("Table1").Comment = "This is a table's comment"
End Sub

Convert a table back to a normal range

That is simple:

Sub RemoveTableStyle()
    Dim oSh As Worksheet
    Set oSh = ActiveSheet
    'remove table or list style
    oSh.ListObjects("Table1").Unlist
End Sub

Special stuff: Sorting and filtering

With tables, we get a whole new set of filtering and sorting options. I'm only showing a tiny bit here, a Sort on cell color (orangish) and a filter on the font color.

Sub SortingAndFiltering()
'NoGo in 2003
    With ActiveWorkbook.Worksheets("Sheet1").ListObjects("Table1")

        .Sort.SortFields.Clear
        .Sort.SortFields.Add( _
                Range("Table1[[#All],[Column2]]"), xlSortOnCellColor, xlAscending, , _
                xlSortNormal).SortOnValue.Color = RGB(255, 235, 156)
        With .Sort
            .Header = xlYes
            .MatchCase = False
            .Orientation = xlTopToBottom
            .SortMethod = xlPinYin
            .Apply
        End With
    End With
    ActiveSheet.ListObjects("Table1").Range.AutoFilter Field:=2, _
        Criteria1:=RGB(156, 0, 6), Operator:=xlFilterFontColor
End Sub

Accessing the formatting of a cell inside a table

You may wonder why this subject is there, why not simply ask for the cell.Interior.ThemeColor if you need the ThemeColor of a cell in a table? Well, because the cell formatting is completely prescribed by the settings of your table and the table style that  has been selected. So in order to get at a formatting element of a cell in your table you need to:

  • Find out where in your table the cell is located (on header row, on first column, in the bulk of the table
  • Determine the table settings: does it have row striping turned on, does it have a specially formatted first column, ...
  • Based on these pieces of information, one can extract the appropriate TableStyleElement from the table style and read its properties.

The function shown here returns the TableStyleElement belonging to a cell oCell inside a table object called oLo:

Function GetStyleElementFromTableCell(oCell As Range, oLo As ListObject) As TableStyleElement
'-------------------------------------------------------------------------
' Procedure : GetStyleElementFromTableCell
' Company   : JKP Application Development Services (c)
' Author    : Jan Karel Pieterse
' Created   : 2-6-2009
' Purpose   : Function to return the proper style element from a cell inside a table
'-------------------------------------------------------------------------
    Dim lRow As Long
    Dim lCol As Long
    'Determine on what row we are inside the table
    lRow = oCell.Row - oLo.DataBodyRange.Cells(1, 1).Row
    lCol = oCell.Column - oLo.DataBodyRange.Cells(1, 1).Column

    With oLo
        If lRow < 0 And .ShowHeaders Then
            'on first row and has header
            Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlHeaderRow)
        ElseIf .ShowTableStyleFirstColumn And lCol = 0 Then
            'On first column and has first column style
            Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlFirstColumn)
        ElseIf .ShowTableStyleLastColumn And lCol = oLo.Range.Columns.Count - 1 Then
            'On last column and has last col style
            Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlLastColumn)
        ElseIf lRow = .DataBodyRange.Rows.Count And .ShowTotals Then
            'On last row and has total row
            Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlTotalRow)
        Else
            If .ShowTableStyleColumnStripes And Not .ShowTableStyleRowStripes Then
                'in table, has column stripes
                If lCol Mod 2 = 0 Then
                    Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlColumnStripe1)
                Else
                    Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlWholeTable)
                End If
            ElseIf .ShowTableStyleRowStripes And Not .ShowTableStyleColumnStripes Then
                'in table, has column stripes
                If lRow Mod 2 = 0 Then
                    Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlRowStripe1)
                Else
                    Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlWholeTable)
                End If
            ElseIf .ShowTableStyleColumnStripes And .ShowTableStyleRowStripes Then
                If lRow Mod 2 = 0 And lCol Mod 2 = 0 Then
                    Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlRowStripe1)
                ElseIf lRow Mod 2 <> 0 And lCol Mod 2 = 0 Then
                    Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlColumnStripe1)
                ElseIf lRow Mod 2 = 0 And lCol Mod 2 <> 0 Then
                    Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlRowStripe1)
                Else
                    Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlWholeTable)
                End If
            End If
        End If
    End With

End Function

You could use this function like this:

Sub test()
    Dim oLo As ListObject
    Dim oTSt As TableStyleElement
    Set oLo = ActiveSheet.ListObjects(1)
    Set oTSt = GetStyleElementFromTableCell(ActiveCell, oLo)
    With ActiveCell.Offset(, 8)
        .Interior.ThemeColor = oTSt.Interior.ThemeColor
        .Interior.TintAndShade = oTSt.Interior.TintAndShade
    End With
End Sub

Removing formating from an Excel Table

Suppose you have just converted a range to a table, but the range had some formatting set up such as background fills and borders. Tables allow you to format things like that automatically, but now your preexisting formatting messes up the table formatting. One way to overcome this is by changing the style of the cells (see this article) in the table back to the Normal style. This however removes your number formats too. The little macro below fixes that by first making a copy of the normal style, setting its Number checkbox to false and then applying the new style without number format to the table. Finally it applies the tablestyle and deletes the temporary style:

Sub RemoveFormattingOfTable()
    Dim oStNormalNoNum As Style
    On Error Resume Next
    Set oStNormalNoNum = ActiveWorkbook.Styles("NormalNoNum")
    On Error GoTo 0
    If oStNormalNoNum Is Nothing Then
        ActiveWorkbook.Styles.Add "NormalNoNum"
        Set oStNormalNoNum = ActiveWorkbook.Styles("NormalNoNum")
        oStNormalNoNum.IncludeNumber = False
    End If
    With ActiveSheet.ListObjects(1)
        .Range.Style = "NormalNoNum"
        'Now apply tablestyle:
        .TableStyle = "TableStyleLight1"
    End With
    ActiveWorkbook.Styles("NormalNoNum").Delete
End Sub

Note that the function shown above does not take into account that you can set the width of the stripes, both vertically and horizontally.

Wrap Up

Of course there is more to learn and know about tables and lists. A good way to come acquainted with the VBA behind them is by recording macro's while fooling around with them. Luckily Microsoft did include the table object if it comes to recording your actions, unlike the omission on the charting side...


Comments

All comments about this page:


Comment by: Gilles Frechet (7-11-2007 14:57:31) deeplink to this comment

Hello:

I am currently trying to use a workbook which was developped using a prior version of Excel. This workbook contains multiple sheets and several large Macros. All of the Macros appear to work, however I have found that if I attempt to select a large range of cells in any of the sheets (by using the mouse or with the use of a Macro) the program slows down considerably and the larger the range selected, the longer the time it takes for the system to respond. I would appreciate any help and comments.


Comment by: Jan Karel Pieterse (7-11-2007 21:59:31) deeplink to this comment

Hi Gilles,

Without seeing the code this is hard to analyse. It may help to turn off screenupdating at the beginning of your code though:
Application.ScreenUpdating=false
Then at the end, turn it back on:
Application.ScreenUpdating=True


Comment by: Manuel (9-11-2007 07:40:44) deeplink to this comment

Necesito saber si como se aplica el
AutofilterMode?
Pasa activar o desactivar filtros


Comment by: Jan Karel Pieterse (10-11-2007 08:16:22) deeplink to this comment

Selection.AutoFilter

Will turn on autofilter

ActiveSheet.AutoFilterMode = False

will turn it off.


Comment by: akj (15-11-2007 23:31:19) deeplink to this comment

Excellent


Comment by: Johan Nordberg (8-12-2007 14:20:19) deeplink to this comment

An important difference between Excel 2003 lists and Excel 2007 tables is that the InsertRowRange property of the ListObject only works when the table is empty. When the table has data InsertRowRange returns nothing.

In that case you have to get the last row of the table and move down one row from that.

If anyone has a better solution, please let me know...

// Johan Nordberg


Comment by: Jan Karel Pieterse (9-12-2007 03:47:59) deeplink to this comment

Hi Johan,

Thanks for the comment. I think you have found the only solution to this problem indeed.


Comment by: Andrei Sheshka (9-1-2008 08:07:33) deeplink to this comment

Hi Johan!

At first you must activate ListObject to get InsertRowRange in Excel 2003.

Function GetInsertRow(objList As ListObject) As Range
objList.Parent.Activate
objList.Range.Activate
Set GetInsertRow = objList.InsertRowRange
End Function

Sub Test_GetInsertRow()
Dim lo As ListObject
Dim objListRng As Range

    Set lo = Worksheets("Sheet3").ListObjects(1)
    Set objListRng = GetInsertRow(lo)
    objListRng.Select

End Sub



Comment by: Jose Manuel (31-1-2008 12:57:16) deeplink to this comment

Hi to all!

After 'insert below
Selection.ListObject.ListRows.Add AlwaysInsert:=True
How can I select the cell in the first column of the new row?

Thanks in advance


Comment by: Jan Karel Pieterse (3-2-2008 22:20:12) deeplink to this comment

Hi Jose,

Like this:
    Selection.ListObject.Range.End(xlDown).Select


Comment by: Aindril De (29-5-2008 00:46:09) deeplink to this comment

This is real excellent stuff.

Can anyone advice any book that is available, that helps differentiate Excel 2003
VBA vs Excel 2007 VBA?

Thanks in advance


Comment by: Jan Karel Pieterse (29-5-2008 02:06:33) deeplink to this comment

Hi Aindril,

I'd recommend "Excel 2007 VBA programming Reference" (Stephen Bullen et al)
and
"Excel 2007 Power Programming with VBA" (John Walkenbach)


Comment by: Adele Summers (9-6-2008 08:19:09) deeplink to this comment

This may seem like a simple question, but can you set the data source of a table to
come from a sheet other than the current sheet that you are on?


Comment by: Jan Karel Pieterse (9-6-2008 10:18:44) deeplink to this comment

Hi Adele,

I'm not sure what you're looking for. What do you mean by "the data source"? Which
option are you referring to?


Comment by: Martin (19-6-2008 08:26:16) deeplink to this comment

i need call the dialog "Modify table Quick style"


Comment by: Jan Karel Pieterse (19-6-2008 10:51:14) deeplink to this comment

Hi Martin,

I had a look at Application.Dialogs(xlDialog......), but I could not find it.


Comment by: Ann Marie (1-7-2008 06:03:09) deeplink to this comment

How and where do you turn off screenupdating in Office Excel 2007?

Thank you.


Comment by: Jan Karel Pieterse (1-7-2008 10:21:56) deeplink to this comment

Hi,

Application.ScreenUpdating=False

at the start of your code

and

Application.ScreenUpdating=True

at the end.


Comment by: Matt (29-7-2008 15:38:08) deeplink to this comment

Fantastic Article! This has been extremely helpful in my projects.

One thing I'm struggling with is deleting multiple table rows. Recording a macro
of selecting the desired rows, right-clicking and selecting Delete > Table Rows
results in the following code repeated for each row selected:
Selection.ListObject.ListRows(1).Delete.
Running the macro is very, very slow relative to the action from the UI. I reduced
the code to loop through this, but it is still slow. I'm regularly deleting 1000+
rows. Any ideas as to how to streamline this?


Comment by: Mazhar Basa (3-10-2008 02:28:27) deeplink to this comment

I used the code for creating "table comment" however I cannot see anything on excel
sheet. When I msgbox the comment I can see it but on screen no?


Comment by: Jan Karel Pieterse (3-10-2008 05:17:10) deeplink to this comment

Hi Mazhar,

You see the comment if you type the table's name within a formula and you have
formula autocomplete turned on.


Comment by: Luc (6-10-2008 07:23:56) deeplink to this comment

Great job.


Comment by: Scott (17-10-2008 13:16:41) deeplink to this comment

How would you delete a table row based on selection.
"Selection.ListObject.ListRows.Delete"


Comment by: Jan Karel Pieterse (19-10-2008 21:22:17) deeplink to this comment

Hi Scott,

Not sure what you mean; is this a question or a suggestion?


Comment by: Michiel Kotting (16-1-2009 11:34:46) deeplink to this comment

How do you select the last row in a ListObject? In a normal range I use myRange.Rows(myRange.Rows.Count).Select, but in a ListObject I can't get it to work...

Similarly, how do I get the count of the number of rows in a ListObject?

thanks!


Comment by: Jan Karel Pieterse (18-1-2009 23:29:31) deeplink to this comment

Hi Michiel,

This selects the last row:

    Dim oL As ListObject
    Set oL = ActiveSheet.ListObjects(1)
    oL.DataBodyRange.Rows(oL.DataBodyRange.Rows.Count).Select


Comment by: Michiel Kotting (19-1-2009 01:28:25) deeplink to this comment

Thanks, it works! I also picked up Excel 2007 VBA by Bullen e.a. at your recomendation.


Comment by: Mohan Kumar Karunakaran (19-2-2009 15:35:27) deeplink to this comment

Hi,

I have created a table using VBA, but I really want to stop the default text entered in the first row of the table. I will be working mostly on financial tables, which doesn't have any value on the first cell. If I apply table style using VBA, it adds "Column1" for the first cell, which is not necessary. Do you have any idea on how to restrict this.

Thanks,
Mohan


Comment by: Jan Karel Pieterse (20-2-2009 05:11:21) deeplink to this comment

Hi Mohan,

You cannot prevent the title row from appearing, as Excel needs that for referencing columns in the table. But you can tell Excel to hide the title row by unchecking the box "Header row" on the table tools tab of the ribbon.


Comment by: Rangarajan Vijayaraghavan (3-3-2009 08:52:46) deeplink to this comment

Excellent work! Thanks a ton!


Comment by: Dian Leger (16-3-2009 09:35:37) deeplink to this comment

I am using Excel 2007.
When I click in a cell to enter data, a range of cells is automatically selected.
Is there a way to stop this so that when I select a cell only one cell is selected?


Comment by: Jan Karel Pieterse (16-3-2009 11:37:29) deeplink to this comment

Hi Dian,

Odd, that is abnormal behaviour. Please check out my page on Excel start up problems, especially the part about addins:

www.jkp-ads.com/articles/startupproblems.asp


Comment by: Radek Kukuczka (17-3-2009 10:10:05) deeplink to this comment

Hello,
First of all - thanks for this useful guide!
I have a problem, which you may be able to help solve... I've created spreadsheet which automaticaly calculates data, based on used values. I am storing data in Excel 2007 tables and use INDEX function in excel to select required data from specific row in the table. It's all working perfectly.
What I need to do now is add a userf form wizzard. Some fields are combo boxes, and I need to load data from a column into these combos. How do I inicialize form to include proper items from a specific table into this combo box? I hope this makes sense, I'd appreciate your help.
thanks, Radek


Comment by: Jan Karel Pieterse (17-3-2009 11:10:54) deeplink to this comment

Hi Radek,

You can add the contents of a column in a table to a listbox quite easily, for example:

Dim vValues As Variant
vValues = ActiveSheet.ListObjects(1).DataBodyRange.Columns(1).Value
ListBox1.List = vValues


Comment by: S Srinivas (23-3-2009 04:22:11) deeplink to this comment

Created a macro for sorting the excel worksheet according to colour . Created one command button and pasted the macro . Afterwardd when I run the command button , I am getting the following error.
Run-time error - 2147319765
Automation error
Element not found.
Pl help.
Thanks
Regards
S Srinivas


Comment by: Jan Karel Pieterse (23-3-2009 06:10:32) deeplink to this comment

Hi Srinivas,

I suggest you to go to this site to ask your question:

http://www.wopr.com/cgi-bin/w3t/login.pl?Cat=


Comment by: Radek Kukuczka (26-3-2009 09:54:14) deeplink to this comment

Hi Jan,
Thanks for the hint! It was very usefull.
Unfortunately I've hit another obstacle... Now, when I load the contens of column 1 to my user form, I need to relate the Cell Y in Row X with Cell Z in the same Row X.
I don't think this makes much sense... Let me explain.

I have a table with some data. In column 1 I have names which I load to the ComboBox in my user form. Column 2 contains a numeric ID(which isn't loaded anywhere), which I need to put in a specific cell when clicking OK in the form(this must be depending on what was choosen in the ComboBox).
I did some googling and this is what I've come up with. Please note the below doesn't work...

Dim i As Integer
For i = 1 To 29 // I have 29 rows in my table
    If comboBox1.Value = Worksheets("Data").ListObjects("Table5").DataBodyRange.Columns(1).Rows(i).Value Then
        ActiveWorkbook.Sheets("Parameter").Activate
        Range("C18").Select
        ActiveCell.Value = i
    End If
Next i

As you can see, I'm nowhere with this script, I'd appreciate help
thanks, Radek


Comment by: Jan Karel Pieterse (27-3-2009 05:50:14) deeplink to this comment

Hi Radek,

You can simply load both columns into the listbox (which you set to have two columns and set the column width of the second column to zero) and set the boundcolumn property to the second column.
Now the listbox will show the first column, but return the value of the second column.


Comment by: Radek Kukuczka (30-3-2009 04:37:07) deeplink to this comment

Hello Jan,
thank you very much for this precious hint!
Once I set up the ComboBox properties as you advised, it does return the value I wanted. Apparently I noticed, that I could use the displayed value as well... can I somehow access it?

Is there any reference where I could familarize myself with object properties etc?

thanks, Radek


Comment by: Jan Karel Pieterse (30-3-2009 05:39:33) deeplink to this comment

Hi Radek,

I'd start with using F1 (Help), it is quite extensive.

You can access the other values using the list property:

Texbox1.List(TextBox1.ListIndex,0)
'returns the value of the selected item in column 1

Texbox1.List(TextBox1.ListIndex,1)
'returns the value of the selected item in column 2


Comment by: Bharani (31-3-2009 02:13:33) deeplink to this comment

Thanks a lot....


Comment by: Tom Pirotte (17-4-2009 13:48:44) deeplink to this comment

Hello,
I have a question regarding tables in use with VBA.
I want to use a sheet as "database" for information.
Let me explain

When I open a new xls I have 3 sheets. Sheet1 ,2 and3.
I fill sheet1 with a table (5 x 2.)
When I save the XLS to XLA the sheet with info isn't visible anymore and I can't use my formula, which was written in VBA, to reach the data on the inputted sheet.
Altho in the VB editor I still see the 3 sheets in the structure. How can I reach the sheets in the xla by a self written function or procedure?
Or what is the best way to handle diffrent tables or sheets in a XLA.

Best regards,



Comment by: Jan Karel Pieterse (19-4-2009 07:18:52) deeplink to this comment

Hi Tom,

You should be able to read information from a worksheet contained in an Excel addin without trouble. Post your code here and I'll have a look at the code.


Comment by: Ray Bernard (1-6-2009 19:12:19) deeplink to this comment

For a cell within an Excel 2007 Table (the table is named "Table1"), with banded coloring of cells within the table, the .Interior.ColorIndex property of the cell returns "No fill" regardless of the cell color.

The code in the following post (due to post size limitations) is intended to change the color of a Wingding dot character in a cell based upon the contents of the adjacent cell. However, .Interior.ColorIndex always returns -4142 for both Green and White cells colored by Table banding.

Is the ColorIndex value only available through ListObjects("Table1")? If so, how would I do that? I am new to Excel Macro coding and can't seem to find a reference for the Table object model on the Web or in the Help.

I will submit the code next.


Comment by: Ray Bernard (1-6-2009 19:12:59) deeplink to this comment

Below is the code (provided to me by Ken Johnson) that goes with the previous post I submitted:

' Written by Ken Johnson
'Check for changes to any of the dropdown cells 4 columns to the right of the Tasks column
If Not Intersect(Target, Range("Tasks").Offset(0, 9)) Is Nothing Then
'Format the font color in the cells to the left of the dropdown cells according to the value in the dropdown cell
Dim rgCell As Range
    For Each rgCell In Intersect(Target, Range("Tasks").Offset(0, 9)).Cells
        Select Case rgCell.Value
            Case "Not Started"
            'Make the wingding character the same color as the cell interior so that it is not visible
                With rgCell.Offset(0, -1)
                    If .Interior.ColorIndex <> -4142 Then
                    '-4142 corresponds to No Fill.
                    'Font.ColorIndex = -4142 causes error
                        .Font.ColorIndex = .Interior.ColorIndex
                    Else: .Font.ColorIndex = 2 'White
                    End If
                End With
            Case "Started"
                With rgCell.Offset(0, -1)
                    .Font.ColorIndex = 5 'Blue
                End With
            Case "Behind Schedule"
                With rgCell.Offset(0, -1)
                    .Font.ColorIndex = 44 'Gold
                End With
            Case "Late"
                With rgCell.Offset(0, -1)
                    .Font.ColorIndex = 3 'Red
                End With
            Case "Completed"
                With rgCell.Offset(0, -1)
                    .Font.ColorIndex = 10 'Green
                End With
        End Select
    Next
End If


Comment by: Jan Karel Pieterse (1-6-2009 22:15:31) deeplink to this comment

Hi Ray,

You need to find out the proper TableStyleElement that belongs to the cell inside the table. Which tablestyleElement is needed depends on the settings of your table style. Assuming your cell is within the dataBodyRange of the table and you have no column striping you'd get something like this:

Function GetStyleElementFromTableCell(oCell As Range, oLo As ListObject) As TableStyleElement
'-------------------------------------------------------------------------
' Procedure : GetStyleElementFromTableCell
' Company : JKP Application Development Services (c)
' Author    : Jan Karel Pieterse
' Created : 2-6-2009
' Purpose : Function to return the proper style element from a cell inside a table
'-------------------------------------------------------------------------
    Dim lRow As Long
    'Determine on what row we are inside the table
    lRow = oCell.Row - oLo.DataBodyRange.Cells(1, 1).Row

    If oLo.ShowTableStyleRowStripes Then
        'We are in the table's body
        If lRow Mod 2 = 0 Then
            Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlRowStripe1)
        Else
            Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlWholeTable)
        End If
    Else
        Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlWholeTable)
    End If
End Function

Sub test()
    Dim oLo As ListObject
    Dim oTSt As TableStyleElement
    Set oLo = ActiveSheet.ListObjects(1)
    Set oTSt = GetStyleElementFromTableCell(ActiveCell, oLo)
    ActiveCell.Offset(, 3).Interior.ThemeColor = oTSt.Interior.ThemeColor
    ActiveCell.Offset(, 3).Interior.TintAndShade = oTSt.Interior.TintAndShade
End Sub


Comment by: Tim (8-6-2009 06:41:54) deeplink to this comment

Excel 2007 tables are named ranges ... but I can't treat them as a database name for SQL queries (example, in the MS Query builder). Named rnages appear as a database table, but not Excel 2007 tables. I have to convert the table to a normal range before the name is recognised by the Excel ODBC driver. This is annoying. Am I doing something wrong?


Comment by: Jan Karel Pieterse (8-6-2009 07:36:00) deeplink to this comment

Hi,

I guess you'll have to define your own "normal" named range for each table to have msQuery pick them up.


Comment by: Brian (2-7-2009 07:59:42) deeplink to this comment

Good morning - maybe this is a stupid question, but how do I use vba to obtain the table name that the activecell is in? eg, I can use CurrentRegion to select the whole table, but how do I obtain the table name so that I can start working with its fields?

Thanks,
Brian


Comment by: Jan Karel Pieterse (2-7-2009 12:19:45) deeplink to this comment

Hi Brian,

You could use something like this:

    If ActiveCell.ListObject Is Nothing Then
        MsgBox "Not in a table"
    Else
        MsgBox ActiveCell.ListObject.Name
    End If


Comment by: HDR (13-7-2009 12:00:28) deeplink to this comment

Hello,
How would you use VBA to loop through each row of the Excel 2007 table/list and get values from specific columns and work with them? I tried the code below but it's not working (it doesn't like the Structured Reference syntax)

Also, if the Tables are Workbook in scope in Excel 2007, how do I set a reference to them without using the worksheet on which it resides? (see code below)?

Dim myTable As ListObject
Set myTable = ThisWorkbook.Worksheets("Sheet1").ListObjects("myTable")

For CurRow = myTable.DataBodyRange.Row To myTable.ListRows.Count

myVar = myTable[[#This Row], [Header1]]").Value
'do other stuff..

'The #This Row should obviously move to the next row for each iteration of CurRow
next


Comment by: Jan Karel Pieterse (15-7-2009 00:39:53) deeplink to this comment

Hi HDR,

This example runs through the cells in the first column of the list:

Sub RunThroughFirstColumnOfList()
    Dim oList As ListObject
    Dim oCell As Range
    Set oList = Worksheets(1).ListObjects(1)
    For Each oCell In oList.DataBodyRange.Columns(1).Cells
        MsgBox oCell.Address & ":" & CStr(oCell.Value)
    Next
End Sub


Comment by: John (3-8-2009 17:15:44) deeplink to this comment

Is it possible to offset by using header names, for instance when using find to locate a cell value and then modifying a value in the located cell's row? Kind of like doing such (with Status and Filing ID being table headers):


For Each acell In Selection

With ext_book.Worksheets("Assignments").Range("AssignmentsTbl[Filing ID]")
    Set c = .Find(acell.Value, LookIn:=xlValues)
    If Not c Is Nothing Then
        c.Offset(0, "[Status]").Value = acell.Offset(0, "[Status]").Value
    End If
    
End With

Next acell


I've been racking my brain on how to do this, any help would be GREATLY appreciated. Many thanks!


Comment by: Robert (10-8-2009 17:41:19) deeplink to this comment

Formulas work well within the same row using [#This Row]

ex. =Table_SDCBIBE01_SDCBFDDS_BF_RetailSummary[[#This Row],[RetailSales]]/Table_SDCBIBE01_SDCBFDDS_BF_RetailSummary[[#This Row],[InvPct]]

Is there any way to reference a different row using the table[] syntax? something like [[#This Row](-1),[RetailSales]]?


Comment by: Brent (2-9-2009 09:33:51) deeplink to this comment

Great article. Thanks!!

What VBA code can I use to resize the current table by one row?


Comment by: Jan Karel Pieterse (7-9-2009 08:58:09) deeplink to this comment

Hi John,

Anything is possible. I'd do two finds: one on the header row of the table to find the fieldname you need.
Then the other you already do.
Say the object variable is called oHeader and you have found row c, then the code to update the proper cell is:

Intersect(c.entirerow,oHeader.Entirecolumn).Value="New Value"


Comment by: Jan Karel Pieterse (7-9-2009 09:31:27) deeplink to this comment

Hi Robert,

I don't really know, I don't think so. Look in Help and search fro "Structured references". That should give you all information on how to refer to tables.


Comment by: Jan Karel Pieterse (7-9-2009 10:13:38) deeplink to this comment

Hi Brent,

Like this:

With ActiveCell.ListObject
    .Resize ActiveSheet.Range(.Range.Resize(.Range.Rows.Count + 1, .Range.Columns.Count).Address)
End With


Comment by: Andreas (22-9-2009 14:39:42) deeplink to this comment

Hi Jan,

I have a bunch of Excel 2003-xlas making heavy use of InsertRowRange.
I want to let them run in 2007 compatibility-mode without any change but that's impossible because InsertRowRange Is Nothing after the 1st row insertion.
In Excel 2003 InsertRowRange was never Nothing when the ListObject was active (ActiveCell within the ListObject).
In Excel 2007 it equals to Nothing after the 1st row insertion despite the ActiveCell is ALWAYS within the ListObject.
The following code of an 2003-xla works fine in 2003 but errors with 2007.


Sub InsertRowRangeTest()
    With ThisWorkbook.Worksheets.Add
        .Range("A1").Value = "head"
        .ListObjects.Add xlSrcRange, Range("$A$1"), , xlYes
        .ListObjects(1).InsertRowRange.Value = "1st insert"
        ' next statement works fine in Excel 2003 but errors in 2007
        ' Since the ActiveCell is within the ListObject the
        ' InsertRow should be visible (despite it is not shown in Excel 2007)
        ' Run time error 91: Object of With variable not set
        .ListObjects(1).InsertRowRange.Value = "2nd insert"
    End With
End Sub

What is the reason of the error?
Did I miss to set something?
Can I make the InsertRow visible in another way to prevent Excel 2007 to throw errors?

Kind regards,
Andreas


Comment by: Debug (23-9-2009 02:38:34) deeplink to this comment

Hi

I'm look for code to change a standard command buttons color after I have refreshed the data from the server and the text to data has been refreshed. I'm using Excel 2007. If you can't use a standard button it is not a problem to change it to something else. Thanks before hand.


Comment by: Jan Karel Pieterse (23-9-2009 10:07:45) deeplink to this comment

Hi Andreas,

Thanks for letting me know the problem and the fix:

http://technet.microsoft.com/en-us/library/cc179167.aspx#whatschanged16


Comment by: Ignatius Verbeek (2-11-2009 18:33:25) deeplink to this comment



If I try to change the formula of a cell in a table (aka listobject) in 2007 using vba I get an error. Here is the psuedo code.

set rng = ' a reference to a cell in a table
rng.formula = "= my formula"

gives error code 1004.

I get around this by unlisting the table adding the formula then relisting it.

Is there a better way? What is throwing the error?


Comment by: Jan Karel Pieterse (3-11-2009 00:40:47) deeplink to this comment

Hi Ignatius,

Seems to me the relevant part of your code is missing, could you please post the real code (or just enough in a sub so it shows the error)?

I just tested and setting a formula to a cell inside a table works without a hitch.


Comment by: Ignatius Verbeek (3-11-2009 04:50:39) deeplink to this comment

Jan,

I just did a test and yes I can get vba to add a simple formula in a simple table. I am sorry to bother you with something I should be able to test myself and thankyou for you comments.

However, I remain curious. In the actual table and vba code I did strike the error that I could not add a formula to a table with vba. After much testing I found that in some instances the formula was not correctly defined and that was the source of the error. After eliminating that problem I still found the formula could not be added. With a deadline looming and hours wasting I found that unlisting the table worked, the formula could be added and appears to be correct. I can only guess that excel is doing and auto correct or something which is masking an error in my formula.

Thanks again for you help.


Comment by: Ignatius Verbeek (3-11-2009 05:01:22) deeplink to this comment

Jan,

When you map a table to xml data you get an "insert row" ie a row at the bottom of the table where if you enter data it automaticall adds a new just like data tables in Access. If you don't map the table to xml you don't get the insert row.

I have worked out a way of emulating the insert row behaviour using the workbook sheet change event. In this way one set of subs and functions works on all tables in a workbook that have been flagged to behave in this way. It was/is a bit tricky to get it working neatly but now that it is, it is a very useful feature.

Do you know of a way that you can get the native "insert row" feature of a table to work for a table the is not mapped to xml.


Comment by: Jan Karel Pieterse (3-11-2009 05:29:07) deeplink to this comment

Hi Ignatius,

You don't really need that insert row, Excel will expand your table automatically when you enter anything below or to the right of the table.


Comment by: Noah Fehrenbacher (3-11-2009 18:23:30) deeplink to this comment

How do we know if sorting (or an autofilter) has been applied to a table since before we used autofiltermode and filtermode to determine it before, and now they don't work. What is the alternative for 2007 tables?


Comment by: amirtha (3-11-2009 22:02:26) deeplink to this comment

hai
i want to create table in the form using vb.i want to know how to implement this.


Comment by: Jan Karel Pieterse (4-11-2009 06:55:51) deeplink to this comment

Hi Noah,

It is not straightforward, this function seems to give what you need:

Public Function HasFilter(oLo As ListObject) As Boolean
    Dim oFltr As Filter
    For Each oFltr In oLo.Range.Parent.AutoFilter.Filters
        If oFltr.On Then
            HasFilter = True
            Exit Function
        End If
    Next
End Function

Sub Test()
    MsgBox HasFilter(ActiveCell.ListObject)
End Sub


Comment by: Jan Karel Pieterse (4-11-2009 06:57:43) deeplink to this comment

Hi Amirtha,

See:

https://jkp-ads.com/Articles/AutoSizeListBox.asp


Comment by: Mark (6-11-2009 10:12:02) deeplink to this comment

I'm trying to create a macro to delete all the rows in a table. I'm very close, but am very confused by the behavior of my macro.


Sub Delete_Lotsa_Rows()
    Dim oList As ListObject
    Dim oRow As ListRow
    Set oList = Worksheets(1).ListObjects(1)
    Set oRow = oList.ListRows(1)
    For Each oRow In oList.ListRows
        oRow.Delete
    Next
End Sub


The problem is that the macro only deletes half the rows in the table, then gives me

"RUN-TIME ERROR '1004':
Application-defined or object-defined error"

I even numbered the rows sequentially, and the macro deleted the odd-numbered rows only. When I started with 256 rows, it deleted 128, then 64, then 32, 16, 8, 4, 2, and 1.

It appears that for some reason the code is deleting every other row.


Comment by: Jan Karel Pieterse (6-11-2009 10:37:55) deeplink to this comment

Hi Mark,

The trick is to use a counter instead of a for-each construct and keep deleting the first row:

Sub Delete_Lotsa_Rows()
Dim oList As ListObject
Dim lCt as Long
Set oList = Worksheets(1).ListObjects(1)
Set oRow = oList.ListRows(1)
For lCt=1 To oList.ListRows.Count
oList.ListRows(1).Delete
Next
End Sub


Comment by: Charlie (8-11-2009 11:59:01) deeplink to this comment

Jan,
This is an excellent place for information on the Excel 2007 table Object. However, I am a bit uncertain how to accomplish an action based on a table. I want to set the value of a variable strUserMembership to a value in a table based on another value in the table.

Imagine the table ("tblAdministration") has several FIELDS and one of the fields is [Username]. When the User opens the Workbook, I want to set some Workbook and Worksheet properties based on the User's access level. In the table is another field called [AccessLevel].

So when the User opens the Workbook, I want to find their [Username] in "tblAdministration", and set strUserMembership (variable) to the associated value in the field [AccessLevel].

Thank you so much for your help.


Comment by: Jan Karel Pieterse (8-11-2009 22:51:24) deeplink to this comment

Hi Charlie,

Something like this should do the trick:

Function GetAccessRightsFromTable(sTableName As String, sUsername As String) As String
    Dim oCol As Range
    Dim oRow As Range
    On Error Resume Next
    Set oCol = ActiveSheet.ListObjects(sTableName).Range.Rows(1).Cells.Find("UserName")
    Set oRow = Intersect(ActiveSheet.ListObjects(sTableName).Range, oCol.EntireColumn.Cells).Find(sUsername)
    Set oCol = Nothing
    Set oCol = ActiveSheet.ListObjects(sTableName).Range.Rows(1).Cells.Find("AccessLevel")
    GetAccessRightsFromTable = Intersect(oCol.EntireColumn, oRow.EntireRow).Value
End Function

Sub Foo()
    MsgBox GetAccessRightsFromTable("tblAdministration", "Smith")
End Sub


Comment by: Ian (19-11-2009 06:38:50) deeplink to this comment

In answer to Robert's question (quoted below)

Table_Name[[#This Row],[ColumnName]] is treated like a cell reference.

I've not tried it myself but wouldn't:

=OFFSET(Table_Name[[#This Row],[ColumnName]],-1,0)

provide you with the result you're seeking?

The only issue I can see with this is when you're approaching the edge of the table (the row above the top row is the header...)
====
Comment by: Robert (8/10/2009 5:41:19 PM)

Formulas work well within the same row using [#This Row]

ex. =Table_SDCBIBE01_SDCBFDDS_BF_RetailSummary[[#This Row],[RetailSales]]/Table_SDCBIBE01_SDCBFDDS_BF_RetailSummary[[#This Row],[InvPct]]

Is there any way to reference a different row using the table[] syntax? something like [[#This Row](-1),[RetailSales]]?


Comment by: Charlie (25-11-2009 23:10:41) deeplink to this comment

Jan,

First, thank you for your help on the previous question I posted (11/8/2009 11:59:01 AM) - worked like a champ.

I have tried to use the information from your answer to Radek (3/17/2009 11:10:54 AM) to populate a data validation drop-down. I am here because I cannot get it to work. the code is:

Sub subDropDownActivate(strInCell As String)
Dim varValues As Variant

varValues = shtListSource.ListObjects("tblDocumentType").DataBodyRange.Columns(2).value

Range("" & strInCell & "").Select
With Selection.Validation
.Delete
.Add Type:=xlValidateList, _
AlertStyle:=xlValidAlertStop, _
Operator:=xlBetween, _
Formula1:=varValues
.IgnoreBlank = False
.InCellDropdown = True
.InputTitle = ""
.ErrorTitle = ""
.InputMessage = "Select a value from the drop-down list"
.ErrorMessage = "Only values in the list can be entered"
.ShowInput = True
.ShowError = True
End With
Selection.ClearContents
End Sub


I have tried everything for "Formula1:=" and cannot get it to work. Am I even close?


Comment by: Hi Charlie (25-11-2009 23:17:28) deeplink to this comment

You're close. For a list, the Formula1 property expects the items as a string, separated by a comma, so you could do it like this:

Sub subDropDownActivate(strInCell As String)
    Dim varValues As Variant
    Dim sFormula As String
    Dim sValues() As Variant
    Dim lCt As Long
    varValues = shtListSource.ListObjects("tblDocumentType").DataBodyRange.Columns(2).Value
    ReDim sValues(1 To UBound(varValues, 1))
    For lCt = 1 To UBound(varValues, 1)
        sValues(lCt) = varValues(lCt, 1)
    Next
    sFormula = Join(sValues, ",")
    Range("" & strInCell & "").Select
    With Selection.Validation
        .Delete
        .Add Type:=xlValidateList, _
             AlertStyle:=xlValidAlertStop, _
             Operator:=xlBetween, _
             Formula1:=sFormula
        .IgnoreBlank = False
        .InCellDropdown = True
        .InputTitle = ""
        .ErrorTitle = ""
        .InputMessage = "Select a value from the drop-down list"
        .ErrorMessage = "Only values in the list can be entered"
        .ShowInput = True
        .ShowError = True
    End With
    Selection.ClearContents
End Sub


Comment by: Charlie (1-12-2009 15:14:18) deeplink to this comment

Jan,

Once again your answer to my previous post was very helpful. I have work on the next (and hopefully last) issue all day and am stumped.

How would you apply SEVERAL filters to the table values BEFORE loading the string into the Validation Object? Based on conditions (that will increase in complexity), I would like to reduce the number of records in the table before they end up in the Data Validation Object.

I have tried a two step Autofilter (based on the "Special Stuff" section of your article) approach that I cannot get to work. How would I get Case "tblDSRDocument" filters to work before assigning the varValues variable?

Sub subDropDownActivate(strRange, strTab As Object, strTable As String)
Dim varValues As Variant
Dim varValuesString() As Variant
Dim strFormula1 As String
Dim lngCount As Long

Select Case strTable
     Case "tblDSRDocument"
         shtListSource.ListObjects("tblDSR").Range.AutoFilter Field:=4, _
            Criteria1:="1", Operator:=xlFilterValues
         shtListSource.ListObjects("tblDSR").Range.AutoFilter Field:=9, _
            Criteria1:="All", Operator:=xlOr, Criteria2:="BW"
         varValues = shtListSource.ListObjects("tblDSR").DataBodyRange.Columns(2).value
     Case "tblDSRUnassigned"
         varValues = shtListSource.ListObjects("tblDocumentType").DataBodyRange.Columns(2).value
End Select
ReDim varValuesString(1 To UBound(varValues, 1))
For lngCount = 1 To UBound(varValues, 1)
     varValuesString(lngCount) = varValues(lngCount, 1)
Next
strFormula1 = Join(varValuesString, ",")
....
End Sub


Comment by: Jan Karel Pieterse (2-12-2009 02:10:45) deeplink to this comment

Hi Charlie,

You can't add the visible cells of a filtered list to a variant array unfortunatly, use something like this instead:

Sub subDropDownActivate(strRange, strTab As Object, strTable As String)
    Dim varValues As Variant
    Dim varValuesString() As Variant
    Dim strFormula1 As String
    Dim lngCount As Long
    Dim oCell As Range
    Dim oFilteredRange As Range
    Select Case strTable
    Case "tblDSRDocument"
        shtListSOurce.ListObjects("tblDSR").Range.AutoFilter Field:=4, _
                                                             Criteria1:="1", Operator:=xlFilterValues
        shtListSOurce.ListObjects("tblDSR").Range.AutoFilter Field:=9, _
                                                             Criteria1:="All", Operator:=xlOr, Criteria2:="BW"
        Set oFilteredRange = shtListSOurce.ListObjects("tblDSR").DataBodyRange.Columns(2).Value
    Case "tblDSRUnassigned"
        Set oFilteredRange = shtListSOurce.ListObjects("tblDocumentType").DataBodyRange.Columns(2).Value
    End Select
    For Each oCell In oFilteredRange.SpecialCells(xlCellTypeVisible).Cells
        strFormula1 = strFormula1 & oCell.Value & ","
    Next
    strFormula1 = Left(strFormula1, Len(strFormula1) - 1)
End Sub


Comment by: Joakim Westin (2-12-2009 04:57:17) deeplink to this comment

Thank you for an excellent post!

Can you add an example of the recommended way to loop through all the data in a ListObject? I am not a seasoned VBA programmer so this may be that I don't understand the difference between a range and the ListObject. But what I want to do is to programatically (VBA) access the ListObject data in order to do things with it.

Any examples and recommendations of how to best do this are most welcome!

/Joakim


Comment by: Jan Karel Pieterse (2-12-2009 11:39:06) deeplink to this comment

Hi Joakim,

This loops through all cells in a listobject:

    Dim oCell As Range
    For Each oCell In ActiveSheet.ListObjects(1).Range
        MsgBox oCell.Address & "," & oCell.Value
    Next


Comment by: dimson (11-12-2009 02:46:42) deeplink to this comment

Hi

I am using excel sheet1 as table and in the same workbook i have created a module in which i am trying to pull data from the SQL statements using ADODB connection.
i am not able to write correct syntex for 'WHERE' Clause. Actually i am not able to write the field name
EX:
"WHERE" & [Sheet1$].[Acc Ref#] & "= '665544' "

please tell me what is the right way to use the 'Where' Clause of SQL statement in Excel macro

Thanks
dimson


Comment by: Jan Karel Pieterse (11-12-2009 03:36:37) deeplink to this comment

HI dimson,

Looks like the syntax is something like this:

"WHERE [Sheet1$].[Acc Ref] = '665544'"


Comment by: Nasser (20-12-2009 00:30:14) deeplink to this comment

Hi,

In excel 2003 changing the color of a line does not make a problem. The code is as follow:

ActiveSheet.Shapes("line 1").Line.ForeColor.SchemeColor = 2

But in excel 2007 the things are a bit different. The line is called" straight connector" and i could not find the right code to change its color. Any help to find the correct code.

Many Thanks
Nasser.


Comment by: Jan Karel Pieterse (21-12-2009 11:55:52) deeplink to this comment

Hi Nasser,

This seems to do the trick (turns line black):

ActiveSheet.Shapes("Name Of Shape").Line.ForeColor.RGB = RGB(0, 0, 0)


Comment by: Nasser (21-12-2009 23:04:09) deeplink to this comment


Hi karel,

It works, thank you so much. Using rectangle shape it works with numbers but RGBs it does not, strange!

Cheers,
Nasser.


Comment by: Nasser (22-12-2009 01:47:32) deeplink to this comment

Hi,

I know that there 4 methods to trigger a combobox. The only one working me is to populate the combobox through a command button. The code i am using is as follow:

private sub cmdsetcombo_click()
combobox1.clear
combobbox.additem "1"
combobox1.additem "2"
combobox1.additem "3"

When open the program, i just click on the command button and my combobox gets populated. Without adding this button my combobox remains empty.

I checked the other 3 methods also but after opening the program i find the combobox almost empty. Is there another method which does not need a button to trigger the combobox?

Cheers,
Nasser.


Comment by: Jan Karel Pieterse (22-12-2009 06:45:32) deeplink to this comment

Hi Nasser,

You could use the Workbook_Open event in the Thisworkbook module to run the code that fills the combobox.


Comment by: Nasser (23-12-2009 00:25:44) deeplink to this comment

Hi Karel,

I put the whole code below in "thisworkbook" but it does not seem to work.

Option Explicit

Private Sub ComboBox1_Change()
ComboBox1.Clear
ComboBox1.AddItem ("1")
ComboBox1.AddItem ("2")
ComboBox1.AddItem ("3")

[/End Sub

Private Sub Workbook_Open()
If ComboBox1.Value = "1" Then
ActiveSheet.Shapes("line 2").Line.ForeColor.RGB = RGB(0, 0, 0)
ElseIf ComboBox1.Value = "2" Then
ActiveSheet.Shapes("line 2").Line.ForeColor.RGB = RGB(225, 0, 0)
Else
ActiveSheet.Shapes("line 2").Line.ForeColor.RGB = RGB(0, 225, 0)
End If
End Sub]

Thanks your feedback
Nasser.


Comment by: Jan Karel Pieterse (23-12-2009 04:19:14) deeplink to this comment

Hi Nasser,

The change event of the combobox needs to go in the module belonging to the worksheet the combobox is on.

So this goes in ThisWorkbook:
(note that you may need to change Sheet1 in this code to the name as shown in the project explorer in the VBA Editor)


Private Sub Workbook_Open()
Sheet1.ComboBox1.Clear
Sheet1.ComboBox1.AddItem ("1")
Sheet1.ComboBox1.AddItem ("2")
Sheet1.ComboBox1.AddItem ("3")
End Sub


And this goes in the sheet's module:

Private Sub ComboBox1_Change()
    If ComboBox1.Value = "1" Then
        ActiveSheet.Shapes("line 2").Line.ForeColor.RGB = RGB(0, 0, 0)
    ElseIf ComboBox1.Value = "2" Then
        ActiveSheet.Shapes("line 2").Line.ForeColor.RGB = RGB(225, 0, 0)
    Else
        ActiveSheet.Shapes("line 2").Line.ForeColor.RGB = RGB(0, 225, 0)
End If
End Sub


Comment by: dimson (23-12-2009 06:16:19) deeplink to this comment

I am trying to pull some data from an external read only file(from defined Table) by using SQL Query.

I am getting all the required result but i am not getting few fields data(I have noticed its Number type data)

the exception is that i am getting few data for the same fields. even when i import data, i am getting the same result

due to this, manual copy pasting only works

what could be the error(is the cariage return in the field causing this)


Comment by: Marek (23-12-2009 08:42:03) deeplink to this comment

Hi Jan,

and thanks for the article. Helped quite a bit where the VBA documentation is lacking ...

One question I have is, how do I use the below line,

oNewRow .Range.Cells(1,1).Value="Value For New cell"


but instead of using Cells(1,1) type of referencing I'd rather call it by their field (or column name) name ... like

oNewRow.Range("tblLog[DateTime]").Value = "Test"


I've tried many different syntax versions, but it doesn't seem to accept anything of this kind .

Thanks,

Marek

ps. Happy holidays


Comment by: Nasser (23-12-2009 10:04:35) deeplink to this comment

Hi Karel,

Great, it works nicely. Thanks.

As a new learner i want to try to use the form control and see what is the difference between them and the activeXcontrol. Using the ActiveXcontrol i did not have any problem using combobox with the label but with the form control, my label is not responding.

After selecting a drop down from form control,i entered in 3 cells the names "red, black and green".
After highlighting the cells and selecting the cell d7 as the number to be tested. I wrote my program and it works nicely with the straight connector" without the label.
Adding the label to display the color names, it gives errors, " variable label5 not defined and also "can't execute code in
'break mode". All the code is kept in folder "module1".

[Sub DropDown4_Change()
If Range("d7").Value = 1 Then
ActiveSheet.Shapes("straight connector1").Line.ForeColor.RGB = RGB(225, 0, 0)
Label5.Caption = "red"
    ElseIf Range("d7").Value = "2" Then
ActiveSheet.Shapes("straight connector 1").Line.ForeColor.RGB = RGB(0, 0, 0)
Label5.Caption = "black"
    Else
ActiveSheet.Shapes("straight connector 1").Line.ForeColor.RGB = RGB(0, 225, 0)
Label5.Caption = "green"
    End If
End Sub]

Cheers


Comment by: Jan Karel Pieterse (23-12-2009 11:12:34) deeplink to this comment

Marek: I don't think that syntax will work, because it probably points to an entire column (the one with that heading).

Nasser: The proper syntax is:

ActiveSheet.Labels("Label 1").Caption = "Red"


Comment by: Nasser (24-12-2009 00:40:26) deeplink to this comment


Great! Thanks

Merry Chrismas


Comment by: Nasser (24-12-2009 01:08:24) deeplink to this comment

Hi Karel,

I am using a timer in my application. Here is the code which works fine so far.

Application.Wait Now + TimeValue("00:00:02")
MSGBox ("Clearing time 1.75 seconds")

I want to show exactely 1.75 sec. Since i can not adjust this particular time so i put 2 secs. Is there a way to program my VBA for less than 1 second since i have different timing 0.01, 0.15, 0.75 sec. and so one.

Cheers,
Nasser.


Comment by: Jan Karel Pieterse (24-12-2009 05:49:47) deeplink to this comment

Hi Nasser,

Use the Timer function in combination with a Do loop:

Sub Test()
    Dim dTime As Double
    dTime = Timer
    Do
    Loop Until Timer - dTime >= 1.75
    MsgBox Timer - dTime
End Sub


Comment by: Nasser (24-12-2009 08:49:08) deeplink to this comment

Hi Karel,

Thanks, that work fine but only thing it displays sometimes more than 1.75, 0.17 or 0.01. What i did is changing the MsgBox contain " Timer - Dtime" by the timing above like MsgBox( 1.75 secs"). That will also satisfy my need. Any better idea will be welcomed.

Many many thanks
Nasser.


Comment by: Nasser (24-12-2009 08:57:55) deeplink to this comment

Hi Karel,

I am making one Multi-choice Question. It works fine but when i click on "cancel or close mark" to exit from the window, it says " Invalid Entry" which normally should not.
With wrong entry other than 1,2,3 it displays "Invalid Entry" which is fine. Something needs to be added to my program, isn't it?

Private Sub question1_click()
Dim answer As Variant
answer = (InputBox( _
"Fuses have an inverse type of current-time operating characteristic. What does it mean?" & vbCrLf & _
"1) The higher the current, the slower the operating time." & vbCrLf & _
"2) The smaller the current, the faster the operating time." & vbCrLf & _
"3) The higher the current, the faster the operating time." & vbCrLf & _
" Your answer to question 1 is "))
Select Case answer
Case 1
MsgBox ("WRONG: When the current is high, the fuse must strip quickly.")
Case 2
MsgBox ("WRONG: For a small current close to the fuse rating, the fuse should not trip.")
Case 3
MsgBox ("CORRECT: In case of a higher current, the fuse should trip very quickly.")
Case Else
MsgBox (" Invalid Entry ")
End Select
End Sub

Thanks and Regards
Nasser.


Comment by: Jan Karel Pieterse (25-12-2009 03:28:28) deeplink to this comment

Hi Nasser,

You need one extra test:

'Existing code
Case ""
Msgbox "Cancelled"
Case Else
'Existing code


Comment by: Geoff Hasforth (3-1-2010 16:38:33) deeplink to this comment

I have created an Excel Table from an external database using the ListObjects.Add method then added additional calulated fields to the right of the data range.

The formulas fill down automatically when first entered, but if the data range expands downward when updated I only get one formula in the very bottom row. This leaves a gap which I then have to manually fill. Nor are the formats copied down with each refresh

I can't seem to get the FillAdjacentFormulas working nor can I find a box to turn it on like I use to get with MSQuery querytable. According to what I have read the option should be available in Data Properties menu.

I am trying to get all this working in VBA and would greatly appreciate some help.

Yours Sincerely

Geoff Hasforth


Comment by: Jan Karel Pieterse (3-1-2010 23:28:29) deeplink to this comment

Hi Geoff,

This should work "out of the box". Make sure the formula column is included in the table range. On the table tools tab of the ribbon (only visible when you're within a table), click the design tab and locate the "Properties" group on the far left. It has a button called "Resize table".


Comment by: Dennis Ceralde (31-1-2010 19:36:54) deeplink to this comment

Hi Jan,
On a 1/3/2010 11:28:29 PM posting of yours you stated:
This should work "out of the box". Make sure the formula column is included in the table range. On the table tools tab of the ribbon (only visible when you're within a table), click the design tab and locate the "Properties" group on the far left. It has a button called "Resize table".
Is there a way to programmatically resize a Excel table via VBA?
Thank you,

Dennis


Comment by: Jan Karel Pieterse (31-1-2010 22:20:06) deeplink to this comment

Hi Dennis,

The example code is already on this page, sub called "TableInsertingExamples". This adds a column:

Selection.ListObject.ListColumns.Add


Comment by: Ramu (3-2-2010 07:04:56) deeplink to this comment

Hi Karel,
I've created a user form using Excel 2007 to enter data in a worksheet and everything works fine as long as I write my data into the defined range of the worksheet. But when I convert the data range into a table, the data entered using the form are put outside the table. What's wrong. Many thanks in advance for your help.

Ramu


Comment by: Jan Karel Pieterse (3-2-2010 07:08:44) deeplink to this comment

Hi Ramu,

Without any code examples this is very hard to solve.
I advise you to post your question -with sample file- here:

http://eileenslounge.com


Comment by: Ramu (3-2-2010 22:24:19) deeplink to this comment

Hi Karel,
Sorry, and here is the code:

Private Sub cmdAdd_Click()
Dim iRow As Long
Dim ws As Worksheet
Set ws = Worksheets("Offres")

'find first empty row in database
iRow = ws.Cells(Rows.Count, 1) _
.End(xlUp).Offset(1, 0).Row

'check for a part number
If Trim(Me.txtCmpny.Value) = "" Then
Me.txtCmpny.SetFocus
MsgBox "Veuillez introduire une offre !"
Exit Sub
End If

'copy the data to the database
ws.Cells(iRow, 1).Value = Me.txtCmpny.Value
ws.Cells(iRow, 2).Value = Me.txtFName.Value
ws.Cells(iRow, 3).Value = Me.txtName.Value
ws.Cells(iRow, 4).Value = Me.txtProduct.Value
ws.Cells(iRow, 5).Value = Me.txtSubBy.Value
ws.Cells(iRow, 6).Value = Me.txtDate.Value
ws.Cells(iRow, 7).Value = Me.txtOValue.Value
ws.Cells(iRow, 8).Value = Me.txtDelai.Value
ws.Cells(iRow, 9).Value = Me.txtConclusion.Value
ws.Cells(iRow, 10).Value = Me.txtIndex.Value
ws.Cells(iRow, 11).Value = Me.txtStatus.Value

'clear the data
Me.txtCmpny.Value = ""
Me.txtFName.Value = ""
Me.txtName.Value = ""
Me.txtProduct.Value = ""
Me.txtSubBy.Value = ""
Me.txtDate.Value = ""
Me.txtOValue.Value = ""
Me.txtDelai.Value = ""
Me.txtConclusion.Value = ""
Me.txtIndex.Value = ""
Me.txtStatus.Value = ""
Me.txtCmpny.SetFocus

End Sub


Comment by: Jan Karel Pieterse (3-2-2010 23:00:02) deeplink to this comment

Hi Ramu,

This adds a new row at the bottom of your list and enters a number in each of its cells:

Sub AddRow2List()
    Dim oNewRow As ListRow
    Dim oCell As Range
    With ActiveCell.ListObject
        Set oNewRow = .ListRows.Add
        For Each oCell In oNewRow.Range
            oCell.Value = "1"
        Next
    End With
End Sub


Comment by: Ramu (4-2-2010 08:16:40) deeplink to this comment

Hi Karel,
Thanks for the time you've spent on my problem. I put your piece of code first on top and then after my "Privat Sub cmdAdd_Click()" but nothing changed. The data are still outside the table. So, what did I wrong. (I'm a newbie to VBA). Can you help me out?

Ramu


Comment by: Jan Karel Pieterse (4-2-2010 08:27:51) deeplink to this comment

Hi Ramu,

I expect this is what you need:

Private Sub cmdAdd_Click()
Dim ws As Worksheet
Dim oNewRow As ListRow
Set ws = Worksheets("Offres")
With ws.ListObjects(1)
Set oNewRow = .ListRows.Add
End With

'check for a part number
If Trim(Me.txtCmpny.Value) = "" Then
Me.txtCmpny.SetFocus
MsgBox "Veuillez introduire une offre !"
Exit Sub
End If

'copy the data to the database
oNewRow.Range.Cells(1, 1).Value = Me.txtCmpny.Value
oNewRow.Range.Cells(1, 2).Value = Me.txtFName.Value
oNewRow.Range.Cells(1, 3).Value = Me.txtName.Value
oNewRow.Range.Cells(1, 4).Value = Me.txtProduct.Value
oNewRow.Range.Cells(1, 5).Value = Me.txtSubBy.Value
oNewRow.Range.Cells(1, 6).Value = Me.txtDate.Value
oNewRow.Range.Cells(1, 7).Value = Me.txtOValue.Value
oNewRow.Range.Cells(1, 8).Value = Me.txtDelai.Value
oNewRow.Range.Cells(1, 9).Value = Me.txtConclusion.Value
oNewRow.Range.Cells(1, 10).Value = Me.txtIndex.Value
oNewRow.Range.Cells(1, 11).Value = Me.txtStatus.Value

'clear the data
Me.txtCmpny.Value = ""
Me.txtFName.Value = ""
Me.txtName.Value = ""
Me.txtProduct.Value = ""
Me.txtSubBy.Value = ""
Me.txtDate.Value = ""
Me.txtOValue.Value = ""
Me.txtDelai.Value = ""
Me.txtConclusion.Value = ""
Me.txtIndex.Value = ""
Me.txtStatus.Value = ""
Me.txtCmpny.SetFocus

End Sub


Comment by: Ramu (4-2-2010 10:48:20) deeplink to this comment

Hi Karel,
Great, it works. Many thanks for your precious help.
Ramu


Comment by: Andrew Morgan (9-3-2010 22:19:19) deeplink to this comment

How or can I select 2 non adjacent columns in range by referencing their column headings? The data comes from an external query and the column order may change, but not the names. I can do 1 column at a time, but not more. The following did not work:

Range("Table_Query_from_LegrandDB[[#All],[who],[when]]").Select


Comment by: Jan Karel Pieterse (9-3-2010 22:27:01) deeplink to this comment

Hi Andrew,

You have to use a slightly different syntax:

Range("Table_Query_from_LegrandDB[[#All],[who]],Table_Query_from_LegrandDB[[#All],[when]]").Select


Comment by: damian (17-3-2010 17:27:58) deeplink to this comment

Hi
I was testing vba code below, and need to trap if there are no rows in the table (ie the header record exists and but there are NO other rows)
Dim oL As ListObject
    Set oL = ActiveSheet.ListObjects(1)
    oL.DataBodyRange.Rows(oL.DataBodyRange.Rows.Count).Select


Comment by: Jan Karel Pieterse (17-3-2010 22:54:32) deeplink to this comment

Hi Damian,

The DataBodyRange is not behaving very nicely, if there are no rows, it returns a runtim error when you try to access it. One way of doing it is by turning off error handling temporarily:

Sub TestForData()
    Dim oLo As ListObject
    Dim oRows As Range
    Set oLo = ActiveSheet.ListObjects(1)
    On Error Resume Next
    Set oRows = oLo.DataBodyRange
    On Error GoTo 0
    If oRows Is Nothing Then
        MsgBox "List has no rows"
    Else
        MsgBox "List contains " & oRows.Rows.Count & " rows"
    End If
End Sub


Comment by: Sasya (18-3-2010 02:50:38) deeplink to this comment

Im working on 2007 Excel. Im trying to run a macro on an excel sheet from a different excel sheet. The macro is to open an excel and perform some operations on it. With the macro below I am able to open the excel( 2003 only but not 2007 ) but I am not able to perform any operation on it.

Code goes here

Sub Open()
Dim objXL
Set objXL = CreateObject("Excel.Application")
With objXL
    .Workbooks.Open ("E:\CAT\wordlist.xls")
    .Visible = True 'Might not want this
End With
Set objXL = Nothing
End Sub


Comment by: Jan Karel Pieterse (18-3-2010 03:43:07) deeplink to this comment

Hi Sasya,

You normally do not have to open a new Excel session to do something with an Excel sheet. What is it you are trying to do?


Comment by: Damian (18-3-2010 05:24:53) deeplink to this comment

Hi Jan,
Thank you for the error trapping on dataBodyRange. Next I Have another question is there a way to do subtotals in table based on a code in a column. I don't really want to use a pivot table as it will sort columns and row etc

ie
Code    Amt
1        10.00
1        15.00
total 1 25.00
2         5.00
total 2 5.00
total    30.00

Thanks
Damian


Comment by: William White (25-3-2010 13:51:30) deeplink to this comment

Hint: Trapping ListObjects with an empty DataBodyRange

With Range("tbTable").ListObject
     If .ListRows.Count > 0 Then
            .DataBodyRange.etc...
     End If
End With


Comment by: William White (25-3-2010 13:55:32) deeplink to this comment

Is it possible to delete all ListRows where values meet an error criteria: Example..

     Range("tbData[Criteria]").SpecialCells(xlHasFormula, xlErrors).EnitreRow.Delete

which does not work.

However, filtering [Criteria] = #N/A and "selecting" the visible cells (.Select) followed by Selection.EntireRow.Delete does appear to work.

Would like a direct solution without filtering or using Select.


Comment by: Jan Karel Pieterse (26-3-2010 03:18:52) deeplink to this comment

Hi William,

You have to step through the column cell-by-cell, starting from the bottom so Excel does not loose track of which row it is processing:

Sub DeleteErrorCells()
    Dim lCt As Long
    With Range("Table1[UserName]")
        For lCt = .Rows.Count To 1 Step -1
            If IsError(.Cells(lCt, 1)) Then
                .Cells(lCt, 1).EntireRow.Delete
            End If
        Next
    End With
End Sub


Comment by: William White (26-3-2010 07:21:18) deeplink to this comment

This is the fastest I have, however, the table has many columns and even with xlCalcManual and screenupdating off, the deletes are taking quite a long time. In the spreadsheet itself, manually I an filter on the errors, select all the errors and select delete. It asks me "Delete Entire Rows", to which I say "yes", and the deletion is done it appears in a single swipe. Perhaps the internal code is able to control calculation better than I can. Anyway, this code identifies contiguous groups of cells meeting the criteria and can remove them as a block:


Sub remNaItems(rng)

' ----- Remove all rows where "rng" has an error value
'
' "rng" = table column reference, eg = "table[Column]"

    Range(rng).Worksheet.Calculate
    
    savedCalc = Application.Calculation
    savedUpdating = Application.ScreenUpdating
    Application.Calculation = xlCalculationManual
    Application.ScreenUpdating = False
    
    On Error GoTo exitSub
    Set items = _
     Range(rng).SpecialCells(xlCellTypeFormulas, xlErrors)
    
    On Error GoTo 0
    Range(rng).Worksheet.Unprotect
    n = items.Areas.Count
    For i = n To 1 Step -1
        items.Areas(i).EntireRow.Delete
    Next i
    Range(rng).Worksheet.Protect
    
exitSub:
    Application.Calculation = savedCalc
    Application.ScreenUpdating = savedUpdating

    Exit Sub
    
End Sub


Comment by: Rick Tipton (29-3-2010 06:09:02) deeplink to this comment

Below allows me to select a cell in the data area of a 2007 pivot based on the cell value and formats the row. I have tried using "For Each C In .ListColumns(5).Cells
" in place of the .DataBodyRange but I have errors.

What needs to be changed to select the cell based on the Row Labels?

Sub FormatPage01Pvt1()
    Dim C As Range
    With ActiveSheet.PivotTables("Page01Pvt1")
    
    'Reset default formatting
    With .TableRange1
        .Font.Bold = False
        .Interior.ColorIndex = 0
        .Font.ColorIndex = 0
    
        End With
    
    'Apply formatting to each row if condition is met
    For Each C In .DataBodyRange.Cells

        If C.Value = 1 Then

            With .TableRange1.Rows(C.Row - .TableRange1.Row + 1)
                .Font.Bold = True
                .Interior.ColorIndex = 46
                .Font.ColorIndex = 2
                .Borders.ColorIndex = 0
            End With
        End If
    Next
    
    End With
        
End Sub


Comment by: Jan Karel Pieterse (29-3-2010 06:24:50) deeplink to this comment

Hi Rick,

The proper syntax is:

For Each C In .ListColumns(5).Range.Cells

Note that the index is zero-based, so this is the sixth column of the table we're addressing here...


Comment by: William White (14-4-2010 13:20:41) deeplink to this comment

We're moving files from Windows servers to SharePoint and I need to convert over some of my file manipulation code.

Old code:

FileCopy saCostsFolder & costModel, fnCostModel

This works when saCostsFolder is a Windows address, eg. "\\Cffp\costs\" but seems to fail when I need a file located at "http://domain.com/SharePointSite/costs".

The error is #52 - "Bad file name or number".

Another method tried is fso.CopyFile, where fso is a File System Object.

The file is not an Excel file.

A more general question is whether there is an equivalent for the "Directories and Files Keywords" (that operate only on Windows style file systems as I understand it) for sharepoint files, or web-based files in general.


Comment by: Jan Karel Pieterse (14-4-2010 21:19:31) deeplink to this comment

Hi William,

See if this page gets you there:
http://www.tek-tips.com/viewthread.cfm?qid=1497864&page=2


Comment by: hermine viaene (15-4-2010 15:30:24) deeplink to this comment

dear Mr.Pieterse

hope you are well.First of all many thanks for all your very interesting info and tips , really very very usefull ..

I am trying in VBA to handle a table where I have to do several steps , I succeed to do a part of it ,but not completely.
in short : i have a table per mth starting 2010 with textnumbers(exc 22105,3506 eur)
I want to have this table per mth(alocated at the same place) starting 2009 and with values divided by 1000
(so resulting in 22,1053 )Do you think this can be done in 1 macro ?
Superthks on beforehand for your help - really appreciated
kindest regards
hermine


Comment by: Jan Karel Pieterse (16-4-2010 03:02:14) deeplink to this comment

Hi Hermine,

I certainly do.
I advise you to go to this site to ask your question:
www.eileenslounge.com


Comment by: Patrick Matthews (18-4-2010 17:39:17) deeplink to this comment

Jan Karel,

Excellent article! Interesting that the ListColumns collection is zero based. This is consistent with how table fields are handled in ADO and DAO, but of course inconsistent with just about all of the established Excel collections!

Oh well :)

Patrick


Comment by: Jan Karel Pieterse (19-4-2010 00:46:26) deeplink to this comment

Hi Patrick,

Thanks! Yes, I love these inconsistencies in Excel (NOT) :-(

It does keep me in business though :-)


Comment by: Carim (22-4-2010 04:23:11) deeplink to this comment

Jan Karel,

Thanks for your very interesting article ...

Is there a VBA code to easily delete all filtered rows from table... since my current loop takes way too long ...

Thanks in advance for your advice

Carim


Comment by: Asher Rodriguez (22-4-2010 06:52:01) deeplink to this comment

I have a table linked to an ODBC data connection. I need to keep a history of the data before I update it. I have created a history table in another sheet in the workbook and manual copy the existing data to the end of the history table before updating the ODBC link.I recorded a macro to do this but it always pastes the data to a specific line of the history table (the line that was the end of table when I recorded it). I cannot find anywhere the code to alter my macro to set it to paste the data to the END of the history table. Any help?


Comment by: Jan Karel Pieterse (22-4-2010 07:58:33) deeplink to this comment

Hi Carim,

Something like this :


With ActiveSheet.ListObject1.DataBodyRange
    .SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With


Comment by: Jan Karel Pieterse (22-4-2010 08:04:00) deeplink to this comment

Hi Asher,

You could use a line of code like this to select the last used cell in column A:

With Worksheets("Sheet1")
    .Range("A" & .Rows.Count).End(xlUp).Offset(1).Select
End With


Comment by: Carim (22-4-2010 08:31:47) deeplink to this comment

Hi Jan Karel,

Many thanks for your suggestion ...
Will try it right away ... It will be a relief for over 60'000 rows to be filtered ...

Cheers
Carim


Comment by: Carim (22-4-2010 10:25:24) deeplink to this comment

Hi Jan Karel,

Sorry to bother you again ... but following code just filters and does not delete ...
I am using Excel 2010 beta... does it matter ?


With ActiveSheet.ListObjects("Table1")
.Range.AutoFilter Field:=56, Criteria1:="0"
.DataBodyRange.SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With


If need be, I can try on another machine with Excel 2007

Thanks again for your kind assistance
Best Regards
Carim


Comment by: Jan Karel Pieterse (23-4-2010 01:12:27) deeplink to this comment

Hi Carim,

I'm sorry, the code I gave appears to be wrong.This should do the trick:

Sub Test()
    Dim lCt As Long
    Dim oArea As Range
    With ActiveSheet.ListObjects(1)
        .Range.AutoFilter Field:=1, Criteria1:="0"
        For Each oArea In .DataBodyRange.SpecialCells(xlCellTypeVisible).Areas
            For lCt = oArea.Rows.Count To 1 Step -1
                oArea.EntireRow.Rows(lCt).Delete
            Next
        Next
    End With
End Sub


Comment by: Carim (23-4-2010 09:27:09) deeplink to this comment

Hi Jan Karel,

Thanks a lot for your help ...

Your first solution works fine, provided sorting is applied beforehand ...
And your second solution does the trick as expected, but without the need to sort !!! Excellent !!!

Again, my sincere thanks for all your advice

Best Regards


Comment by: Asher Rodriguez (28-4-2010 08:02:55) deeplink to this comment

Hi Jan Karel,

Thanks for your suggestion, I used a different form of it but it sure did point me in the right direction.

I have another question. I am pasting my code in so you can see what I am doing this time :-)

This code worked just fine yesterday, but today it give me a run-time error and stops at Line 9: Sheets("Test Request Table CURRENT").Select

Can you help?


Sub TR_Tracking_Refresh()


    Sheets("Test Request Table CURRENT").Select
    Range("Table_SysTest").Select
    Selection.Copy
    Sheets("TR_Table_Recent_Past").Select
    Range("Table_Recent_Past").Select
    Selection.End(xlDown).Select
    ActiveCell.Offset(1, 0).Select
    ActiveSheet.Paste
    Sheets("Test Request Table CURRENT").Select
    ActiveWorkbook.RefreshAll
    Range("Table_SysTest[Download Date]").FormulaR1C1 = "=TODAY()"
    Range("Table_SysTest[Download Date]").Select
    Selection.Copy
    Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
        :=False, Transpose:=False
    Range("A2").Select
    Application.CutCopyMode = False
    Range("A2").Select
    
End Sub


Comment by: Jan Karel Pieterse (28-4-2010 10:12:13) deeplink to this comment

Hi Asher,

My guess is that there is no sheet called
"Test Request Table CURRENT"
in the workbook that is active when trying to execute that line.


Comment by: Asher Rodriguez (6-5-2010 06:04:32) deeplink to this comment

Hello again Jan Karel :-)

I have another issue I was hoping you could assist me with.

My range is cells U4:AF302.

The columns represent one month in the fiscal year. The rows are the tasks for a project. People input percent complete values into the cells.

What I am trying to do is make it so that if the is a value of 100 in the column to the left (starting with column V) then it will automatically fill the rest with 100 through the last column.

I can't do it in cell with an IF statement (ex. for cell V4: IF(U4=100,100,"")) like I planned to at first because the users don't like typing over formulas.

Can you help me figure out how do it in the page code?


Comment by: Martin Bustios (6-5-2010 19:48:06) deeplink to this comment

Jan,

Thanks very much for posting this information on tables. I was wondering if you new what is the VBA code to turn off the auto-expansion property of tables.

Thanks ! in advance

Martin


Comment by: Jan Karel Pieterse (7-5-2010 05:17:30) deeplink to this comment

Hi Martin,

I recorded a macro whilst turning it off and got this:

Application.AutoCorrect.AutoExpandListRange = False


Comment by: Jan Karel Pieterse (7-5-2010 05:53:51) deeplink to this comment

Hi Asher,

If you rightclick the sheet tab and select View code and paste in this code I think it will do what you need:

Private Sub Worksheet_Change(ByVal Target As Range)
    If Intersect(Target, Range("U4:AF302")) Is Nothing Then Exit Sub
    If Target.Cells(1, 1).Value = 100 And Target.Cells(1, 1).Offset(, 1).Column <= Range("AF1").Column Then
        Target.Cells(1, 1).Offset(, 1).Value = 100
    End If
End Sub


Comment by: Asher Rodriguez (7-5-2010 12:17:23) deeplink to this comment

Jan Karel YOU ROCK! That code worked perferctly! :-)

If I could ask one last thing on this sheet I would be much grateful.

I have been searching many forums and books and still can't find a code that works for this task:

I need to allow ONLY paste values in ONLY in that same Range of cells. Range("U4:AF302")

I have tried many suggested bits of code from forums I found and so far nothing has worked right and now, somehow I can no longer Paste Special in ANY worksheet.

Please help. I really miss being able to paste special, and I would really love to be able to make just that onerenage within that one sheet be resticted to paste values only.


Comment by: Jan Karel Pieterse (8-5-2010 03:01:40) deeplink to this comment

Hi Asher,

You would use code like the one I propose in this article:
www.jkp-ads.com/articles/catchpaste.asp

But you need to use worksheet events to turn that code on or off where needed.

I would suggest to turn it on or off for the entire worksheet, using the worksheet_activate and worksheet_deactivate events.


Comment by: Asher Rodriguez (10-5-2010 07:43:20) deeplink to this comment

Before I can try this I still need to figure out how to re-enable the paste-special function on my right-click mouse menu.

I have tried lots of code and it seems as though somewhere in there it got disabled.

Any suggestions?


Comment by: Jan Karel Pieterse (10-5-2010 23:20:39) deeplink to this comment

Hi Asher,

This little macro resets all right-click menu's of Excel:

Sub ResetAll()
    Dim cbar As CommandBar
    For Each cbar In Application.CommandBars
        If cbar.Type = msoBarTypePopup Then
            cbar.Reset
        End If
    Next cbar
End Sub


Comment by: kunal (24-5-2010 01:44:43) deeplink to this comment

I have a dump of data in excel which is arranged in the format of a table. I want to write an excel code that processes the required information from the data dump and creates another table with my required rows and columns.
Could you please provide me with sample codes relevant to my problem ?


Comment by: Asher Rodriguez (24-5-2010 06:24:43) deeplink to this comment

Jan Karel,

Thanks so much for the macro to re-enable my right click menu's. I learned a very valuable lesson about thoroughly checking out code before trying it.

I'm going to try and get that book on ribbons soon, thank so myuch for the reference, and I'm still studying how to work your catchpaste macro.

Thanks for making your expertise available for us beginners :-)


Comment by: Jan Karel Pieterse (24-5-2010 09:49:59) deeplink to this comment

Hi Kunal,

Without knowing what processing is needed, this is hard to answer.


Comment by: Asher Rodriguez (24-5-2010 10:10:22) deeplink to this comment

Hi again Jan Karel,

I am trying to create a macro that will:

1. create a table from exported data
2. hide all columns so i can then:
3. show only columns necessary (there are crazy amt of columns in this export)

I started with this code:

    ActiveSheet.ListObjects.Add(xlSrcRange, Range("$A$2:$CI$66"), , xlYes).Name = _
        "Table1"
    
    Range("Table1").EntireColumn.Hidden = True
    Range("Table1[Report]").EntireColumn.Hidden = False
    Range("Table1[Project]").EntireColumn.Hidden = False

End Sub

-And it works, but I need a lot of columns, so in order to avoid retyping this:

Range("Table1[Project]").EntireColumn.Hidden = False

for each column I tried to make this Sub-Procedure:

    

Sub showColumn(cName)
    Range("Table1[cName]").EntireColumn.Hidden = False
End Sub

-And I tried to call it using:

showColumn "Status"

-But I keep getting this error:

Method 'Range' of object '_Global' failed

-Any suggestions?


Comment by: Jan Karel Pieterse (25-5-2010 00:04:12) deeplink to this comment

Hi Kunal,

This code should do it:

Sub HideColumn(sTableName As String, sColumnName As String)
    With ActiveSheet.ListObjects(sTableName)
        .ListColumns(sColumnName).DataBodyRange.EntireColumn.Hidden = True
    End With
End Sub


Comment by: Asher Rodriguez (25-5-2010 06:45:53) deeplink to this comment

Good Morning Jan Karel :-)

Thanks for that code snippet. I modified it for my need and this is what I have now (deleted some columns in the interest of length):

ActiveSheet.ListObjects.Add(xlSrcRange, Range("$A$2:$A$66"), , xlYes).Name = _
        "Table1"
    
    Range("Table1").EntireColumn.Hidden = True
    
    showColumn "Table1", "Report"
    showColumn "Table1", "Project"
    showColumn "Table1", "Status"
    showColumn "Table1", "Date Created"


    'Formatting table view
    Range("Table1").WrapText = True
    Columns("A:A").ColumnWidth = 8
    Columns("B:B").ColumnWidth = 30
    Columns("U:U").ColumnWidth = 30
    
End Sub

Sub showColumn(tName As String, cName As String)
    With ActiveSheet.ListObjects(tName)
        .ListColumns(cName).DataBodyRange.EntireColumn.Hidden = False
    End With
End Sub


It works great!

I have a question though...

Is there a way I can create the table if the range of data is unknown? I never know how many lines there will be on the export.

Also, is there any way I can get this to work even if the table has a different name? What if there is already a table 1 when they run the code.
I keep searching the internet and my VBA books but nothing seems to fit quite right. I think I need to make a variable for the table name but not sure where to put it. I'm a bit frustrated since I took a VB Course last semester but it was all about making programs with GUI Forms and did not mention Excel at all. I thought it would help but it hasn't so far.

Thanks again so much for all your help, much more valuable than the course I took.

-Asher


Comment by: Joe Salmi (14-6-2010 15:38:30) deeplink to this comment

Hi, I'm wondering if someone could PLEASE help me with this. I have a formatted table in Excel 2007 that I am pulling data from a home tab and pasting it into my table. My problem is I need the script to find the last row of data then move to the last column of the table so it can tab down and create a new row then paste in the information. With that being my main issue I come to another problem that I don't always have all the information I need at the time of pasting it.

My table headers are on Row 3 and my data goes from B4:L4. My last row of data is B37:L37 with 2 cells with out information in them. A constant would be my D column because it will always have data in it but the rest may not.
I'm looking to find the last row in column D then move over to the L column then tab to the new row (which would out me at B38 and then paste the information...

Can anyone help me with this?


Comment by: Jan Karel Pieterse (15-6-2010 00:47:48) deeplink to this comment

Hi Joe,

If you already have something in your clipboard to paste, then this pastes that immediately below the table and makes it become a part of that table:

Sub PasteBelowTable()
    Dim oLo As ListObject
    Set oLo = ActiveSheet.ListObjects("Table1")
    ActiveSheet.Paste oLo.DataBodyRange.Offset(oLo.DataBodyRange.Rows.Count).Resize(1, 1)
End Sub


Comment by: Jo Salmi (15-6-2010 14:01:18) deeplink to this comment

Jan Kare,

Thanks for the help, I am going to change it to your way but I did end up getting it working with this little bit:


Range("AA17:AK17").Select
    Selection.Copy
    ActiveWindow.ScrollColumn = 1
    Sheets("No List").Select
    Range("D4").End(xlDown).Offset(0, 8).Select
    Selection.ListObject.ListRows.Add AlwaysInsert:=False
    Range("D4").End(xlDown).Offset(1, -2).Select
    Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
        :=False, Transpose:=False
    Sheets("Home").Select


Everything else after that is the macro clearing the cells on my home sheet.

Joe


Comment by: Olivier (15-6-2010 17:46:05) deeplink to this comment

I am trying to use a ListObject in a function, e.g.,


Function Test(Table As ListObject)
    MsgBox TypeName(Table)
End Function

but when I call that function in a formula, with a table name as argument where the ListObject is expected, e.g.

=Test(MYTABLE)

I get a value error saying that the data type is wrong. Is "MYTABLE" construed as a range rather than a ListObject and if so how should I pass the table?


Comment by: Jan Karel Pieterse (16-6-2010 00:24:21) deeplink to this comment

Hi Jo,

Thanks for letting me know!


Comment by: Jan Karel Pieterse (16-6-2010 00:26:01) deeplink to this comment

Hi Olivier,

Excel cells can only pass ranges and values to VBA functions, so your declaration must be changed to:

Function Test(Table As Variant)
    MsgBox TypeName(Table)
End Function


Comment by: Tjeerd (13-7-2010 08:14:15) deeplink to this comment

Hello,

How can I fill the values of a range of cells with the visible values of a column of a listobject (without using the copy/paste method, which always gives me many runtime errors), using VBA?

This works for the complete list:
Range(destination).value = Listobjects("x").listcolumns(3).databodyrange.value

But I need this for only a part of the list values, based on a criterium. You know of a way to do this without copy/paste?

Thx in advance


Comment by: Jan Karel Pieterse (13-7-2010 10:17:34) deeplink to this comment

Hi Tjeerd,

You could loop through the specialcells collection, cell type xlCellTypeVisible and use that.

Sub Demo()
    Dim lCt As Long
    Dim ocell As Range
    For Each ocell In ActiveSheet.ListObjects(1).DataBodyRange.SpecialCells(xlCellTypeVisible).Cells
        lCt = lCt + 1
        Range("a20").Offset(lCt).Value = ocell.Value
    Next
End Sub


Comment by: MARCO (13-7-2010 15:06:31) deeplink to this comment

Hi,thank you for the usefull tips,
have you some experience in searching data within a table.
in excel is there the Vlookup function that works fine. Is there something similar the have a specific info from a table in VBA?
thank you for tha answer...
BRGDS
Marco


Comment by: Jan Karel Pieterse (13-7-2010 23:58:02) deeplink to this comment

Hi Marco,

In VBA you can use
Application.WorksheetFunction.VLookup([argument list])


Comment by: Fred (5-8-2010 04:50:41) deeplink to this comment

How can I know if the selected cell is a table cell?


Comment by: Kurt Roy (10-8-2010 12:56:54) deeplink to this comment

Hello,

I have a range of cells bound to a ListObject. I would like to be able to have users type into one of its cells "=SUM(A2:B2)" (or whatever), and have the cell "remember" the formula.

Currently, When I type a formula into a cell backed by a list object, it immediately runs the formula for all cells in the column. I realize this is the "Calculated Fields" feature, but I would like to turn this off.

Any ideas would be appreicated.


Comment by: Rhoda Collins (10-8-2010 15:13:30) deeplink to this comment

My excel spreadsheet is telling me when I try to access Visual Basics, that I am out of Memory.....How do I add more memory
Rhoda


Comment by: JEFF (16-8-2010 03:19:25) deeplink to this comment

I am attempting to link a view in Sharepoint 3.0 with Excel 2007 using the following code:

Sub LINKPAGES()

ActiveSheet.ListObjects.Add SourceType:=xlSrcExternal, _
Source:=Array("http://server:45261/sites/Schaefer/Quality/_vti_bin", _
"{94F47A76-7A06-4BBC-B057-4D036508CD24}", _
"{BC63DC59-6685-4613-8F19-3BBABAB840ED}"), _
LinkSource:=True, Destination:=Range("A10")


End Sub



It works fine and will synchronize fine. The problem is the view in Sharepoint shows all folders and files in folders and subfolders. The linked view shows only the three folders of the root library.

How can I show all files and folders in the linked table?

Thanks in advance for your help.


Comment by: Jan Karel Pieterse (16-8-2010 03:20:23) deeplink to this comment

Hi Jeff,

I'm sorry, I know close to nothing about Sharepoint lists!


Comment by: Jan Karel Pieterse (16-8-2010 03:29:42) deeplink to this comment

Hi Fred,

Code like this:

If ActiveCell.ListObject Is Nothing Then
        MsgBox "Not in a table"
    Else
        MsgBox ActiveCell.ListObject.Name
    End If


Comment by: Jan Karel Pieterse (16-8-2010 04:31:10) deeplink to this comment

Excel automatically fills a formula entered into a table cell in the entire column. You can click undo to undo that.


Comment by: Jan Karel Pieterse (16-8-2010 04:32:23) deeplink to this comment

Hi Rhoda,

I expect memory has little to do with this. Maybe there is a problem with your VBA project. Try downloading and running Rob Bovey's code cleaner, to be found at http://www.appspro.com


Comment by: Damian (2-9-2010 21:44:34) deeplink to this comment

How do determine if a column exists in a table?


Comment by: Jan Karel Pieterse (3-9-2010 07:30:00) deeplink to this comment

Hi Damian,

You could loop through the cells of the listobject's HeaderRowRange:


Dim oCell as Range
For Each oCell in ActiveSheet.ListObjects(1).HeaderRowRange
    'Test here
Next


Comment by: Guy (12-9-2010 19:25:34) deeplink to this comment

Hi Jan Karel,

Thanks for the excellent stuff!

I have a table in the range of B1:C2.
When I select C2, I want to get a column number within the table range.
For example, the column number of C2 is 3 in worksheet but it should be 2 in the table range because the table starts from column B.
How can I get it?

Thanks,
Guy


Comment by: Jan Karel Pieterse (12-9-2010 23:47:56) deeplink to this comment

Hi Guy,

Fairly simple:

ActiveCell.Column - ActiveCell.ListObject.Range.Cells(1,1).Column


Comment by: Guy (13-9-2010 09:39:26) deeplink to this comment

Hi Jan Karel,

Thanks a lot for your simple but very effective code.
I added '+1' at the end of your code because when I selected C2, it was returning not 2 but 1.

Would appreciate your advice to make my below macro simpler and more effective.

============
Sub Filter_Value()
    Dim iFieldCol As Integer
    Dim vValue As Variant
    Dim i As Integer
    Dim lDate As Long
    Dim iTbActiveCol As Integer
    
    iFieldCol = ActiveCell.Column
    vValue = ActiveCell.Value
    On Error GoTo 1
    lDate = vValue
    
    If Not (IsError(vValue)) Then
        If Not (IsError(lDate = vValue)) And vValue <> "" And lDate > 0 Then
            
            ActiveCell.AutoFilter Field:=iFieldCol, Criteria1:=">=" & lDate, _
                Operator:=xlAnd, Criteria2:="<=" & lDate
        Exit Sub
        End If
    End If
1
    If IsError(vValue) Then
        Select Case vValue
            Case CVErr(xlErrDiv0)
                vValue = "#DIV/0"
            Case CVErr(xlErrNA)
                vValue = "#N/A"
            Case CVErr(xlErrName)
                vValue = "#NAME?"
            Case CVErr(xlErrNull)
                vValue = "#NULL!"
            Case CVErr(xlErrNum)
                vValue = "#NUM!"
            Case CVErr(xlErrRef)
                vValue = "#REF!"
            Case CVErr(xlErrValue)
                vValue = "#VALUE!"
        End Select
    End If
    If Len(ActiveCell.Value) <= 255 Then
        If ActiveCell.ListObject Is Nothing Then
            On Error Resume Next
            Selection.CurrentRegion.AutoFilter Field:=iFieldCol, Criteria1:=vValue
        Else
            On Error Resume Next
            iTbActiveCol = ActiveCell.Column - ActiveCell.ListObject.Range.Cells(1, 1).Column + 1
            Selection.CurrentRegion.AutoFilter Field:=iTbActiveCol, Criteria1:=vValue
        End If
    Else
        MsgBox "Cannot filter because text length is greater than 255!", vbCritical, "FILTER"
    End If
End Sub
============
Thanks,
Guy


Comment by: Ngo Cham (17-9-2010 11:20:06) deeplink to this comment

Hi Jan Karel,
Thank for your writes,
I have a table in a worksheet, when I set password to protect sheet with some cells (not in table), table does not work.
What can I do?


Comment by: Jan Karel Pieterse (19-9-2010 09:53:05) deeplink to this comment

Hi Ngo,

I'm afraid that is a limitation of the table (list) object, when the wrksheet is protected, you cannot make the list larger or smaller, only edit the contents of the cells, provided you unlocked them.


Comment by: Lauri Reeves (21-9-2010 06:22:00) deeplink to this comment

I have several tables on one worksheet (this is what my boss wants) and I am trying to add rows, which works fine until there are not rows between the tables. Then it errors out stating cannot shift cells. I know why this is happening, I just don't know how to keep a blank row between tables at all times. These tables grow on a weekly basis.

Any help would be appreciated.

Thank you, in advance, for any help.


Comment by: Jan Karel Pieterse (21-9-2010 07:16:20) deeplink to this comment

Hi Lauri,

You must either have sufficient rows between tables, or have the tables next to each other instead of below each other. The only way to prevent the cannot shift error is by inserting empty rows between the tables before adding data.

If I were you, I'd tell your boss this is considered bad spreadsheet design (having multiple tables on one sheet that can grow).


Comment by: Leirimaa (11-10-2010 04:22:48) deeplink to this comment

Hi,
I have 6 charts as objects in Chart sheet. I want to re-size the chart as I click on it. This works with 2003, but not with 2007. Macro recording does not give any help. What should I change t be able to use same functionality with 2007?

Here is the code that works with 2003:
***********************************************
Public Sub ZoomGraph_02()
    ActiveSheet.ChartObjects("Chart02").ShapeRange.ZOrder msoBringToFront
    With ActiveSheet.ChartObjects("Chart02")
        .Top = 0 ' reposition
        If .Height < 300 Then
            .Height = 500 ' resize
            .Left = 0 ' reposition
        Else
            .Height = 230
            .Left = 245 ' reposition
        End If
        If .Width < 300 Then
            .Width = 750 ' resize
        Else
            .Width = 245
        End If
    End With
End Sub
***********************************************
Thanks in advance
Markku


Comment by: Jan Karel Pieterse (11-10-2010 08:01:53) deeplink to this comment

Hi Leirimaa,

I apologize, but I'm a bit too busy to answer your question on short notice I'm afraid.

So I'd suggest to go to www.eileenslounge.com with this question.


Comment by: Mr. Lost (13-10-2010 08:33:20) deeplink to this comment

I havent used Excel in a while and my office is using 2007. Everything has changed dramatically. Where do I type this code? I dont even know where to start.

Thanks,

Mr. Lost


Comment by: Jan Karel Pieterse (13-10-2010 11:22:03) deeplink to this comment

Hi Mr. Lost,

Surely, the VBA editor hasn't changed at all and luckily, VBA is barely touched too. You can get into the VBA editor by hitting alt+F11. Most of the code samples go into a normal module (Insert, Module).


Comment by: Jaideep Koratagere (16-10-2010 06:31:44) deeplink to this comment

Is there a way to copy a custom table style from one workbook to another?


Comment by: Jan Karel Pieterse (16-10-2010 10:40:36) deeplink to this comment

Hi Jaideep,

One way is by copying the table and pasting it somewhere into the other file. After that, the new style is available to use on other tables and you can remove the copy.


Comment by: Jaideep Koratagere (16-10-2010 13:23:50) deeplink to this comment

Hi JKP,
Thanks for the reply, it worked! Couple of other questions now

1. If you save the theme for the excel file containing the custom table, is the custom table saved as part of the theme.

2. When you create a new table style, why is the font disable, i.e unable to set the font for the table style


Comment by: Jan Karel Pieterse (17-10-2010 05:13:02) deeplink to this comment

Hi Jaideep,

I don't think the table style becomes part of the theme, it is only saved with the workbook.
I think different elements of the style can have different kinds of formatting being part of the style or not, depends on what part of the table style you select.


Comment by: Martha Jordan (24-10-2010 20:27:26) deeplink to this comment

I have 3 locked tables on a worksheet, adjacent to each other. The sheet also contains a lot of other data, all locked or limited. The users need to be able to add table rows to the 3 mentioned (that's easy - just add to the bottom of each table using table name after unlocking in VBA)... BUT ALSO the ability to delete rows (that I can't accomplish alone - the active cell can be anywhere in the one-column table) through VBA to turn off the protection and then back on when done. I can't delete entire rows or more than one table will be affected.

I want to use

ActiveSheet.ListObject.ListRows(3).Delete
- without naming the table row. Can I pull the active cell automatically? I can't ask the user in an inputbox for a row number, because it's not the same as a table row number.

PLEASE HELP! Your resource here was sooooo close. Thank you in advance. :-)

MJ


Comment by: Jan Karel Pieterse (26-10-2010 00:54:54) deeplink to this comment

Hi Martha,

You're close. The sample below takes the intersect of the current row and the databodyrange of the listobject (note that there can be no databodyrange if the list is empty, which will cause this code to error out!):

    With ActiveCell.ListObject
        Intersect(.DataBodyRange, ActiveCell.EntireRow).Delete xlUp
    End With


Comment by: Martha (26-10-2010 05:39:30) deeplink to this comment

Thank you very much! After so much headache, I tried another way last night (before getting your answer) and it also works:

ActiveCell.Delete xlShiftUp


Don't you love that there's always multiple ways to solve any problem in Excel!?

Martha


Comment by: Jan Karel Pieterse (26-10-2010 05:43:56) deeplink to this comment

Hi Riel,

I wouldn't have guessed that code to work, as it only appears to delete the active cell. But because the cell is in a table, VBA seems to assume it has to remove the entire datarow in that table.


Comment by: Abdul Azeez (9-11-2010 16:37:26) deeplink to this comment

i have written code in excel module as below
-----------------------
Function HireCharges(TotHr As Date, TotKm As Integer)
Dim HRate As Integer, EKms As Integer
Dim Ehrs As Date, BRate As Integer
Dim Vhr As Integer, ACel As Variant
MaxT = Format(TimeValue("12:00 PM"), "h:mm:ss")
MinT = Format(TimeValue("06:00 AM"), "h:mm:ss")
ZroT = Format(TimeValue("00:00 AM"), "h:mm:ss")
TotHr = Format(TotHr, "h:mm:ss")
MaxK = 80: MinK = 40
FullRt = Range("K2")
HalfRt = Range("L2")
PerKm = Range("M2")
PerHr = Range("N2")
'Fixing Rates
If TotHr >= MaxT Or TotKm >= MaxK Then
    HRate = FullRt
Else
    HRate = HalfRt
End If
'Fixing Extra Kilometers
If TotHr >= MaxT And TotKm >= MaxK Then
    EKms = TotKm - MaxK
ElseIf TotHr > MinT And TotHr < MaxT And TotKm > MinK And TotKm < MaxK Then
    EKms = TotKm = MinK
End If
'Fixing Extra Hours
If TotHr > MaxT Then
    Ehrs = TotHr - MaxT
ElseIf TotHr < MaxT And TotHr > MinT And HRate = HalfRt Then
    Ehrs = TotHr = MinT
End If
'Base Rate Calculation
Vhr = (Ehrs - Int(Ehrs)) * 24
BRate = HRate
BRate = BRate + Round(EKms * PerHr, 0)
BRate = BRate + Round(Vhr * PerKm, 0)

ACel = ActiveCell.Address
RowX = ActiveCell.Row
ColX = ActiveCell.Column
CelX = ActiveCell.NumberFormat

If ColX = 5 And CelX = "0" Then
    XCel = ActiveCell.Address
    
    Selection.Offset(5, 0).Range("A1").Activate
    CelX = ActiveCell.NumberFormat
    ColX = ActiveCell.Column
End If

End Function

the offset command line is not working
i want the line working at this point
what shall i do


Comment by: Jan Karel Pieterse (10-11-2010 04:13:17) deeplink to this comment

Seems to work fine for me. What error do you get?


Comment by: Abdul Azeez (16-11-2010 08:05:20) deeplink to this comment

for Jan Karel Pieterse
Enter 10 in cell A1
Enter =Checkdoubt(a1) in B1
write the following code in excel module

function checkdoubt(x)
range("c1")=x*2
end function


now edit a1 and see what happens
this is the problem i am facing
i want the code run at this place


Comment by: Jan Karel Pieterse (16-11-2010 08:07:54) deeplink to this comment

Hi Abdul,

A VBA function (which we call a User Defined Function or UDF) called from a cell cannot change the content of another cell. A UDF function only returns a value to the cell that called the function. So in your example, the proper code for the function is:

function checkdoubt(x)
checkdoubt=x*2
end function


Comment by: Abdul Azeez (23-11-2010 08:26:07) deeplink to this comment

Thank you very much


Comment by: James (29-11-2010 08:45:37) deeplink to this comment

Hi, I was wondering if you could shine a light on accessing specific parts of a table via a macro. For example

Type, Name, Status

Orange, Orange1, ripe
Orange, Orange2, mouldy
Apple, Apple1, firm
Banana, Banana1, ripe

say i want to operate on only the orange subset, and read all the values from name and status into a string (my actual database is much much larger) via a macro. the thing is, more subsets are added in all the time, so hardcoding isn't really an option. i was trying to use the dropdown menus to isolate the desired items and work on them from there. Do you have any suggestions?
help would be much appreciated


Comment by: Jan Karel Pieterse (29-11-2010 10:29:42) deeplink to this comment

Hi James,

Depends what you want to do with the resulting data I guess. Could you perhaps provide a bit more detail?


Comment by: James (29-11-2010 15:20:12) deeplink to this comment

the data im trying to get a hold of (from the macro) is a list of contact email addresses, i want to be able to send a subset of people an email by selecting their subset via dropdown filter on the table then running a macro, or is this not possible? or for the sake of simplicity, just copying the relevant info to a semicolon separated string (i've already written a macro so send an email to everyone in the list, easy peasy)
im not a vb programmer, more c#, the language difference to me is striking haha.

example:
Friend Type, Name, eMail

Uni, Bob, Bob@bob.com
Uni, Alice, Alice@alice.com
Uni, Charlie, Charlie@charlie.com
School, Giles, Giles@giles.com
School, Vicky, Vicky@vicky.com
Work, Ryan, Ryan@ryan.com

i would like to be able to send an email to say, all uni friends (there are many subsets so individual subs for each subset is impractical) how would i go about this?


Comment by: Jan Karel Pieterse (29-11-2010 23:13:45) deeplink to this comment

Hi James,

You can use code like this to fetch the email addresses:

Function GetAddresses() As String
    Dim oCell As Range
    For Each oCell In ActiveSheet.ListObjects(1).DataBodyRange.Columns(1).SpecialCells(xlCellTypeVisible)
        GetAddresses = GetAddresses & oCell.Value & ";"
    Next
    GetAddresses = Left(GetAddresses, Len(GetAddresses) - 1)
End Function


You can either use this function from any sub in VBA or call it directly from a cell:

=GetAddresses()

In order for the function to work, you must be on the same sheet as where the list is.


Comment by: Andres (6-12-2010 15:25:17) deeplink to this comment

Hello,

I am interested in copying information from a pivot table over to a new sheet, the thing is, the pivot table data is not always going to occupy the same region, so I want to account for that in the VBA code.

Once I select that data and place it in a new sheet, I want to create a table that will house the data.

the thing is I want to use VBA code to copy/paste the information from one sheet to another, and then create a table that will automatically stop on the last row and last column.

Hopefully my question makes sense.


Thanks,
Andres


Comment by: Jan Karel Pieterse (7-12-2010 00:49:36) deeplink to this comment

Hi Andres,

This selects the entire pivot table:

activesheet.pivottables(1).tablerange1.select


Comment by: Andres (7-12-2010 08:23:46) deeplink to this comment

Thanks!

That helped a lot.

Last question, is there a way to choose specific information, or set a row limit within a pivot table where it can copy information from and past in another location. let's say for example, my limit would be the last row that contains "Alerts" on Column "A".

Thanks.

Andres


Comment by: Jonathan (9-12-2010 03:36:00) deeplink to this comment

Greetings,

First off - thanks for this article, I've been able to glean quite a bit from it. Thank you very much.

So I've read how you convert a table back to a normal range, but am wondering if you can convert a single row back to a normal range, and not the whole table?

The reason why I'd like to do this, is to allow myself to delete a table row, but only those cells within the table, and not the entire row itself. Doing so when the row is still a table gives you the "Operation not valid. Attempting to shift cells within your table" error.

Thanks,
Jonathan


Comment by: Jan Karel Pieterse (9-12-2010 13:45:36) deeplink to this comment

Hi Andres,

See if this code example helps you out:

Sub SelectPivotUpToAlerts()
    Dim oCell As Range
    Dim oRange As Range
    Dim oSmallerRange As Range
    Set oRange = ActiveSheet.PivotTables(1).TableRange1
    For Each oCell In oRange.Columns(1).Cells
        If oCell.Value = "Alerts" And oCell.Offset(1).Value <> "Alerts" Then
            Exit For
        End If
    Next
    Set oSmallerRange = ActiveSheet.Range(oRange.Cells(1, oRange.Columns.Count), oCell)
    oSmallerRange.Select
End Sub


Comment by: Jan Karel Pieterse (9-12-2010 13:48:45) deeplink to this comment

Hi Jonathan,

I'm afraid you'll either have to convert the entire table back to a normal range, or alternatively, change the size of the table range (resize table button on the properties group of the Tabletools design tabl on the ribbon), so the table ends above the area where you want to delete rows. Then after deleting the rows, resize the table back to the size you want it to be.


Comment by: diem (17-12-2010 14:20:10) deeplink to this comment

true or false The only way to filter data is by selecting from a list of data.


Comment by: Jan Karel Pieterse (19-12-2010 12:13:54) deeplink to this comment

Hi Diem,

Not entirely true, you can also use advanced filter.


Comment by: Larry T (3-1-2011 13:11:12) deeplink to this comment

Here's what I'm trying to accomplish. For the active sheet, I need to import three different text files using data import. I've written a macro to accomplish this and massage the data the way it need it to. This works fine using ActiveSheet.QueryTable.Add. Here's the problem. For that same worksheet, I may need to reimport the data. The old data is no longer valid. So my process is to select the all the data in the sheet and to delete it and then reimport and process the data. This works too, but it keeps adding each query to the Query tables and of course to the name tables. I would like the query to actually be deleted when when I delete the data, but its not. If I do it manually, excel asks if you want to delete the query and if I say yes its deleted. I tried recording a macro this way and its no different from what I already have. Any suggestions on how I can delete the query and the names from the name table?

Cheers, Larry


Comment by: Jan Karel Pieterse (3-1-2011 22:47:37) deeplink to this comment

Hi Larry,

Instead of removing your old QT and inserting new ones every time, why not just change their source file:

Sub ChangeQT()
    If ActiveSheet.QueryTables.Count < 1 Then
        'Add querytables here
    Else
        'I've assumed all QT remained...
        With ActiveSheet.QueryTables(1)
            .Connection = "TEXT;C:\Users\UserName\Documents\Copy of Same1.txt"
            .Refresh False
        End With
        With ActiveSheet.QueryTables(2)
            .Connection = "TEXT;C:\Users\UserName\Documents\Not Same.txt"
            .Refresh False
        End With
    End If
End Sub


Comment by: YOGESH K.S. (10-1-2011 23:25:20) deeplink to this comment

how to extract the folder and inside folder(file) name in MS excel 2007 work sheet.kindly do the need ful.

regards,
Yogesh


Comment by: Jan Karel Pieterse (11-1-2011 04:59:33) deeplink to this comment

Hi Yogesh,

Please ask your question here:
www.eileenslounge.com


Comment by: James P (13-1-2011 04:32:15) deeplink to this comment

Hi

Bit of a basic question but what if I want to select a table and name it, in many different files but the number of rows always varies?

Many Thanks

Regards

James


Comment by: Jan Karel Pieterse (13-1-2011 04:54:44) deeplink to this comment

Hi James,

So the files you refer to have "tables" on certain sheets, but they have not been formatted as a table yet?

If each "table" has no empty cells then you could use the CurrentRegion property of the range object to convert them to tables:

ActiveSheet.ListObjects.Add(xlSrcRange, Range("$A$1").CurrentRegion, , xlYes).Name = _
        "Table1"


Comment by: James P (13-1-2011 05:20:54) deeplink to this comment

Hi

Thank you for your time. I tried to follow your guide on sorting by colour, but how would I combine that with sorting of coloumn 1 (A) and coloumn 10 (J)

        ActiveSheet.ListObjects.Add(xlSrcRange, Range("$A$10").CurrentRegion, , xlYes).Name = _
        "Table1"
    With ActiveWorkbook.ActiveSheet.ListObjects("Table1")

        .Sort.SortFields.Clear
        .Sort.SortFields.Add( _
                Range("Table1[[#All],[Column1]]"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
        xlSortNormal
                Range ("Table1[[#All],[Column10]]"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
        xlSortNormal
        With .Sort
            .Header = xlYes
            .MatchCase = False
            .Orientation = xlTopToBottom
            .SortMethod = xlPinYin
            .Apply
        End With


I know it's going to be something really simple, but this the first time I'm trying to code it rather than use the record macro.

Thank you again for your time and patience.

Regards

James


Comment by: Jan Karel Pieterse (14-1-2011 03:12:12) deeplink to this comment

Hi James,

The simplest way to see how to sort by color on more than one column is by recording a macro whilst setting up one.
Basically, you repeat the Sortfields.Add line for each separate sort rule before issueing the Sort.Apply command.


Comment by: James P (14-1-2011 03:27:21) deeplink to this comment

Hi

I realised after I posted I hadn't made myself clear and couldn't edit. I don't want it to sort off colour, I just want ascending sort off two columns and was basing it off your code for the colour sort. When I recorded the macro it records the specific range of cells for that particular set of data. So I'm trying to get a generic code running but the debugger is giving me errors on the .add lines of code and I can't see what's the difference between my code and yours other than what we want to sort by.

Thank you again.

Regards

James


Comment by: Jan Karel Pieterse (14-1-2011 03:53:48) deeplink to this comment

Hi James,

OK, got it.

You have:

.Sort.SortFields.Add( _
                Range("Table1[[#All],[Column1]]"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
        xlSortNormal
                Range ("Table1[[#All],[Column10]]"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
        xlSortNormal


I think that should be:

.Sort.SortFields.Add( _
                Range("Table1[[#All],[Column1]]"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
        xlSortNormal)
.Sort.SortFields.Add( _
                Range("Table1[[#All],[Column10]]"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
        xlSortNormal


Comment by: Lauri (19-1-2011 14:17:09) deeplink to this comment

I have a worksheet ("TempData"). If cells in column C say "Empty" then add that row to Table1_1 which resides in worksheet ("DataSource"). I get an error message that states: "Object variable or With block variable not set." I finally realized this is because I had to select the datasource worksheet before I run the macro. My question is, is there a way to specify the worksheet other than Worksheets("DataSource").Select, which, by the way I added before the 'For each' statement. Just curious to see if there is a better to do this. Thank you for your help and time. (By the way, thanks for oNewRow help.)

Sub UpdateSource()
    

Dim oNewRow As ListRow

'Set variables
WB1 = ActiveWorkbook.Name '=the name of the active workbook
WS1 = "TempData"


    Set rng1 = Workbooks(WB1).Sheets(WS1).Range("C2:C" & Workbooks(WB1).Sheets(WS1).Range("C" & Workbooks(WB1).Sheets(WS1).Rows.Count).End(xlUp).ROW)
    'Set rng2 = oNewRow

    For Each a In rng1
        If a.Value = "Empty" Then
        Set oNewRow = Selection.ListObject.ListRows.Add(AlwaysInsert:=True)
        oNewRow.Range.Cells(1, 1).Value = a.Offset(0, 1).Value
        oNewRow.Range.Cells(1, 4).Value = a.Offset(0, 2).Value
        oNewRow.Range.Cells(1, 3).Value = a.Offset(0, -1).Value
        oNewRow.Range.Cells(1, 2).Value = a.Offset(0, -2).Value
    End If
Next a
End Sub


Comment by: Jan Karel Pieterse (19-1-2011 22:51:31) deeplink to this comment

Hi Lauri,

I modified your code a little bit:

Sub UpdateSource()

    Dim WS1 As String
    Dim oNewRow As ListRow
    Dim rng1 As Range
    Dim a As Range
    'Set variables
    WS1 = "TempData"

    With ActiveWorkbook.Sheets(WS1)
        Set rng1 = .Range("C2:C" & .Range("C" & .Rows.Count).End(xlUp).Row)
    End With

    For Each a In rng1
        If a.Value = "Empty" Then
            Set oNewRow = ActiveWorkbook.Worksheets("Datasource").ListObjects(1).ListRows.Add(AlwaysInsert:=True)
            oNewRow.Range.Cells(1, 1).Value = a.Offset(0, 1).Value
            oNewRow.Range.Cells(1, 4).Value = a.Offset(0, 2).Value
            oNewRow.Range.Cells(1, 3).Value = a.Offset(0, -1).Value
            oNewRow.Range.Cells(1, 2).Value = a.Offset(0, -2).Value
        End If
    Next a
End Sub


Comment by: mohd (23-1-2011 02:27:43) deeplink to this comment

thanks for ur help but i need the rows and column to be format.

thanks in advance


Comment by: Jan Karel Pieterse (23-1-2011 06:36:41) deeplink to this comment

Hi Mohd,

Can you explain what you need more precisely?


Comment by: Mohd (25-1-2011 08:10:54) deeplink to this comment

Hi James,


Please note that above code expend the line formate down between Rows while im looking to have border around cell as






Comment by: Jan Karel Pieterse (25-1-2011 22:22:52) deeplink to this comment

Hi Mohd,

You can modify a table's style by creating a custom style (described elsewhere in the article), but I doubt if you can set it up to have alternating borders.


Comment by: Hans (28-1-2011 09:23:39) deeplink to this comment

In your article you show how to remove a table. But how do you simply restore the worksheet to its original plain form without the table formatting yet maintaining whatever data exists?


Comment by: Jan Karel Pieterse (28-1-2011 12:35:37) deeplink to this comment

Hi Hans,

The "Convert Table to range" button does what you need.
In VBA it is the Listobject.Unlist method.


Comment by: Hans (28-1-2011 16:20:41) deeplink to this comment

That's what I thought but it appears that the Listobject.Unlist method results in removing the table but not the formatting if one of the tablestyles has been used.

I have no problem selecting the table and then clicking on Cell Styles - Normal, but I would like to do it programmatically.


Comment by: Jan Karel Pieterse (29-1-2011 03:01:06) deeplink to this comment

Hi Hans,

You could:


Dim oRng as Range
Set oRng=ActiveSheet.ListObjects(1).Range
ActiveSheet.ListObjects(1).Unlist
oRng.Style="Normal"


Comment by: Hans (30-1-2011 16:38:46) deeplink to this comment

Thanks for your help. As expected, it works. Now for another question:

I have set a worksheet object: objws=Worksheets(1).ActiveSheet
I then create a ListObject using
     objlistobj = objws.Listobject.Add . . . .

which produces the desired listobject. I then remove the table
     objlistobj.unlist

That works too. But the following unexpected event happens.

    The object "objws" is set to nothing.

Presumably it's because it's part of the objlistobj definition. But the intention is merely to remove the ListOject from the Worksheet, not to lose the Worksheet object, also. Am I missing something?


Comment by: Jan Karel Pieterse (30-1-2011 22:16:55) deeplink to this comment

Hi Hans,

I assume you do use the Set keyword in you code:
Set objws = ActiveSheet
?

I cannot repro your issue in Excel 2010.


Comment by: Hans Gruenberg (31-1-2011 06:04:34) deeplink to this comment

I used: set objws = Worksheets(1).ActiveSheet

My Excel version is 2007 v.12.0.6300.5000

In my code I followed the .unlist statement with another using objws. and received a "Need object" type of error.
I then repeated the set objws statement between the unlist and the next objws. statement and the error went away.


Comment by: Jan Karel Pieterse (31-1-2011 06:26:34) deeplink to this comment

Hi Hans,

The correct syntax is either:
Set objws = ActiveSheet
or
Set objws=Worksheets(1)
depending on wich worksheet you need.

This code works fine for me on my Excel 2007:

Sub foo()
Dim objws As Worksheet
Set objws = ActiveSheet
objws.ListObjects(1).Unlist
MsgBox objws.Name
End Sub


Comment by: Torstein S. Johnsen (4-2-2011 03:24:44) deeplink to this comment

Thanks for your great article about excel tables and VBA, and as you say "On the VBA side there seems to be nothing new about Tables".
Is there a way to make some VBA code work on "the ActiveTable". I have been looking for such an expression, but it's not there.
I have several similar tables, and I have made a sort-procedure that I would like to work on the table thats selected when the procedure is started.


Comment by: Jan Karel Pieterse (4-2-2011 03:46:17) deeplink to this comment

Hi Torstein,

You can access a table belonging to the activecell like this:

ActiveCell.ListObject
Should the activecell not be part of a table, then this is Nothing:

If ActiveCell.ListObject Is Nothing Then
    MsgBox "Actve Cell not in a table"
ENd If


Comment by: Marco V. (17-2-2011 16:52:27) deeplink to this comment

Hello, thanks for the info!

Can you tell me how i can change the name of a table? i use now this script but the name is stil a problem because the pivot table is looking at table name.

Application.Wait Now + TimeValue("00:00:01")
Application.ScreenUpdating = False
Dim myLastRow As Long
Dim myLastColumn As Long
Range("A2").Select
On Error Resume Next
ActiveSheet.ListObjects.Unlist
    Range(Selection, Selection.End(xlDown)).Select
    Range(Selection, Selection.End(xlToRight)).Select
myRange = "A2:" & myLastCell
ActiveSheet.ListObjects.Add.TableStyle = "TableStyleMedium2"
Application.ScreenUpdating = True
Range(myRange).Select


Comment by: Jan Karel Pieterse (20-2-2011 11:09:05) deeplink to this comment

You could change the last part of your code to:

With ActiveSheet.ListObjects.Add
.TableStyle = "TableStyleMedium2"
.Name="TableNameGoesHere"
End With
Application.ScreenUpdating = True
Range(myRange).Select


Comment by: Jay (12-3-2011 22:03:50) deeplink to this comment

Thanks for this information - I have been looking for info specific to table row manipulation and not just ranges.
I am tinkering around with an employee progress tracking sheet that monitors how many "widgets" they sell each day during a week. With your help from above I have the ability to add a button at the top of each table (1 table per store department) and by clicking it a new row is inserted which indicates someone was hired.

I have gotten the ability to insert using the code:


' Each department has a button that runs the first Sub below and passes on an argument to the add_employee(dept) Sub.
Sub add_ladies_wk1() 'Button on Ladies table activates this
    Dim dept As String
    dept = "ladies"
    add_employee (dept)
End Sub

Private Sub add_employee(dept As String)
    Dim oSh As Worksheet
    Set oSh = ActiveSheet
    oSh.ListObjects(dept).ListRows.Add AlwaysInsert:=True
End Sub

Now what I need is a way to fire people - I would love for a button to be in a cell next to each of their names that can be clicked with a confirmation dialog box. On click it would delete that record and shift rows up as to not leave blank spaces. Any ideas?

Thanks for the help!
Jay

PS - Sorry if this double posts. I didn't see the message to use the VB tags around the code


Comment by: Jan Karel Pieterse (13-3-2011 11:42:51) deeplink to this comment

Hi Jay,

Thanks for sahring your code. I removed your duplicate post, no problem at all!


Comment by: Ayaz S. Zanzeria (16-3-2011 06:24:32) deeplink to this comment

Hi,

I have an 2007 excel sheet that has a table whose data is populated from an external data source. This data has various vendors in a column called "vendor". Based on the Vendor, I want to segregate the data in different sheets for the different Vendors. Please let me know how do I reference the individual table values in a loop. I have these two lines of code as an example which are a no go.

First Attempt:

Select Case Table_WOV.Range("[#This Row],[vendor]").Value


Second Attempt:
Select Case Range("Table_WOV[[#This Row],[vendor]]").Value


Thanks in advance for your help.

Thanks & Regards,
Ayaz S. Zanzeria.


Comment by: Jan Karel Pieterse (16-3-2011 08:13:18) deeplink to this comment

Hi Ayaz,

You can access a specific column in a listobject on the active worksheet like so:

Sub GetValueFromTable()
    Dim oLo As ListObject
    Set oLo = ActiveSheet.ListObjects("Table_WOV")
    MsgBox oLo.ListColumns("vendor").DataBodyRange.Rows(1).Value
End Sub


Comment by: Jonathan (27-3-2011 02:41:46) deeplink to this comment

Greetings,

Your work here has really helped in my development of some Excel/VBA projects at my work, however I'm experiencing one problem.

In my Excel project I have a table where people log information, from this table I've created a function to search the table and calculate a number of different sets of data. In order to search the table, I've followed your example in referencing the ListObjects as seen below:

Dim oSh As Worksheet
Set oSh = ActiveSheet

For c = 0 to (k - oSh.ListObjects("Hours").ListRows(1).Range.Row)
....
....

The function works great when the sheet where the data is located at is selected. However I have a number of graphs that are based on this data on additional sheets, and when those sheets are selected my data somehow goes away. I'm guessing this is due to my function using "ActiveSheet" - Is there anyway to specify which sheet that my ListObjects is referring to.

I've tried..

Set oSh = Worksheets(1)
Set oSh = Sheets(1)

and neither has worked.

Thanks,
Jonathan


Comment by: Jan Karel Pieterse (27-3-2011 11:14:26) deeplink to this comment

Hi Jonathan,

You could use

Set oSh = Worksheets("SheetNameGoesHere")


Comment by: Scott Bernard (13-4-2011 05:59:56) deeplink to this comment

Sorry if I am not posting correctly. To preface my question, I use vba code to parse out text docs(not important) to create tables and link them to a Sharepoint list (not important). I then *attempt* to update and modify the data.
My question is regarding the tables - after modifing the data I can not get around the table duplicated the rows with the new data.

Thanks


Comment by: Jan Karel Pieterse (13-4-2011 09:33:24) deeplink to this comment

Hi Scott,

Nothing wrong with your post!
I know close to nothing about sharepoint lists, so all I can suggest is to find a forum on Sharepoint to ask your quetsions.


Comment by: Scott (13-4-2011 09:42:01) deeplink to this comment

I understand, I was kind of misleading. The question really pertains to excel tables (2010/2007). When I attempt to copy data (vba) sometimes rows seem to duplicate.


Comment by: Jan Karel Pieterse (14-4-2011 01:44:59) deeplink to this comment

Hi Scott,

Can you post the relevant piece f code please?


Comment by: scott (14-4-2011 07:32:25) deeplink to this comment

I am using a simple copy statement. I am not sure if it is a coincidence but the code is working now. The only thing I did differently is to add Set for workbook & sheets (instead of naming the workbook). However, instead of copying just the modified data, I started to copying the same range e.g "A2:D2000" (blanks and all).
Thanks though :)


Comment by: John Redmon (17-4-2011 07:46:23) deeplink to this comment

Hi Jan,
I've been using your examples to gain a better understanding of VBA and tables and they have helped tremendously. I have encountered one problem I can't resolve. I'm using Excel 2007.

Sub SelectingPartOfTable()
    Dim oSh As Worksheet
    Set oSh = ActiveSheet
    With oSh.ListObjects("Table1")
        'Select third column
        .ListColumns(3).Range.Select
        'Select a column by name
        .ListColumns("Part#").Range.Select
    End With

    
This works with a pound sign in name column name, got the same results as using the column number.

The following fails if a "#" pound sign is in a column name.
'select an entire column (data plus header - Using name of column)
    'Will fail if "#" sign is in column name - Works if using a column name without pound sign.
    oSh.Range("Table1[[#All],[Part#]]").Select
    End Sub


Is there a way to get VBA to accept the pound sign in a column name using this statement?
Thanks,
John


Comment by: Jan Karel Pieterse (17-4-2011 10:10:18) deeplink to this comment

Hi John,

Looks like when the column name has a pound sign in it you can't do it that way indeed. I guess the first example you gave is the only option then.


Comment by: Jono (2-5-2011 00:24:35) deeplink to this comment

Hi

Thanks for this article, it's very informative.

I was just wondering, is there any way to take the currently selected cell and find the number of the row it's in?

As context, I've got some buttons on my spreadsheet that are used to add new rows to or delete rows from various tables on the page. For the 'delete' function, I'd like to highlight the entire table row based on the selection while a confirmation box is displayed, so my users can see exactly what will be deleted.


Comment by: Jan Karel Pieterse (2-5-2011 03:43:29) deeplink to this comment

Hi Jono,

If the current cell already is in the table it is easy:

intersect(activecell.EntireRow,activecell.ListObject.DataBodyRange).Select


Comment by: Jono (4-5-2011 00:22:17) deeplink to this comment

Thanks! That worked a treat!


Comment by: Peter (12-5-2011 17:20:42) deeplink to this comment

From your example now I know how to select one column or all columns from a table. Thank you.
I would like to select only specific columns say column1 and column5. Could you please let me know how.


Comment by: Jan Karel Pieterse (13-5-2011 01:49:28) deeplink to this comment

Hi Peter,

Can you tell me why you want to select the listcolumns?

I guess you could do it like so:


    With ActiveSheet.ListObjects(1)
        Union(.ListColumns("a").Range, .ListColumns("c").Range).Select
    End With


Comment by: Anthony (13-5-2011 18:57:39) deeplink to this comment

Good article.

There was a bug introduced in 2007 fixed in 2010, in that if VBA code just puts values after the last row in a table/list, in 2003 1nd 2010 the table grows by one automatically, but not in 2007. Caused me nuiscence.

Also, it is a pity that XK 2003 tables don't have a border around them by default in 2007/10. Not everyone wants to update t .xlsx.


Comment by: Peter (15-5-2011 17:23:57) deeplink to this comment

Hi Jan

Here is a sample table
FName LName City     State DOB     SSN
Peter David Seattle WA     1.1.90 123-45-6789
Kyle Eric    Redmond WA     2.2.89 987-65-4321

I want to copy the columns "FName","City" and "SSN" with the respective data and copy in a different worksheet("Sheet2") on the first three columns (the worksheet is blank). Pleast let me know how to do this. Appreciate your help





Hi Peter,

Can you tell me why you want to select the listcolumns?

I guess you could do it like so:

    With ActiveSheet.ListObjects(1)
        Union(.ListColumns("a").Range, .ListColumns("c").Range).Select
    End With


Comment by: Peter (15-5-2011 17:54:07) deeplink to this comment

Hi Jan
I kind of figured how to do that.

Range("table1[[#all],[FName]],table1[[#all],[City]], table1[[#all],[State]]").Copy Range("M1")

If there is a better way let me know.
Thank you


Comment by: Anthony (15-5-2011 18:18:06) deeplink to this comment

Actually, to clarify my previous comment, 2007 and 2010 appear to be consistently different from 2007. The difference is in the way the "*" insert row is created and that the ListObject.Range is different. And of course there is no insertrowrange. I actually prefered the 2003 behaviour.


Comment by: Jan Karel Pieterse (15-5-2011 21:09:56) deeplink to this comment

@Peter: Your syntax looks fine to me. There are often many ways to do things in VBA.

@Anthony: I've seen similar problems in 2003 where it is hard to make sure your new data is appended to the table. 2007/2010 behave worse indeed. My workaround was to unlist the table/list, add the row and then re-list. But that causes havoc if you have formulas pointing to the table.


Comment by: Peter (16-5-2011 16:51:09) deeplink to this comment

FName LName City     State DOB     SSN
Peter David Seattle WA 1.1.90 123-45-6789
Kyle Eric    Redmond WA 2.2.89 987-65-4321

I used the name of the columns (FName, City, State)in the code. It works even if the order of columns change or a new column is inserted in the table.
Actually what I really want to do is this. Delete the unnecessary columns from the table (It could be 3, 4 or .. more). I just want only these 3 columns.
Could you please let me know how to do this. Thank you.


Comment by: Jan Karel Pieterse (16-5-2011 22:59:33) deeplink to this comment

Hi Peter,

I'd do something like this:

Sub DeleteListColumns()
    Dim oCols2Delete As Range
    Dim lCt As Long
    With ActiveSheet.ListObjects("Table1")
        Set oCols2Delete = Union(.ListColumns("City").Range, .ListColumns("State").Range, .ListColumns("DOB").Range)
    End With
    'count down to prevent VBA from getting confused
    For lCt = oCols2Delete.Areas.Count To 1 Step -1
        oCols2Delete.Areas(lCt).EntireColumn.Delete
    Next
End Sub


Comment by: Peter (17-5-2011 15:03:52) deeplink to this comment

Hi Jan

Thanks for the quick reply. Your code works fine. It is really exciting to learn new things . Appreciate it. But there is one thing. Sorry if I am not explaining it right. I guess it is because I am not familiar with all the technical terms.

I manipulate a report which I get from a vendor site. The number of columns and the order of columns always change in that report. But it always has the columns that I wanted. I want to keep them and delete the rest of them and then do some calculations.

I just know the columns that I need. I don't know the ones I need to delete because it changes every time.

How would I do this




Comment by: Jan Karel Pieterse (17-5-2011 23:36:40) deeplink to this comment

Hi Peter,

Sure, like this:

Sub DeleteListColumns2()
    Dim oColsNot2Delete As Range
    Dim lCt As Long
    With ActiveSheet.ListObjects("Table1")
        Set oColsNot2Delete = Union(.ListColumns("City").Range, .ListColumns("State").Range, .ListColumns("DOB").Range)
        'count down to prevent VBA from getting confused
        For lCt = .ListColumns.Count To 1 Step -1
            If Intersect(.ListColumns(lCt).Range, oColsNot2Delete) Is Nothing Then
                .ListColumns(lCt).Delete
            End If
        Next
    End With
End Sub


Comment by: Paul Jose (6-6-2011 07:37:28) deeplink to this comment

Any idea how in Excel 2007 you hold the VBA code while the table refreshes ??

I inherited some code (below) from an old file, that doesnt seem to work in 2007.

' I open the file
ActiveWorkbook.RefreshAll
With Worksheets(1).QueryTables(1)
Do While .Refreshing = True
Loop
End With
ExistFault.Show
ActiveWorkbook.Save
ActiveWindow.Close

All help appreciated as this is doing my head in !

Paul


Comment by: Jan Karel Pieterse (7-6-2011 00:13:59) deeplink to this comment

Hi Paul,

One way would be by doing each querytable/pivot table in turn and setting them to refresh synchronously, e.g.:

Sub Refreshall()
    Dim oPt As PivotTable
    Dim oQt As QueryTable
    Dim oSh As Worksheet
    Dim oLo As ListObject
    For Each oSh In Worksheets
        For Each oQt In oSh.QueryTables
            oQt.Refresh False
        Next
        For Each oLo In oSh.ListObjects
            On Error Resume Next
            Set oQt = oLo.QueryTable
            On Error GoTo 0
            If Not oQt Is Nothing Then
                oQt.Refresh False
            End If
        Next
    Next
    For Each oSh In Worksheets
        For Each oPt In oSh.PivotTables
            oPt.RefreshTable
        Next
    Next
End Sub


Comment by: Becky (10-6-2011 11:04:44) deeplink to this comment

resolved issue regarding not seeing table-context-sensitive recording/formula building

saving as file type .xlsm wasnt quite enough...I needed to close and reopen the file.

I have to juggle between 2007 and 2003 compatible formatting- building a spreadsheet application that will be processing on a win 2010 or 2007 client, but then sent out to larger audience known to be limited to 2003...so I will have to see if I can use the fun features during processing before compiling a spreadsheet that could be opened for viewing in 2003.

again - I would really like to THANK YOU for putting very clear information on Excel out on the web- it was so helpful to find info I can understand and apply to help me figure out what is going on in my own world.


Comment by: Deepu (11-6-2011 13:57:50) deeplink to this comment

I have a CSV file with large number of rows. I need to create a table in a separate worksheet, where I can just sum up the data with it's ID's, like if I have 10 rows for Monday, I need to put Monday in the table and in it's columns, I just put the total count for different names. To be more clear, on Monday, John made 5 saves, Patrick made 10, Tim made 4, etc.

So the row in the table is for Monday, and it's columns are 5, 10, 4, etc. where the header files can also be given as Day, John, Patrick, Tim, respectively. I hope I have been able to explain it clearly. I need help as soon as possible.


Comment by: Jan Karel Pieterse (14-6-2011 00:21:50) deeplink to this comment

Hi Deepu,

I would take a 2-step approach:
1. Import the table using the steps outlined in the article
2. Create a pivot table report from the imported data.


Comment by: Ed (19-6-2011 05:01:10) deeplink to this comment

Is there any elegant way of iterating over a table / listobject by row idiomatically like this (pseudo VBA)?

Assume a table called "People" with two columns, one headed "Firstname" the other "Surname".


Dim r As TableRow
For Each r In Table("People").Rows
MsgBox "The person is called: " r("Firstname").Value & " " & r("Surname").Value
Next r


everything I have read about VBA named ranges / tables / ListObjects seems to prevent this straightforward stanza. I have looked at how do do it, forming a collection class of table rows and binding to the table, but it seems as though this is such a simple requirement that I'm missing something blindingly obvious.


Comment by: Jan Karel Pieterse (19-6-2011 23:24:49) deeplink to this comment

Hi Ed,

That is relatively straightforward, once you know which syntax to use :-)

Sub RunThroughRows()
    Dim oLo As ListObject
    Dim lCt As Long
    Set oLo = ActiveSheet.ListObjects("People")
    For lCt = 1 To oLo.DataBodyRange.Rows.Count
        MsgBox "Name of person number " & lCt & ": " & _
             oLo.ListColumns("firstname").DataBodyRange.Cells(lCt, 1).Value & _
             " " & oLo.ListColumns("lastname").DataBodyRange.Cells(lCt, 1).Value
    Next

End Sub


Comment by: Bibu Sebastian (21-6-2011 02:29:07) deeplink to this comment

I have a pivot table, if I include a comment right to pivot table row, how will the comments get updated with the refresh pivot table. Kindly help.. plssssssss


Comment by: Rainhart (21-6-2011 04:36:17) deeplink to this comment

How can I add an item to the tables contect menu, i.e. to a listObject?


Comment by: Jan Karel Pieterse (22-6-2011 00:44:04) deeplink to this comment

Hi Rainhart,

Like this:

Sub AddItemToListPopupCommandbar()
    With Application.CommandBars("List Range Popup")
        With .Controls.Add(msoControlButton)
            .Caption = "MyCaption"
            .OnAction = "MyMacro"
        End With
    End With
End Sub


Comment by: Jan Karel Pieterse (22-6-2011 00:51:19) deeplink to this comment

Hi Bibu,

Comments written in cells next to a pivot table will not move when you refresh the pivot table and the rows of the pivot table shift.

If you want that to happen, you could add a small table which contains all the possible row items of the pivot table (for example, all countries if you have countries as a row field) and write the comments next to that new table.

Then next to the pivot table, use a vlookup function to find the comment belonging to the pivot table's rowfield value. When building the VLOOKUP function, make sure you use a normal cell reference to the pivot field cells, not the GetPivotData function.


Comment by: Rainhart (22-6-2011 02:38:34) deeplink to this comment

Thank you, Jan!

I had tried with "Cell" instead of "List Range Popup".


Comment by: AJ (23-6-2011 10:35:55) deeplink to this comment

Hi,
I have a macro the adds lots of data (9000 rows and 6 cols) to a table. There is another table (9000 rows and 10 cols) calculated based on the first one. In the workbook there are also lots of formulas linked to this 2 tables. Because of this, it takes a lot of time (1-2 minute) to add the data to the first table.
The calculations is set to manual. Events and screen updating are turned off, but nothing helps reduce the time.
Can you please advise anything?


Comment by: Jan Karel Pieterse (24-6-2011 05:27:51) deeplink to this comment

Hi AJ,

One thing you could do is add the data below the table, but leave an empty row in-between your new data and the table, thus preventing Excel from expanding the table.
Then once all data is "in", remove the empty row and then expand the table.


Comment by: Marina (24-6-2011 12:02:17) deeplink to this comment

Hi! I want to use a specific range to create a table with a chart using the range I have selected, however it seems not to work. Could you please let me know why? Thanks!


Private Sub CommandButton1_Click()
    Dim oRangeSelected As Range
    On Error Resume Next
    Set oRangeSelected = Application.InputBox("Please select a range of cells!", _
                                             "SelectARAnge Demo", Selection.Address, , , , , 8)
Set objExcel = CreateObject("Excel.Application")
objExcel.Visible = True
Set objWorkbook = objExcel.Workbooks.Add()
Set objWorksheet = objWorkbook.Worksheets(1)
Set objRange = objWorksheet.UsedRange
objRange.Select

Set colCharts = objExcel.Charts
colCharts.Add

Set objChart = colCharts(1)
objChart.Activate

objChart.ChartType = 65

objChart.PlotArea.Fill.PresetGradient 1, 1, 7

objChart.SeriesCollection(1).Border.Weight = -4138
objChart.SeriesCollection(2).Border.Weight = -4138
objChart.SeriesCollection(3).Border.Weight = -4138

objChart.SeriesCollection(1).Border.ColorIndex = 2
objChart.SeriesCollection(1).MarkerBackgroundColorIndex = 2

objChart.SeriesCollection(2).MarkerForegroundColorIndex = 1
objChart.SeriesCollection(3).MarkerForegroundColorIndex = 1

objChart.HasTitle = True
objChart.ChartTitle.Text = ""
objChart.ChartTitle.Font.Size = 18

objChart.ChartArea.Fill.Visible = True
objChart.ChartArea.Fill.PresetTextured 15

objChart.ChartArea.Border.LineStyle = 1

objChart.HasLegend = True
objChart.Legend.Shadow = True

    If oRangeSelected Is Nothing Then
        MsgBox "It appears as if you pressed cancel!"
    Else
        MsgBox "You selected: " & oRangeSelected.Address(External:=True)
    End If

End Sub
.


Comment by: Jan Karel Pieterse (27-6-2011 02:44:02) deeplink to this comment

Hi Marina,

Your code fails because you first insert a new -empty- workbook and then try to create a chart, which will have no series because there is no data. You are also instantiating a new instance of Excel, which is not needed. I think this code will do the trick:

Private Sub CommandButton1_Click()
    Dim oRangeSelected As Range
    On Error Resume Next
    Set oRangeSelected = Application.InputBox("Please select a range of cells!", _
                                             "SelectARAnge Demo", Selection.Address, , , , , 8)
    On Error GoTo 0
    If Not oRangeSelected Is Nothing Then
        Set objRange = oRangeSelected.CurrentRegion
        objRange.Select

        Set colCharts = Charts
        colCharts.Add

        Set objChart = colCharts(1)
        objChart.Activate

        objChart.ChartType = 65

        objChart.PlotArea.Fill.PresetGradient 1, 1, 7

        objChart.SeriesCollection(1).Border.Weight = -4138
        objChart.SeriesCollection(2).Border.Weight = -4138
        objChart.SeriesCollection(3).Border.Weight = -4138

        objChart.SeriesCollection(1).Border.ColorIndex = 2
        objChart.SeriesCollection(1).MarkerBackgroundColorIndex = 2

        objChart.SeriesCollection(2).MarkerForegroundColorIndex = 1
        objChart.SeriesCollection(3).MarkerForegroundColorIndex = 1

        objChart.HasTitle = True
        objChart.ChartTitle.Text = "Title"
        objChart.ChartTitle.Font.Size = 18

        objChart.ChartArea.Fill.Visible = True
        objChart.ChartArea.Fill.PresetTextured 15

        objChart.ChartArea.Border.LineStyle = 1

        objChart.HasLegend = True
        objChart.Legend.Shadow = True

    Else
        MsgBox "It appears as if you pressed cancel!"
    End If

End Sub


Comment by: Mandeep Mehta (28-6-2011 07:18:56) deeplink to this comment

How can I have totals calculated for all columns of a list object using vba?


Comment by: Jan Karel Pieterse (28-6-2011 23:59:54) deeplink to this comment

Hi Mandeep,

The easiest way is to record a macro while you turn on the total row for a table and study the code:

ActiveSheet.ListObjects("Table1").ShowTotals = True


Some examples to change the function in the total row of one of the columns (column "q"):

ActiveSheet.ListObjects("Table1").ListColumns("q").TotalsCalculation = _
        xlTotalsCalculationAverage
    ActiveSheet.ListObjects("Table1").ListColumns("q").TotalsCalculation = _
        xlTotalsCalculationCount
    ActiveSheet.ListObjects("Table1").ListColumns("q").TotalsCalculation = _
        xlTotalsCalculationSum


Comment by: Ramakrishnan (30-6-2011 11:56:52) deeplink to this comment

Hi, Am very new to VBA. i want to know how to apply the coding in vba excel and and how to run a macro for any coding. i have tried many times some codes in vba but am getting some errors while am run the macro. Please teach me how to proceed.


Comment by: Jan Karel Pieterse (4-7-2011 01:40:32) deeplink to this comment

Hi Ramakrishnan,

I suggest to get a beginner book on VBA, such as one of the Dummies series: Excel 2007 VBA for Dummies by John Walkenbach for example.


Comment by: ddset (7-7-2011 02:34:48) deeplink to this comment

Hi, I've been busting my head with one problem for some time already. The problem is that I have worksheet of data with multiple columns. In one column I have data that is different for each cell in the column and data has been inserted so that text is all in one line (e.g. no break lines). What I want is to insert break line in each row after let's say 40 characters.
Tried with strings, but just can't get it right. Is there some other way I haven't considered maybe? In any case, thanks :)


Comment by: Jan Karel Pieterse (7-7-2011 05:04:12) deeplink to this comment

Hi ddset,

Put this code in a normal module in your file:

Public Function Wrap(Text As Variant, LineLength As Variant)
    Dim lCt As Long
    If LineLength >= Len(Text) Then
        Wrap = Text
    ElseIf Len(Text) > 0 Then
        For lCt = 1 To Int(Len(Text) / LineLength + 0.5)
            Wrap = Wrap & Mid(Text, (lCt - 1) * LineLength + 1, LineLength) & vbNewLine
        Next
        Wrap = Left(Wrap, Len(Wrap) - 1)
    End If
End Function

Then go to your sheet and split the content of cell A1 like so (formula to go in a separate column):
=WRAP(A1,40)
After copying down the formula, copy, paste-special value this formula to get static results.
Make sure you turn on wrap text on the relevant column's cell properties.


Comment by: John (11-7-2011 00:13:46) deeplink to this comment

Hi
please i have an excel table, that includes phone numbers of staff, i have been able to record a macros that fliters each name by the location
however i am try to get a code that updates the list alphabetically and serially when i include a new staff in the table
please help
regards


Comment by: Jan Karel Pieterse (11-7-2011 00:26:37) deeplink to this comment

Hi John,

I suggest you ask this question here:

http://www.eileenslounge.com


Comment by: Paul Godfrey (12-7-2011 04:02:17) deeplink to this comment

Thanks for this page. Very useful info and well explained.
Unlike the macro recorder for 2007 tables your code works.
You have saved me a lot of time.


Comment by: Rob (30-7-2011 06:11:10) deeplink to this comment

Hi Jan,
Interesting read! You mention in Selecting parts of tables

'select an entire column (data only)
     oSh.Range("Table1[Column2]").Select

'select an entire column (data plus header)
     oSh.Range("Table1[[#All],[Column1]]").Select

But how would you select TWO (or 3) columns in both of these examples? (e.g. Col D & Col E)I've tried various ways but keep getting in to errors. My fault entirely, I'm a bit of a noob when it comes to VBA. :)


Comment by: Charlie (3-8-2011 12:38:13) deeplink to this comment

Jan,

Is there a way to "trap" if a User manually adds / deletes a row (or column) from a ListObject? When a row is added (for example in the last row of the table: right-click > Insert > Table Rows Below) manually, the Worksheet_Change event fires. How could I determine if the Change event was a result of the row being added?


Comment by: Per Inge (6-8-2011 08:09:51) deeplink to this comment

Hi, and thanks for a very valuable article.

Do you have any overview of the notation where you refer to rows and columns using e.g. [Column1] instead of an index?

Example:

oSh.Range("Table1[[#All],[Column1]]").Select


Best regards Per Inge


Comment by: sam (12-8-2011 07:08:09) deeplink to this comment

excellent page! quick question - how do we copy a column from 1 table to another table (empty) in a specific column

something like


wrksource.ListObjects(tablsource).ListColumns("column1").DataBodyRange.Copy Destination:=wrkdest.ListObjects(tabldest).ListColumns("column2").databodyrange


Comment by: AJ Henderson (18-8-2011 09:07:23) deeplink to this comment

Hello,

I am working on appending information to and removing information from a table. Is there a way to set up the code so that when I'm appending I can fill in a form and it will automatically append it to a new row in the table? Also the same thing for removing data. I would like to fill out a similar form and have it remove the row for me.

Thanks in advance.


Comment by: Jan Karel Pieterse (22-8-2011 02:03:47) deeplink to this comment

Hi AJ,

You might be helped with the old excel 2003 Data, Form functionality, which provides a basic data entry form?

From Excel 2007 and 2010 you can find the relevant function with the shortcut key alt + d, o. You can add the bottun to your QAT, the Form button is located in the "Commands not on the ribbon" category.


Comment by: Jan Karel Pieterse (22-8-2011 02:29:17) deeplink to this comment

Hi Sam,

The trick is to first insert the column(s) in the target listobject and then do the copy:

Sub CopyAColumn()
    Dim oLo As ListObject
    Set oLo = ActiveSheet.ListObjects(2)
    With oLo.ListColumns.Item("e")
        .Range.Columns.Insert
        ActiveSheet.ListObjects(1).ListColumns("B").Range.Copy .Range.Offset(, -1).Cells(1, 1)
    End With
End Sub


Comment by: Jan Karel Pieterse (22-8-2011 02:46:07) deeplink to this comment

Hi Per Inge,

The correct syntax is:

ActiveSheet.ListObjects(1).ListColumns("Column1")


Comment by: Jan Karel Pieterse (22-8-2011 02:52:01) deeplink to this comment

Hi Charlie,

You would have to keep track of the dimensions of your table *before* the event fires and then when the event fires check if they have changed and act accordingly.


Comment by: Per Inge Sævareid (22-8-2011 02:52:48) deeplink to this comment

Thanks Jan for your answer. I actually found an overview of "Referencing cells in a table (structured referencing)" in https://jkp-ads.com/Articles/Excel2007Tables.asp

Powerfull and elegant, thanks!


Comment by: Jan Karel Pieterse (22-8-2011 02:56:43) deeplink to this comment

Hi Rob,

Regarding selecting multiple columns:

ActiveSheet.ListObjects(1).ListColumns("b").range.Resize(,2).select


Comment by: Sean (25-8-2011 12:49:15) deeplink to this comment

Hi, great article!

I am looking for any resources on creating a join between an excel 2007 table and a database table.

For example, is it possible to query the account info from a SQL server table for a given set of account numbers in a range and return the result set to the excel spreadsheet?


Comment by: Jan Karel Pieterse (26-8-2011 05:13:18) deeplink to this comment

Hi Sean,

I think your best bet is to create an empty Access database and use two linked tables to the SQL database and to the Excel file respectively and then create a query from there.
Excel cannot join tables from multiple sources.
Another option might be to use PowerPivot, but I have little to no experience with that.


Comment by: Markus (22-9-2011 07:47:22) deeplink to this comment

Hi Jan!

Thank´s all for the helpful tips in this thread!

Im trying to use Jan's TableInsertingExamples above. I only want to insert one row at the end of the table.


Sub TableInsertingExamples()
'insert below
ActiveSheet.ListObject.ListRows.Add AlwaysInsert:=True
End Sub


I get this error message:
Object does not support this property or method

What can i do?
Regards,
Markus


Comment by: Jan Karel Pieterse (22-9-2011 07:51:14) deeplink to this comment

Hi Markus,

The correct syntax is:

Worksheets("Sheet1").ListObjects("Table1").ListRows.Add AlwaysInsert:=true

Or:

ActiveCell.ListObject.ListRows.Add AlwaysInsert:=true


Comment by: Ayaz S. Zanzeria (9-10-2011 21:05:13) deeplink to this comment

Hi,

In response to your comment,

Dim myTable As ListObject
Set myTable = ThisWorkbook.Worksheets("Sheet1").ListObjects("myTable")

For CurRow = myTable.DataBodyRange.Row To myTable.ListRows.Count

myVar = myTable[[#This Row], [Header1]]").Value
'do other stuff..

'The #This Row should obviously move to the next row for each iteration of CurRow
next

I tried this bit of code but it does not work. Please let me know what is missing.

I have a table and I want to extract the values using the header names and not the column indices.


Comment by: Jan Karel Pieterse (9-10-2011 23:58:14) deeplink to this comment

Hi Ayaz,

Like this:

    For currow = 1 To myTable.ListRows.Count

        myvar = myTable.ListColumns("Header1").DataBodyRange.Rows(currow).Value
        'do other stuff..
    Next


Comment by: Dovy Reiner (19-10-2011 09:38:30) deeplink to this comment

Pass Column Header in Table as Argument?

I use vba to sort a table, each time on different column.
I would like to have one generic Sort procedure, and pass the column header as argument.
All my attempts failed and I had to resort to writing specific proceduure for each and every column (ugly).

MS says: "... Because column headers are text strings, you cannot use expressions within brackets..."
http://office.microsoft.com/en-us/excel-help/using-structured-references-with-excel-tables-HA010155686.aspx

Is there a workaround?


Comment by: Jan Karel Pieterse (19-10-2011 23:26:11) deeplink to this comment

Hi Dovy,

Not sure what your problem is, but this appears to work:


Sub SortList(sColumnName As String, oLo As ListObject)
    oLo.Range.Sort key1:=oLo.ListColumns(sColumnName).Range, order1:=xlAscending, Header:=xlYes
End Sub

Sub Demo()
    SortList "a", ActiveSheet.ListObjects(1)
    SortList "b", ActiveSheet.ListObjects(1)
End Sub


Comment by: Dovy Reiner (24-10-2011 12:51:43) deeplink to this comment

Jan,

My problem was that I insisted on using the new structured reference and forgot about the old and simple style.

At the end, I was able to get it right like this:


Sub SortManager(sHeader1 As String, Optional sHeader2 As String)
With ThisWorkbook.Worksheets("MData").ListObjects("tblMain").Sort
    .SortFields.Clear
    .SortFields.Add Key:=Range("tblMain[" & sHeader1 & "]"), SortOn:=xlSortOnValues, _
                    Order:=xlAscending, DataOption:=xlSortNormal
                    
    If sHeader2 <> "" Then
     .SortFields.Add Key:=Range("tblMain[" & sHeader2 & "]"), SortOn:=xlSortOnValues, _
                     Order:=xlAscending, DataOption:=xlSortNormal
    End If

    .Header = xlYes
    .MatchCase = False
    .Orientation = xlTopToBottom
    .Apply
End With
End Sub


Dovy


Comment by: Merv Gleeson (26-10-2011 12:49:32) deeplink to this comment

Hi Jan

I'M new to all this, so can you help me with a small problem
please?

Example

Wk/Sheet 1 Table col 1
A    
B
C
D
E
F
G
H
I
J
K

I need to have a script to produce another wk/sheet where by it picks out every 3rd row ie
A
D
G
J etc

Does that make any sence ?

Merv


Comment by: Jan Karel Pieterse (27-10-2011 02:00:21) deeplink to this comment

Hi Merv,

This formula will do the trick, provided you start on row 1:

=OFFSET(Sheet1!$A$1,(ROW()-1)*3,0,1,1)


Comment by: jay (27-10-2011 06:24:58) deeplink to this comment

Hi, I have bunch of excel files (.xls or .xlsb) which i am looking to convert to pdf files with the same file name as those given to the excel files. I am looking for a vba macro and am somewhat familiar but no where near an expert. I believe we have ADOBE Acrobad 9 standard. So I have the option of goin to the excel file and printing a pdf..

I need the macro to go to individual files in a folder on my desktop and print pdf out of them and then save them automatically in another folder on desktop with the same file name as orginally assigned to as an excel file. Please HELP good sir..


Comment by: Jan Karel Pieterse (27-10-2011 08:17:59) deeplink to this comment

Hi Jay,

Please visit http://www.eileenslounge.com for a swift answer to your question!


Comment by: Shafqat (4-11-2011 01:05:51) deeplink to this comment

excellant work


Comment by: Amado (19-11-2011 05:38:05) deeplink to this comment

ok, you show how to select rows, columns, etc... but how I could select only one element of the table, for example, in row 3, column 5 ?

Sorry if it's a begginer question !!

Tanks a lot, excellent article.


Comment by: Jan Karel Pieterse (20-11-2011 22:53:20) deeplink to this comment

Hi Amado,

The third row in the list column with a title of "b" can be found like so:

ActiveSheet.ListObjects(1).ListColumns("b").DataBodyRange.Cells(3, 1)


Comment by: Jen (22-11-2011 07:25:02) deeplink to this comment

Hi,
I'm working on a macro to find and replace the content of several columns in a table. Is there a way to adapt the line:

oSh.Range("Table1[Column2]").Select

to work for a range of columns, excluding the headings.

Thanks very much!


Comment by: Jan Karel Pieterse (22-11-2011 07:50:47) deeplink to this comment

Hi Jen,

Something like this should do it:

oSh.range(oSh.range("table1[column1]"),oSh.range("table1[column3]"))


Comment by: Klaus (22-11-2011 09:53:50) deeplink to this comment

Hi,

just wondering if anyone knows how to create UDFs that can read/write whole columns of an Excel 2007 table by passing them as arrays?


Comment by: Jan Karel Pieterse (22-11-2011 10:09:06) deeplink to this comment

Hi Klaus,

You can have the UDF return an array, enter the UDF as an array formula into a range of cells. Or have I misunderstood the question?


Comment by: William Thornton (2-12-2011 13:04:45) deeplink to this comment

Good day,

I am trying to find basic code to get vba to index a table, so I can use a macro to index the same table for dif cells. My example is as follows. In cells D6, D7 etc. I have a value 1 0r 3. In cells F6, F7..... I have a value that matches a cable size. My table has a row with all the cable sizes and two columns, one with a 1, and one with a 3. after accessing the two values I want to return a value that corresponds to the matching cell. Once I have the value I can use it in a equation. I'm pretty sure I can figure out the for or do loop structure to go thru a table from top to bottom but I have been unable to locate code that imulates the Index function in excel. Any Help is appreciated.

Thanks, Will


Comment by: Jan Karel Pieterse (5-12-2011 01:34:45) deeplink to this comment

Hi William,

The simplest way is to add a calculated column in the lookup table which combines the two columns, using a delimiter which isn't in the data, like the pipe character (above the backslash): =A2&"|"&B2

Then use something like this to look up the data (note you have to point to the proper locations, which will fill in the proer table references in the formula below):

=INDEX(Tablereference,MATCH(COl1 & "|" & Col2,OtherTableNewColumn,0))


Comment by: Pavlinka (6-12-2011 07:58:27) deeplink to this comment

Hello

On my active spreadsheet I have a table called Table1.
In my macro, I am writing the formulas that are used to populate certain columns of the table.
in these formuas, elements from other columns appears, but I don't know how to get them.

If for instance I want to get the value in the table at the same row, but in the column called "Np", coult I say something like:

ActiveSheet.ListObject(Table1).ListColumns("Np")DataBodyRange.Cells([[#This Row],[Np]])

???

thanks!


Comment by: Jan Karel Pieterse (6-12-2011 08:28:36) deeplink to this comment

Hi Pavlinka,

I'm not sure what your question is.

You say you're writing formulas, but I suspect you meant to say you need the VBA syntax to get a value from the current row in a specific column inside a table.

Am I right?

Maybe you can try to explain what your macro does/should do?


Comment by: Alan (11-12-2011 23:03:59) deeplink to this comment

First off, great page! I have learned a great deal on working with tables in 07 and 10. I am working on a project that has multiple worksheets with one worksheet containing a "base" table called "ROSTER" being filled with constants. All the other worksheets contain tables that pull info from parts of the base table. I wanted different users to be able to update certain things on their related table and record the information on the base table and then show updated data in the users range by inserting a lookup formula that reflects the constant inserted into the base table. I have worked out most of this but have run into a few hang ups and have been beating my head against the wall. I will post Question in next post due to limit of 2000 charterers.
This is the code I use on a worksheet change event:

Private Sub Worksheet_Change(ByVal Target As Range)
    Application.EnableEvents = False
    Worksheet.Unprotect "password"
    Dim myValue As String
    Dim myRange As String
    Dim myID As Integer
    Dim myRow As Integer
    Dim myColumn As Integer
    Dim myColumnName As String
    Dim myTargetColumn As Integer
    Dim myFormula As String
    myValue = Target.Value
    myRange = Target.Address(RowAbsolute:=False)
    endRange = ActiveCell.Address
    myColumn = Range(myRange).Column
    myRow = Range(myRange).Row
    myID = Cells(myRow, 1) ' first column is a unique numbered ref for each row.
    myColumnName = Cells(2, myColumn) '2 is my header row
    myFormula = Cells(myRow + 1, myColumn).Formula ' gets formula for row below target
    myTargetColumn = Application.Match(myColumnName, Worksheets("ROSTER").Rows(2), 1)
    Worksheets("ROSTER").Range(Worksheets("ROSTER").Cells(myID, myTargetColumn).Address()).Value
= myValue
    Range(myRange).Formula = myFormula
    Worksheet.Protect "password", True, True, AllowSorting:=True, AllowFiltering:=True
    Application.EnableEvents = True
End Sub


Comment by: Alan (11-12-2011 23:04:19) deeplink to this comment

The previous code seems to work but I am having a sorting issue which I would like to allow users the ability to sort but there seems to be an issue sorting a protected table. Short of inserting Form controls over the current controls for sorting, I am at my wits end. I can't unprotect the sheet for the user because there are cells that I don't want edited. So my approach was to Trap their input into the default control, set in variables, and perform the sort through VBA. It is just a matter of catching the values of the users sort input which I can't seem to figure out. (Most important)
The next project will be allowing users to insert columns into their prospective worksheets and name it, once named, create a new column in the base table and name the column with the same name so my lookup formulas work and apply those formulas to the column the user created. As it is now Users must ask me to sort the data when they need it in different formats and add columns for them.
It would be nice to do it through VBA, Any help would be greatly appreciated and any suggestions on the code already developed would be equally welcomed.

I hope I have explained myself well enough, Thanks for any input!


Comment by: Jan Karel Pieterse (11-12-2011 23:08:32) deeplink to this comment

Hi Alan,

I see no problem with using VBA to do the sort where you start out by unprotecting the sheet, then doing the sort and finally protecting the sheet again.
Recoridng a macro when doing a manual sort whould give you a head start on the code.


Comment by: Asher Rodriguez (16-12-2011 12:47:35) deeplink to this comment

Hello JKP,

Regarding this section: "Special stuff: Sorting and filtering"

I see how you can call out a sort range by table and column name now ex: "Sort.SortFields.Add( _
                Range("Table1[[#All],[Column2]]")"

I am trying to figure out how to do that with the Autofilter. My problem is that if I use the column number for the "Field:=2" argument, then if someone inserta a column into the table, the macro will give me the wrong information.

But if in 2007 there is a way to call out the Field argument (range) by the table and column name, it would always look for the name and not the number so I will get the correct data no matter what columns are added (or removed).

Please let me know if this is possible.

Thanks for your time,

Asher


Comment by: Jan Karel Pieterse (18-12-2011 23:08:57) deeplink to this comment

Hi Asher,

You would have to "sort out" the columns number prior to setting up the filter, something like:

    Dim oLo As ListObject
    Dim lCol as Long
    Set oLo = ActiveCell.ListObject
    lCol = oLo.ListColumns("Column2").DataBodyRange.Column - oLo.DataBodyRange.Cells(1, 1).Column + 1


Comment by: Asher Rodriguez (23-12-2011 11:42:34) deeplink to this comment

How do Select the first cell in the first column in the first row of a listObject (not headers)?


Comment by: Bill (27-12-2011 22:18:52) deeplink to this comment

Hi Jan,

I am working on a macro that clears the data within a table that has been created. My purpose in doing so is to be able to repopulate the table later on. I find this useful because i have another table that has formulas linking to this one. The problem i am having is that once the table has no data, i seem to get an error preventing me from repopulating the table. Do you know why this is occurring and have any suggestions to fix this problem? I found this page to be invaluable. Thanks for your help!

Bill


Comment by: Jan Karel Pieterse (29-12-2011 07:11:01) deeplink to this comment

Hi Asher,

ActiveSheet.ListObjects(1).DataBodyRange.Cells(1,1)


Comment by: Jan Karel Pieterse (29-12-2011 07:12:26) deeplink to this comment

Hi Bill,

Which error do you get and can you show the relevant bit of code please?


Comment by: Asher Rodriguez (10-1-2012 13:52:19) deeplink to this comment

Hi JKP,

I am implementing the code bundle you gave me before above "You would have to "sort out" the columns number prior to setting up the filter, something like:

    Dim oLo As ListObject
    Dim lCol as Long
    Set oLo = ActiveCell.ListObject
    lCol = oLo.ListColumns("Column2").DataBodyRange.Column - oLo.DataBodyRange.Cells(1, 1).Column + 1 "

and I have a question.

It looks like this part: "lCol = oLo.ListColumns("Column2").DataBodyRange.Column" gives me the column number I need for the filter, so, what is this part: "- oLo.DataBodyRange.Cells(1, 1).Column + 1 " for? Is it necessary in some way that I am not figuring out?

Thanks for your time,

Asher


Comment by: Jan Karel Pieterse (11-1-2012 04:41:52) deeplink to this comment

Hi Asher,

The column number autofilter needs is counted from the top-left cell of the table you're autofiltering, whereas the Column property returns the column index number counting from column A (=col. 1) of the sheet. This is why I subtract the column number of the top-left cell of the table from the found column number and add 1.


Comment by: Asher Rodriguez (11-1-2012 06:35:49) deeplink to this comment

Oh I see. The reason it seems redundant in my code is because my table just happens to start at A1 (so to me it looked like it was subtracting 1 then adding 1).

But if my table started anywhere else in the sheet, the code would give a wrong index for the filter without that last portion. That's important to know.

Thanks for explaining!


Comment by: Wyeth (13-1-2012 14:33:03) deeplink to this comment

I am trying to name a table created withing a macro and getting an error. Debugger is telling me the following text is the issue.

ActiveSheet.ListObjects("BinderL").Name = "BinderLog"


I am VBA illiterate (pretty much) but have been able to stumble may way through some basics. Hope you can help. Thanks!


Comment by: Jan Karel Pieterse (15-1-2012 11:12:57) deeplink to this comment

What error do you get? Maybe the activesheet doesn't have alistobject by the name of BinderL, or alternatively it already has a listobject named "BinderLog"


Comment by: Craig (26-1-2012 10:09:26) deeplink to this comment

I'm trying to delete rows in my table where a certain field has a specific value. Can you show me some sample code for that?
thanks! -Craig


Comment by: Deepak kumar (6-2-2012 09:53:42) deeplink to this comment

I want to connect excel data base file to vba user form on click commond button and store the vba text data to excel file.


Comment by: Jan Karel Pieterse (8-2-2012 02:35:17) deeplink to this comment

Hi Deepak,

Please ask your question at:

http://www.eileenslounge.com/


Comment by: David (10-2-2012 06:31:43) deeplink to this comment

Is it possible to use ADODB connections to work with the data in an excel 2007 table using SQL?

I.e. I'd like to insert a table via the insert->table menu then in vba be able to access rows or cells from the table by writing sql.

E.g. "select column2 from Table1 where column1 = 150"

I know I should probably use an access database for things like this but I'm curious if it can work anyway.


Comment by: john coyne (21-2-2012 11:12:52) deeplink to this comment

how do i loop through the rows in a table once selected - here's essentially what i want to do:


For Each oLo In oSh.ListObjects
    For Each Row In oLo
        MsgBox oLo.Name & "row: " & this.rownumber
    Next
Next


Comment by: Jan Karel Pieterse (23-2-2012 03:04:20) deeplink to this comment

Hi John,

You could use


Dim oRow as Range
For Each oRow in ActiveCell.ListObject.DataBodyRange.Rows
    MsgBox oRow.Address
Next


Comment by: Patrick (23-2-2012 09:00:01) deeplink to this comment

The code for sorting a table based on the contents of a specifici column works fine:


.Sort.SortFields.Add(Range("Table1[[#All],[Column2]]"),xlSortOnCellColor, xlAscending, , xlSortNormal).SortOnValue.Color = RGB(255, 235, 156)
.

but what if I need a routine in which I can dynamically change that column?

Say, I want to change [Column2] by whatever column was selected through a combobox on a userform.

I just can't get the required syntax right and always get a run-time error.


Sub Sort_By_Column(Col_Name As String, Sort_Order As Long)
    ActiveWorkbook.Worksheets("TaakDB").ListObjects("Table3").Sort.SortFields.Clear
    ActiveWorkbook.Worksheets("TaakDB").ListObjects("Table3").Sort.SortFields.Add _
        Key:=Range("Table3[[#All],[Col_Name]]"), SortOn:=xlSortOnValues, Order:= _
        xlAscending, DataOption:=xlSortNormal
    
        With ActiveWorkbook.Worksheets("TaakDB").ListObjects("Table3").Sort
            .Header = xlYes
            .MatchCase = False
            .Orientation = xlTopToBottom
            .SortMethod = xlPinYin
            .Apply
        End With
End Sub
.


Comment by: Jan Karel Pieterse (24-2-2012 07:57:13) deeplink to this comment

Hi Patrick,

The key is with this part of your code I expect:

... Key:=Range("Table3[[#All],[Col_Name]]"), ...

Suppose the name of the column is in a variable names sColName, then change the piece above to:

... Key:=Range("Table3[[#All],[" & sColName & "]]"), ...


Comment by: Patrick (27-2-2012 01:52:57) deeplink to this comment

Thanks Jan Karel,

It's working now! :-)


Comment by: John (10-3-2012 22:31:01) deeplink to this comment

New to VBA in excel, understand the context, have a table macro that auto sorts when a value of a cell is changed according to an input reference. ie. value of "AWP,FMC" etc. All values change and auto sort except the first row (8). One site referenced changing .Header = xlYes to xlNo. Same problem exist. I have been looking for a reference to .Header objects for an explenation but get more "add a picture to a header, change a header" reference on the "Internut". (1) is there a reference book or sheet for each line object(tag) for reference so I can understand what I am looking at as termenology goes (2) a .Header would tell me that there is a header in the table and should not be used in value of (H8:H23). If I am wrong please advise.

Thank you for your time


Comment by: Jan Karel Pieterse (12-3-2012 00:59:10) deeplink to this comment

Hi John,

Can you perhaps post the relevant part of your VBA code?


Comment by: Arretta (14-3-2012 09:23:21) deeplink to this comment

I have a dataTable in Excel 2007 that contains columns A-P that are entered via a UserForm which works perfectly. There are formulas in columns Q through KO (285 columns, wow). So far so good.

Steps my user goes through:
Step 1) Enter the data via UserForm starting on row 2 of the dataTable
Step 2) Generate Reports, Graphs, Etc. for this project
Step 3) Saves the data only (columns A-P) to an external file (which can be imported later if need be)
Step 4) Clears dataTable to start new project

Steps 1 through 3 are working fine, it is step 4 that is causing the problem. When I clear the data in the table I leave row 1 intact so it keeps the formulas in columns Q-KO. This works if I do it manually but when I do it with this macro, the next time they add data the formulas don't continue down. I am beginning in the 2nd row of the dataTable, could that be the problem? Also, I am using EntireRow.Delete for rows 2 to the end because there could be 500 or so rows of data.

Your help is greatly appreciated.


Sub ResetData()
    Application.ScreenUpdating = False
    ActiveSheet.DisplayPageBreaks = False

    Sheets("Main Menu").Select
    Sheets("data").Visible = True
    Sheets("DATA").Select
    ActiveSheet.Cells.EntireColumn.Hidden = False
    ActiveSheet.Cells.EntireRow.Hidden = False
    Range("A7").Select

    Range("dataTable[[plot]:[vc3]]").Select
    Range(Selection, Selection.End(xlDown)).Select
    Selection.EntireRow.Delete 'ClearContents

'I tried this but then the formulas are gone completely
'    Range("dataTable").Offset(1).SpecialCells(xlCellTypeConstants).ClearContents

    Range("dataTable[[L1length]:[Form Class]]").Select
    Selection.EntireColumn.Hidden = True
    Range("A6").Select
    Sheets("data").Visible = False

    Application.ScreenUpdating = True
End Sub


Comment by: Bjørnar (14-3-2012 20:24:53) deeplink to this comment

Hi.

At work we use an excel sheet to keep track of inventory in our storage. I have created a few macros to help us with this, but I see now I'm not completely there. I have made a button that copies the latest storage layout with inventory and sums it up. The problem is that when copying to a new sheet, a table I have gets a new name, and the macro does not work, because it is set to work with "Table1".

I guess the solution is in the chapter "Listing the tables". I get that to work fine, but I am clueless how to actually get the macro to use the table name it comes up with. I only have one table for each active sheet.

The code in the "Listing the tables" - chapter returns for example "Table148773". (Seems it adds a number every time, instead of just going from 1 to 2 to 3 etc). So what I need is to put the "TableXXXXX" it returns into this code, instead of Table1:

Range("Table1[[#Headers],[Product]]).Select
ActiveSheet.ListObjects("Table1").sort.SortFields.Clear
-------- more code
Key:=Range("Table1[Product]"), SortOn:= etc....


This will then sort the products alphabetically.

Most of this is stuff I have recorded, but I did not think about the fact that the Table gets a new name for each sheet.

I've seen in the name manager, that the header Product exists for all the tables, so that one is being copied.

I've searched for hours now, and I never got further than getting to read the table number.

I'd appreciate any help you can give me.


Comment by: Jan Karel Pieterse (15-3-2012 02:38:26) deeplink to this comment

Hi Arretta,

Have you tried to replace:

Selection.EntireRow.Delete 'ClearContents

with:

Selection.Delete Shift:=xlUp


Comment by: Jan Karel Pieterse (15-3-2012 02:45:20) deeplink to this comment

Hi Bjørnar,

This seems to do the same and always uses the first table on the active sheet, regardless of its name:


Dim oLo As ListObject
Set oLo = ActiveSheet.ListObjects(1)
oLo.ListColumns("Product").Range.Rows(1).Select
oLo.Sort.SortFields.Clear


Comment by: Bjørnar (15-3-2012 12:05:59) deeplink to this comment

This did the trick! Thanks a million!


Comment by: Silke (3-4-2012 08:59:07) deeplink to this comment

Hello,

I would like to know how I add rows with data to an existing table.
In the example I copy data from two tables ("JDE" and "WISE") into one table ("upload").
So far I have this:


Sheets("JDE").Select
Range("JDE[QTY],JDE[SALES]").Select
Selection.Copy
Sheets("upload").Select
Range("upload[[#Headers],[IDINAD]]").Select
Selection.End(xlDown).Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
        :=False, Transpose:=False


My problem is that every time I copy and paste the second table (doesnt matter which one it is) the last column is overwritten. Which makes sense as xldown finds exactly that last row.

I have tried to solve the whole thing with 'offset' but then the data are pasted but not part of the table.

The code might look a wee bit odd due to the fact that I am a beginner with VBA so lots of google, forums, books, recording macros copy & paste...

Thank you so much for your help.

S.


Comment by: Jan Karel Pieterse (3-4-2012 22:30:19) deeplink to this comment

Hi Silke,

You could do it like this:
'...Part of your code up to:
Selection.Copy
Worksheets("upload").Activate
With Worksheets("upload").ListObjects("upload")
.DataBodyRange.Row(.DataBodyRange.Rows.Count).Select
End With
ActiveSheet.Paste Selection.Offset(1)


Comment by: Al (6-4-2012 21:09:43) deeplink to this comment

I am adding multiple records from an invoice form to another worksheet that holds all of the order lines of the all invoices. Even though the records are added to the bottom of the table6 they do not seem to be part of the table structure. Any formulas that are part of table6 are not implemented in the new records.

I'm not sure how to us the ListObjects object to add these records to the table. Data is added to cells A through AD in the new record, but I need to bring down the formulas from columns AK through AO, that allows me to automatically extract data to another worksheet if there is unique data in the new record, for example new customer name.

Also is there a simple way to refresh a pivot table that uses this table6 as its data source after the new records are added? Right now I just copied a macro that did the refresh.

Sub MoveRecord()

    Dim Increment As Integer 'looping variable
    Dim WSF As Worksheet ' Invoice worksheet Dim WSD As Worksheet ' SalesData worksheet
    Set WSF = Worksheets("Invoice")
    Set WSD = Worksheets("SalesData")
    NextRow = WSD.Cells(Rows.Count, 2).End(xlUp).Row + 1 ' (Rows.Count, 2), the 2 says which column number to check if blank
For Increment = 19 To 34                
    If WSF.Cells(Increment, 3) <> "" Then
    
    WSD.Cells(NextRow, 1).Resize(1, 31).Value = Array([D4], [F4], [P4], [C16], [F3], [A16], [B16], [c9], _
    Cells(Increment, 3), Cells(Increment, 4), Cells(Increment, 6), Cells(Increment, 7), Cells(Increment, 12), Cells(Increment, 2), Cells(Increment, 13), Cells(Increment, 8), Cells(Increment, 14), [D3], [D3], [D3], [D3], Cells(Increment, 15), Cells(Increment, 16), Cells(Increment, 17), Cells(Increment, 18), Cells(Increment, 19), [D3], [C8], [C13], [F6], [F16])
    
NextRow = WSD.Cells(Rows.Count, 2).End(xlUp).Row + 1

    End If
    
Next Increment

RefreshSalesData

End Sub



Thanks


Comment by: Jan Karel Pieterse (9-4-2012 10:11:18) deeplink to this comment

Hi,

This adds a new row below a table AND expands the table:

With Worksheets(1).ListObjects(1)
.DataBodyRange.Row(.DataBodyRange.Rows.Count + 1 ).Value = "New Row"
End With


Comment by: AlexJ (13-4-2012 10:57:58) deeplink to this comment

Jan Karel,
Do you know if it is possible to create events for ListObjects in Excel 2010? If so, do you have an example?


Comment by: Jan Karel Pieterse (15-4-2012 22:05:09) deeplink to this comment

Hi Alex,

No, there are no specific ListObject events as far as I know. You'll have to use the standard worksheet events.


Comment by: Chris (19-4-2012 16:10:39) deeplink to this comment

To AlexJ,

A good question and something i am currently looking at developing for our team.

Expanding on Jan Karel's response:

An idea might be to;

1. Use the worksheet cell 'change' or 'calculate' event to create a catch for changing cells.

2. Using a simple function that includes the VBA "Intersect" method you could test if the changed cell falls within the ObjectList.Range of your table.

EG.
'### Worksheet Change Event Capturing Cell
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ActCell As Range
Dim ActSheet As WorkSheet

Set ActSheet = ActiveSheet

Set ActCell = Target

Call IsInTable(ActCell,ActSheet)

End Sub

Function IsInTable(ActCell As Range, ActSheet As WorkSheet) As Boolean

Dim ObjLists As ListObjects
Dim ObjList As ListObject
Dim IsectRange As Range

Set ObjLists = ActSheet.ListObjects
IsInTable = False

For Each ObjList in ObjLists
Set IsectRange = Application.Intersect(ObjList.Range,ActCell)
If Not ISectRange Is Nothing Then
IsInTable = True
MsgBox "Cell found to be part of..." & Objlist.Name
Exit For
End If
Next ObjList

End Function


End Function

#### Warning #### may be mistakes above as i wrote this in a hurry and without testing. Hopefully it helps with ideas though.

Kind Regards.


Comment by: Jan Karel Pieterse (20-4-2012 02:55:35) deeplink to this comment

Hi Chris,

Your function can be simplified:

Function IsInTable(oRng As Range) As Boolean
    Dim oLo As ListObject
    On Error Resume Next
    Set oLo = oRng.Cells(1, 1).ListObject
    IsInTable = (Err.Number = 0) And Not oLo Is Nothing
End Function


Comment by: Osian Jones (20-4-2012 08:17:00) deeplink to this comment

Hi,

I'm having trouble trying to refer to data contained within Excel tables through a VLOOKUP formula in VBA.

My code never seems to be able to locate the Excel tables in my workbook even though I know their names are correct.

It always returns an Error 1004 which means it can't find the table. They are on a different worksheet so perhaps that may be the problem, in which case I don't know how to diplay this.

I'm querying multiple tables in a loop so it needs to vary the table name (FK_Header) within the VLOOKUP formula as shown:


For i = 1 To lookup_col_count - 1
    
        Lookup_col = 1 + i
        
        For r = 1 To cckdpt_row_count
    
        Cells(1 + r, Last_Column + (FK * i)).Select
        ActiveCell.FormulaR1C1 = _
        "=VLOOKUP([@" & FK_Header & "],Table_" & FK_Header & "_1," & Lookup_col & ",0)"
        
        Next r

    Next i


Any suggestions on how this works?

Thanks
Osian


Comment by: Ito (23-4-2012 16:43:56) deeplink to this comment

I am trying to assign only the filtered (visible cells) of a table to an array based on your example. I used the following code :


Dim oDataTable() as Variant

oDataTable = oSheet.ListObjects(sDataTable).Range.SpecialCells(xlCellTypeVisible).Value


I am getting up to the part where I have no hidden rows. My array size (1 to 31, 1 to 24) is up to the un-hidden rows ( i.e. rows 1 to 31 are not hidden but 32 to 34 are but 35 is not and so on.

Any help would really be appreciated

thx





Comment by: Jan Karel Pieterse (23-4-2012 23:25:24) deeplink to this comment

Hi Osian,

Does the formula get entered into the cell?
If so, is the formula correct?
One way to get the proper formula in VBA is to first enter a working formula in Excel. Then (with that cell active) go to the VBA editor, hit control+g and enter:

?Activecell.formula

and hit enter at the end.

Any quotes within the formula must be doubled-up:

=IF(A1="test","Yes","No")

becomes:

=ActiveCell.Formula = "=IF(A1=""test"",""Yes"",""No"")"


Comment by: Jan Karel Pieterse (23-4-2012 23:26:36) deeplink to this comment

Hi Ito,

You cannot assign filtered cells to a variant array, all values will be pushed into the array. This is "by design".

One way is to filter the array after it has been sent to VBA using array looping.


Comment by: Ito (24-4-2012 09:27:48) deeplink to this comment

Thank you for your prompt reply.
Is there a way to assign the non contiguous cells (from the table) to the arrray without having to loop through all the visible cells. By looping through the areas and assigning them to the array.

From what I unerstand assigning a range to an array is more

set Array = Range ...Value

efficient then loopping throug the cells, then assigning them one by one to the array.

Thx Again for ur help


Comment by: Jan Karel Pieterse (25-4-2012 01:03:40) deeplink to this comment

Hi Ito,

No, you simply cannot do that efficiently. The only way is to either pull in the entire range and filter in VBA, or to loop through the visible areas and fill arrays from there.


Comment by: venugopal (30-4-2012 06:03:55) deeplink to this comment

how do i locate a particular value in a table of numbers


Comment by: Jan Karel Pieterse (1-5-2012 04:47:31) deeplink to this comment

Hi Venugopal,

Use the Find option perhaps? (Control+F)


Comment by: Venugopal (6-5-2012 20:18:55) deeplink to this comment

Thanks for the help Jan KP


Comment by: AM (7-5-2012 23:10:48) deeplink to this comment

Hi,

I used your creating a table example just as a test.
I now do not know how to get rid the effects of running it on my worksheet.
I know this is a pretty silly situation, but could you please help me with how to 'undo' it?

Thanks!


Comment by: Jan Karel Pieterse (8-5-2012 02:04:38) deeplink to this comment

Hi AM,

You can convert a table back to a range.

Select any cell in the table

Press the Table Tools, Design tab item on the ribbon.

On the left-hand side of the ribbon it says "Convert to range". Press that button.


Comment by: Am (8-5-2012 17:01:56) deeplink to this comment

Thanks!


Comment by: Simon (31-5-2012 13:21:26) deeplink to this comment

Hi,
I have a table that I get from a sharepoint site that I am automating the creation of Pivot tables. Before I do that I would like to delete some of the rows based on criteria. In order to keep it as dynamic as possible I would like to automate the deletion based on the criteria and the column title rather than the position of the column. Is there a simple way of doing that?


Comment by: Jan Karel Pieterse (31-5-2012 14:08:47) deeplink to this comment

Hi Simon,

I don't have a Sharepoint environment to test, but can't you specify criteria when pulling in the sharepoint list?


Comment by: Simon (31-5-2012 14:44:13) deeplink to this comment

Hi Jan,
The way that I have handled that is by customising the sharepoint view with filters, unfortunately sharepoint has an inbuilt limit of 10 filters per view, and this is the 11th criteria. The code to import only pulls the data table in and doesn't manipulate it. Using your examples and explanations above it would be simple enough for me to do it based on the column number, but I was hoping to avoid that and treat it as any excel table regardless of where it comes from?


ActiveSheet.ListObjects.Add SourceType:=xlSrcExternal, _
    Source:=Array("URL string", "{LISTNAME string}", _
    "{VIEWNAME string}"), LinkSource:=False, Destination:=Range("A1")


Comment by: Jan Karel Pieterse (31-5-2012 15:01:11) deeplink to this comment

Hi Simon,

One thing you could do is -after refreshing the sharepoint list in Excel- save the file. Then base your pivot table on an external data source, use Excel as the data source and point it to the same excel file. Then you can use the query wizard to specify criteria.


Comment by: Simon (31-5-2012 15:26:47) deeplink to this comment

Thank you Jan, I will have a go!


Comment by: Gary (15-6-2012 18:12:58) deeplink to this comment

Thank you so much for all of this information! I am working with a list/table in excel 2007 that has mapped XML elements to retrieve data from an XML file.
I have noticed on initial load, when sending data to this list, it is extremely slow - but if I remove/or convert the list/table to a range, it is MUCH faster - but then I lose all of my mappings. Is there a way to "deactivate" the list or "hide" and can then reactivate it after the data initially comes across?
or, is there a way to create the XML map within VBA?
Thanks!


Comment by: Jan Karel Pieterse (18-6-2012 06:53:59) deeplink to this comment

Hi Gary,

If there is a lot to add to the list, one way to speed things up is by first gathering everything that needs to go into the list on a separate location. Then when all rows are ready to go, copy them in one go below the list.


Comment by: George Spaniak (19-6-2012 17:50:33) deeplink to this comment

Dear Jan,

I just wanted to thank you for your wonderful website. I am an amateur vba coder who spends a great deal of time on google trying to figure out solutions to my coding problems. Of all the excel help sites on the internet, I find myself coming back to yours by far the most. I wanted to thank you for both creating this site and sharing your knowledge. It has helped me a great deal and is greatly appreciated.

Sincerely,

George Spaniak


Comment by: Jan Karel Pieterse (19-6-2012 18:13:03) deeplink to this comment

Hi George,

You're welcome!


Comment by: Gary (22-6-2012 16:17:11) deeplink to this comment

Thank you very much for the reply!

Unfortunately, I am already bringing the data from SQL db to a separate sheet, then sending it over to the other page, which contains the list/table. I am stuck as I need the list as it contains the mapping to the XML elements, but it is also the sole cause (to my knowledge) of the slow down on load...

Is it possible to disable/re-enable a list/table? can the mapping be recreated in VBA, or can mappings only be created manually?

Thanks again!


Comment by: Henri Kayali (24-6-2012 10:46:15) deeplink to this comment

Hello Jan,

Again with tables.Thanks for all your help.

When inserting a table, Excel guesses automatically the data range.

I was wondering if you knew of some way in VBA to get this "guessed" range without necessarily having to insert the table.

Thanks a lot.


Comment by: Jan Karel Pieterse (25-6-2012 07:04:13) deeplink to this comment

Hi Henri,

That would be the Worksheets("Sheet1").CurrentRegion property.


Comment by: Jan Karel Pieterse (25-6-2012 07:37:59) deeplink to this comment

Hi Gary,

Yes you can programmatically add mappings. Perhaps you can find the information you need here (Look for page 99 to start):

http://books.google.nl/books?id=gtPQMxO8XuoC&printsec=frontcover&dq=cXML+pro+excel+2007+VBA&hl=nl&sa=X&ei=JfjnT9SLK-PE0QWu3fGfCQ&ved=0CD4Q6AEwAA#v=onepage&q&f=false


Comment by: Shagun Ajmera (29-6-2012 12:01:47) deeplink to this comment

can you tell me how to select the n'th cell in i'th column in a table


Comment by: Jan Karel Pieterse (29-6-2012 14:38:20) deeplink to this comment

Hi Shagun,

Untested:
ActiveSheet.ListObjects(1).ListColumns(i).Cells(n,1)


Comment by: R C Cox (28-7-2012 16:55:59) deeplink to this comment

Thanks for the introduction to coding Tables.

Reading the comments I think you have a typo in one answer. CurrentRegion needs a cell reference as well as the sheet.


Comment by: Jan Karel Pieterse (7-8-2012 11:27:42) deeplink to this comment

Hi R C,

You are right of course!

Worksheets("Sheet1").RAnge("A1").CurrentRegion


Comment by: Simon Knapper (30-8-2012 11:36:03) deeplink to this comment

Hello Jan
I found your article very informative and useful.
My question concerns the use of tables as a datasource in an ADO recordset.
Currently I use named ranges which works OK e.g.

myRecordset.Open "SELECT * FROM myNamedRange", myAdoConnection


but if I use a table
myRecordset.Open "SELECT * FROM myTable", myAdoConnection

It doesn't. Is it possible to use tables in this way?
Simon


Comment by: Jan Karel Pieterse (30-8-2012 12:58:44) deeplink to this comment

Hi Simon,

Unfortunately, you cannot use Excel Tables in ADO directly. However, if you create a "normal" range name which points to the table, you can use the normal range name.
Benefit: the range name will expand and contract with the size of the table.


Comment by: Tom Groszko (2-9-2012 19:16:30) deeplink to this comment

This page has helped me quite a bit, I am very new to VBA but an experienced developer. Your first example does not work if the table has no data. How do I test for the absense of data in a table? Likely obvious and simple but I am having trouble figuring this out.

Thanks


Comment by: Tom Groszko (2-9-2012 19:18:55) deeplink to this comment

that should have been the second example not the first, sorry about that.

Thanks


Comment by: Jan Karel Pieterse (3-9-2012 09:37:10) deeplink to this comment

Hi Tom,

One way could be by testing for the number of listrows:

If ActiveSheet.ListObjects(1).ListRows.Count = 0 Then
'No data
Else
'We have data
End If


Comment by: Deon (11-9-2012 20:22:38) deeplink to this comment

Best Article on VBA for Tazble I could find on the web so I hope you can assist me. (I am a real amateur when it comes to VBA)

I am working with a workbook in which I copy Worksheet which has two Tables on it. I rename the new worksheet to a date reference e.g. 11-09-2012 the tables are given names similar to the master sheet but with different numbers e.g. (Table 1 becomes Table 5 and Table 2 becomes Table 6) now I do not know what these numbers are going to be so I cannot use that table names in the following code I need to write. When I create a second or third copy of the Master sheet the Table numbers just keep on incrementing.

I want to use the code you provided to ListObjects but get dont seem to get the right way to use the Name which is displyed in teh Msgbox in a code line to rename it. Then If I have that Code how do i change the name for the second table

    Dim oSh As Worksheet
    Dim oLo As ListObject
    Set oSh = ActiveSheet
    For Each oLo In oSh.ListObjects
        Application.Goto oLo.Range
        oLo.Range.Select
        Selection.ListObjects.Name = "MasterData" & Range("H" & 2).Value
    Next

The Idea is to rename
Table1 to MasterData11-09-2012(Date Identified in Cell H2)
Table2 to SlaveData11-09-2012


Comment by: Jan Karel Pieterse (12-9-2012 09:48:22) deeplink to this comment

Hi Deon,

You could do it like this:

Sub RenameTables()
    Dim oLo As ListObject
    For Each oLo In ActiveSheet.ListObjects
        If LCase(oLo.Name) Like "table1*" Then
            oLo.Name = "MasterData" & Range("H2").Value
        Else
            oLo.Name = "SlaveData" & Range("H2").Value
        End If
    Next
End Sub


Comment by: David Darby (17-9-2012 01:52:52) deeplink to this comment

This button code works like a charm to clear all filters in my xl2010 datasheet.
How can I make it work on the addition of "Table1" to the sheet?


Private Sub CommandButton1_Click()
With ActiveSheet
    If .AutoFilterMode Then
     If .FilterMode Then
        .ShowAllData
     End If
    End If
End With
End Sub


Comment by: Jan Karel Pieterse (17-9-2012 09:24:53) deeplink to this comment

Hi David,

Probably like this:


Sub RemoveFilters()
    Dim oLo As ListObject
    For Each oLo In ActiveSheet.ListObjects
        oLo.AutoFilter.ShowAllData
    Next
With ActiveSheet
    If .AutoFilterMode Then
     If .FilterMode Then
        .ShowAllData
     End If
    End If
End With
End Sub


Comment by: prakash (23-9-2012 15:06:13) deeplink to this comment

How do you copy a range in table


Comment by: Jan Karel Pieterse (24-9-2012 11:42:13) deeplink to this comment

Hi Prakash,

Depends; what "range" do you want copied and to where?


Comment by: NigelG (7-10-2012 22:59:04) deeplink to this comment

Hi, Great thread however when I got to the end, had forgotten what I was looking for despite picking up 'never know when I might need that' code snippets!

I have a version of this which works using conventional 'range' notation. The challenge I have is to convert the process to work with a Table/ListObject notation hopefully using structured references.
The process involves 'resetting' a table by:
a. Archiving rows based on filtered 'Status' onto a newly added worksheet using Copy with 'visible' cells and multiple PasteSpecial(s) (this works).
b. Deleting the archived rows from the original worksheet (I can't get any combination of ListObject syntax to work)
c. Applying ClearContents to complete columns (less headers of course) (This works)
d. Applying pre-sets to multiple columns in the rows selected by filter (this works).

Code snippet:
VB
With oloTableName
        .Range.AutoFilter Field:=1, _
            Criteria1:="<>Paid", Operator:=xlAnd
    ' Copy the visible cells including the headings
        .Range.SpecialCells(xlCellTypeVisible).Copy
    ' Use Paste Special to transfer to the new worksheet
        With wksArchive.Range("A1")
            .PasteSpecial Paste:=xlPasteColumnWidths,        
operation:=xlNone, _
                skipblanks:=False, Transpose:=False
            .PasteSpecial Paste:=xlPasteValuesAndNumberFormats, operation:=xlNone, _
                skipblanks:=False, Transpose:=False
        End With
    ' Now delete the rows offloaded to archive
    ' .Range.SpecialCells(xlCellTypeVisible).ListRow.Delete


Comment by: Nigelg (7-10-2012 23:25:00) deeplink to this comment

Tried to post earlier but 'submit' sent before I had finished so this time a lot briefer;
What is the syntax for deleting rows from Tables/ListObjects when the table has a filter applied that is I only want to delete the rows that are 'visible'?

Thanks,
A great thread by the way. Code snippets all the way.
Nigel


Comment by: Hi Nigel, (8-10-2012 08:49:19) deeplink to this comment

I expect it might take something like this:

Sub RemoveFilteredRows()
    Dim oCell As Range
    Dim lCt As Long
    With ActiveSheet.ListObjects(1)
        For lCt = .ListRows.Count To 1 Step -1
            If .ListRows(lCt).Range.EntireRow.Hidden = False Then
                .ListRows(lCt).Range.EntireRow.Delete
            End If
        Next
    End With
End Sub


Comment by: NigelG (9-10-2012 10:25:41) deeplink to this comment

Hi Jan,
Thanks for the prompt answer re: Deletion of only "visible" rows in a filtered table I was trying to optimise the process just to one statement rather than an iterative process which you have offered. Is it me or are there inconsistencies in the way that VBA Object, methods and properties are referenced?

For example, I can copy all visible rows using


With oloTableName
' Copy the visible cells including the headings
        .Range.SpecialCells(xlCellTypeVisible).Copy
' Cut also works but that cuts the headings as well- Doh!
' Attempted deletion that follows doesn't work
' Now delete the rows offloaded to archive
    ' .Range.SpecialCells(xlCellTypeVisible).ListRow.Delete
End With

When I recorded the original macro along with the usual smattering of '.Select' this and Selection.that statements captured, having selected the visible rows (and header) it left me with a very simple Selection.EntireRow.Delete. I have tried the EntireRow.Delete method specification and that didn't work in my code.
Any idea why the methods that are applicable to a 'Selection' are not available to object referenced streamlined code (that is code that removes all the supposed inefficiencies of 'Select' and 'Activate' stuff).
Thanks, Nigel


Comment by: Jan Karel Pieterse (9-10-2012 10:31:13) deeplink to this comment

Hi Nigel,

I just tried and it appears you cannot delete a set of non-consecutive rows when one or more of the rows are part of a table. Hence the iteration.


Comment by: Newb (14-11-2012 15:42:45) deeplink to this comment

I liked the section on inserting a new row to a table, however I'm trying to insert the row to the end of the table and then select the first cell of that new row. H E L P (I have no idea what I'm doing). Thanks for this thread! Vette


Comment by: Jan Karel Pieterse (16-11-2012 16:01:58) deeplink to this comment

Hi Newb

Does my answer to this comment:

"Comment by: Silke (4/3/2012 8:59:07 AM)"

Help?

To view all comments, click:

https://jkp-ads.com/articles/Excel2007TablesVBA.asp?AllComments=True


Comment by: Jim Malkowski (24-11-2012 12:57:36) deeplink to this comment

Hi. Got to share this with this great thread. It's some code I wrote to make row deletes from a table. I found that it is possible to delete non-contiguous selections from a protected table, and without sorting. Anyone who can improve this, please post back.

Private Sub DeleteRow_Click()                     ' delete all rows which have any cell in the row selected

Dim xLoop As Long                                 ' xLoop counts rows to be deleted
Dim xMyCell As Range, xMyRange As Range, fRange As Range 'fRange is the range of rows to be deleted
Dim xMyArray() As String                         ' xMyArray accumulates the unique row numbers to be deleted
Dim xz As Variant                                 ' xz is used for the array search to make sure rows aren't duplicated

Application.ScreenUpdating = False 'set selection to visible cells, ActiveCell if only 1 cell selected; initialize fRange with 1st selected cell
Set xMyRange = IIf(Selection.Count > 1, Selection.SpecialCells(xlCellTypeVisible), ActiveCell): Set fRange = xMyRange(1)
For Each xMyCell In xMyRange                     ' find rows to be deleted
    ReDim Preserve xMyArray(xLoop)                 ' get unique rows(as string) for duplicate row check
    xz = Filter(xMyArray, CStr(xMyCell.Row))     ' check if cell row is already selected, if not add row to range to be deleted
    If UBound(xz) < 0 Then xMyArray(xLoop) = CStr(xMyCell.Row): Set fRange = Union(fRange, xMyCell): xLoop = xLoop + 1 'increment counter
Next
If MsgBox("The " & IIf(xLoop = 1, "selected row", xLoop & " selected rows") & " will be deleted", 305, "Confirm Delete . . .") = vbOK Then
    For Each xMyCell In fRange.Areas
        xMyCell.EntireRow.Delete                 ' delete range of all rows to be deleted
    Next
End If
ActiveCell.Select
Erase xMyArray
End Sub


Comment by: Jan Karel Pieterse (25-11-2012 19:11:20) deeplink to this comment

Hi Jim,

Thanks!


Comment by: Carey (7-12-2012 11:12:13) deeplink to this comment

This is a great article! How do I reference just a single cell? Like row 4 of column 'lastname'? Sorry if this is a stupid question but I'm kind of a newbie.


Comment by: Jan Karel Pieterse (7-12-2012 13:10:09) deeplink to this comment

Hi Carey,

For example like this:

Sub test()
    With ActiveCell.ListObject
        MsgBox .ListColumns("lastname").DataBodyRange.Rows(4).Value
    End With
End Sub


Comment by: Carey (7-12-2012 18:52:54) deeplink to this comment

Thanks, Jan! I completely missed the part where I could pass the column name to the ListColumns method. All of the examples above use the column number with the ListColumns method and the column name with the Range method so I thought that was a hard and fast rule.


Comment by: Lance Thomas (30-12-2012 07:03:22) deeplink to this comment

Is it possible to auto-sort a table with three columns after the new data is entered in the third column?

Sub Macro10()
'
' Macro10 Macro
'

'
    ActiveWorkbook.Worksheets("Symbol List").ListObjects("Table4").Sort.SortFields. _
        Clear
    ActiveWorkbook.Worksheets("Symbol List").ListObjects("Table4").Sort.SortFields. _
        Add Key:=Range("Table4[[#All],[Symbol]]"), SortOn:=xlSortOnValues, Order _
        :=xlAscending, DataOption:=xlSortNormal
    With ActiveWorkbook.Worksheets("Symbol List").ListObjects("Table4").Sort
        .Header = xlYes
        .MatchCase = False
        .Orientation = xlTopToBottom
        .SortMethod = xlPinYin
        .Apply
    End With
End Sub


Symbol is Column 1; Description is Column 2; and Exchange is Column 3.


Comment by: Jan Karel Pieterse (2-1-2013 13:47:46) deeplink to this comment

Hi Lance,

I would probably just tie your macro to a button placed just above your table, that is easiest.


Comment by: Lance (3-1-2013 02:53:13) deeplink to this comment

Hello Jan,

I had played with it for a while and was able to get it working without tying it to a button:

Private Sub Worksheet_Change(ByVal Target As Range)
    ActiveWorkbook.Worksheets("Symbol List").ListObjects("Table4").Sort.SortFields. _
        Clear
    ActiveWorkbook.Worksheets("Symbol List").ListObjects("Table4").Sort.SortFields. _
        Add Key:=Range("Table4[[#All],[Symbol]]"), SortOn:=xlSortOnValues, Order _
        :=xlAscending, DataOption:=xlSortNormal
    If Not Intersect(Target, Range("Table4[[Exchange]]")) Is Nothing Then
        With ActiveWorkbook.Worksheets("Symbol List").ListObjects("Table4").Sort
            .Header = xlYes
            .MatchCase = False
            .Orientation = xlTopToBottom
            .SortMethod = xlPinYin
            .Apply
        End With
    End If
End Sub


Thanks for the additional idea…


Comment by: Joe (4-1-2013 23:04:42) deeplink to this comment

I want to insert a table via VBA into a data range of unknown size. I know where the range will start, but its size will vary depending on the amount of info collected. The number of columns will stay the same, but the number of rows will increase or decrease.

For example:
ActiveSheet.ListObjects.Add(xlSrcRange, _
Range("$A$3:$Q$[???]"), , xlYes).Name = _
        "Table1"

Where I don't know what [???] will be.

I tried to look for this answer, but I just can't sift through five years of responses! Thanks!


Comment by: Jan Karel Pieterse (5-1-2013 20:28:19) deeplink to this comment

Hi Joe,

Instead of Range("$A$3:$Q$[???]"), you could use:

Range("$A$3").CurrentRegion


Comment by: Joe (7-1-2013 23:12:09) deeplink to this comment

So easy and so effective. Thanks Jan!!


Comment by: Bambi (19-1-2013 08:55:20) deeplink to this comment

it is not possible to insert a new table at parts of the existing table, e.g. with another makro, so how to remove the existing table?


Comment by: Jan Karel Pieterse (20-1-2013 19:00:59) deeplink to this comment

Hi Bambi,

Can you please explain a little more, I don't understand what you mean?


Comment by: Kpooz (21-1-2013 16:04:55) deeplink to this comment

I cant get this one to work, i need to be able to use the same macro for multiple tables on multiple worksheets.

wsx is a a stored integer adding 1 for each time the macro is repeated.
lastrow is a stored integer saying wich row is the last row containing something.
tbl is a listobject.


    ActiveSheet.ListObjects.Add(xlSrcRange, Range("A3", "L" & lastrow), , xlYes).Name = "Table" & wsx
    Set tbl = "Table" & wsx
    ActiveSheet.ListObjects(tbl).Sort.SortFields.Clear
     ActiveSheet.ListObjects(tbl).Sort.SortFields.Add Key _
        :=Range(tbl & "[[#All],[Adress]]"), SortOn:=xlSortOnValues, Order:= _
        xlAscending, DataOption:=xlSortNormal
    With ActiveSheet.ListObjects(tbl).Sort
        .Header = xlYes
        .MatchCase = False
        .Orientation = xlTopToBottom
        .SortMethod = xlPinYin
        .Apply
    End With


Comment by: Jan Karel Pieterse (22-1-2013 08:49:13) deeplink to this comment

Hi Kpooz,

I'm not sure what exactly your macro is supposed to do.
How do the two variables you mention get their value?
If I read your code, none of the ranges are curreently formatted as table, correct?


Comment by: Kpooz (22-1-2013 11:28:12) deeplink to this comment

Hi

This is how the variables get their values;


Dim ws As Worksheet
Dim wsx As Integer
Dim lastrow As Integer
Dim tbl As ListObject

wsx = 0

For Each ws In ThisWorkbook.Worksheets

If ws.Name = "Sheet2" Then
    wsx = wsx + 2
Else
    wsx = wsx + 1
End If

If wsx > ThisWorkbook.Worksheets.Count Then
    Exit For
End If

    Sheets(wsx).Select
    lastrow = Cells(Rows.Count, "A").End(xlUp).Row


And the macro is supposed to create a table named table + the active worksheet number (wsx). And add a ascending sorting. The problem is how to use a variable (tbl) to select a table and add sorting;
Set tbl = "Table" & wsx

And
(tbl & "[[#All],[Adress]]")


I dont know if this is possible, maybe by selecting a table using currentregion or something similar.

Thanks / Kpooz


Comment by: Jan Karel Pieterse (22-1-2013 14:04:40) deeplink to this comment

Ah, now I get it.

Perhaps like so:


    Dim tbl As ListObject
    Set tbl = ActiveSheet.ListObjects.Add(xlSrcRange, Range("A3", "L" & lastrow), , xlYes)
    With tbl
    .Name = "Table" & wsx
        .Sort.SortFields.Clear
        .Sort.SortFields.Add Key:=.ListColumns("Address").Range, SortOn:=xlSortOnValues, Order:= _
                             xlAscending, DataOption:=xlSortNormal
        With .Sort
            .Header = xlYes
            .MatchCase = False
            .Orientation = xlTopToBottom
            .SortMethod = xlPinYin
            .Apply
        End With
    End With


Comment by: Kpooz (22-1-2013 14:59:13) deeplink to this comment

That did it!

Thanks alot!


Comment by: royce (28-1-2013 08:27:19) deeplink to this comment

I was trying to find a way to query a Excel table using SQL statement.
Please can you help me with a sample example, how this can be acehived.


Comment by: Jan Karel Pieterse (28-1-2013 10:55:04) deeplink to this comment

Hi Royce,

Have a look at this page:

http://erlandsendata.no/?p=2139


Comment by: Alan (12-2-2013 20:29:03) deeplink to this comment

I am copying table rows (2010) from one worksheet and pasting in another worksheet in the same workbook. How can I return to the first page and remove that section from the existing row?


Comment by: Jan Karel Pieterse (13-2-2013 08:55:20) deeplink to this comment

Hi Alan,

Can you please post the relevant bit of your code?


Comment by: Alan (13-2-2013 19:11:45) deeplink to this comment


Sub DUE_to_YTD()
'
'
' Keyboard Shortcut: Ctrl+Shift+ D
'
If ActiveSheet.Name = "DUE" Then
strCell = ActiveCell.Address(ReferenceStyle:=xlA1)
Else
Exit Sub
End If
    Application.ScreenUpdating = False
    Range("A" & ActiveCell.Row).Select
    ActiveCell.Range("A1:I1").Select
    Selection.Cut
    Application.Goto Reference:="YTD_Totals"
    Application.Goto Reference:="R35000C1"
    Selection.End(xlUp).Select
    ActiveCell.Offset(1, 0).Range("A1").Select
    ActiveSheet.Paste
    Selection.FormatConditions.Delete
    Selection.Borders(xlDiagonalDown).LineStyle = xlNone
    Selection.Borders(xlDiagonalUp).LineStyle = xlNone
    Selection.Borders(xlEdgeLeft).LineStyle = xlNone
    Selection.Borders(xlEdgeTop).LineStyle = xlNone
    Selection.Borders(xlEdgeBottom).LineStyle = xlNone
    Selection.Borders(xlEdgeRight).LineStyle = xlNone
    Selection.Borders(xlInsideVertical).LineStyle = xlNone
    Selection.Borders(xlInsideHorizontal).LineStyle = xlNone
    ActiveCell.Range("A1:I1").Select
    With Selection.Interior
        .Pattern = xlSolid
        .PatternThemeColor = xlThemeColorAccent1
        .Color = 13434879
        .TintAndShade = 0
        .PatternTintAndShade = 0.799981688894314
    End With
    With Selection.Interior
        .Pattern = xlNone
        .TintAndShade = 0
        .PatternTintAndShade = 0
    End With
    Range("A2").Select
    ActiveSheet.Previous.Select
    Selection.ListObject.ListRows(5).Delete
    ActiveCell.Select
    ActiveWindow.ScrollRow = 2
    Application.ScreenUpdating = True
    ActiveCell.Select
End Sub


Comment by: Jan Karel Pieterse (14-2-2013 08:43:01) deeplink to this comment

Hi Alan,

Before writing any code:
I think you are copying columns A:I from the current row to another worksheet. SO if you are on row 5, you copy A5:I5.
Am I correct in assuming you want to remove A5:I5 from the table?


Comment by: Alan (14-2-2013 21:22:02) deeplink to this comment

Thanks Jan.

No, that is the problem. When recorded it identifies that section of row and I need something that is not so absolute but rather based on the row it lands on when returning from the other worksheet.


Comment by: Jan Karel Pieterse (15-2-2013 15:54:21) deeplink to this comment

It is not clear to me which rows(s) need to be removed. What is the logic behind it?


Comment by: Alan (15-2-2013 19:17:37) deeplink to this comment

Thanks. What happens is once a task from the table is complete, the section is moved to a worksheet to retain for history. Once that row is moved the macro needs to return to the first/current sheet and remove that section of cells. Does this make sense?


Comment by: Jan Karel Pieterse (18-2-2013 08:28:45) deeplink to this comment

Hi Alan,

I reworked your code a bit (Selecting is not needed and it is faster not to). Please check whether this works on a copy of your file:

Sub DUE_to_YTD()
'
'
' Keyboard Shortcut: Ctrl+Shift+ D
'
' Variable to Remember the current selection
    Dim oSel As Range
    Set oSel = Selection
    If ActiveSheet.Name = "DUE" Then
        strCell = ActiveCell.Address(ReferenceStyle:=xlA1)
    Else
        Exit Sub
    End If
    Application.ScreenUpdating = False
    Range("A" & ActiveCell.Row).Resize(, 9).Cut
    With Worksheets("YTD")
        .Paste .Range("A" & .Rows.Count).End(xlUp).Offset(1)
        With .Range("A" & .Rows.Count).End(xlUp)
            .FormatConditions.Delete
            .Borders(xlDiagonalDown).LineStyle = xlNone
            .Borders(xlDiagonalUp).LineStyle = xlNone
            .Borders(xlEdgeLeft).LineStyle = xlNone
            .Borders(xlEdgeTop).LineStyle = xlNone
            .Borders(xlEdgeBottom).LineStyle = xlNone
            .Borders(xlEdgeRight).LineStyle = xlNone
            .Borders(xlInsideVertical).LineStyle = xlNone
            .Borders(xlInsideHorizontal).LineStyle = xlNone
        End With
        Application.ScreenUpdating = True
    End With
    Intersect(oSel.EntireRow, oSel.Parent.Range("A:I")).Delete xlUp
End Sub


Comment by: Kabir Ahmed (24-2-2013 08:30:50) deeplink to this comment

This will be appreciated if you provide me answer to the following queries:
1) How I can know that few cells of Excel are at VBA default.
2)How I can remove those defaults.
3)I am new in VBA , can you guide me how I can learn that .

Thanking you.


Comment by: Jan Karel Pieterse (24-2-2013 21:18:00) deeplink to this comment

Hi Kabir,

I kindly request you to ask your question at www.eileenslounge.com
There are lots of people there who can help you with this question.


Comment by: Wayne P (26-2-2013 18:32:07) deeplink to this comment

Good article on tables, but I am wondering if you can comments on the memory usage of tables. I have a spreadsheet that is approx 44000 rows and 60 columns in a table. When the data is configured as a TABLE in Excel the memory requirements are huge....1.2GB. If I make one simple change of converting the table to a range the memory requirements drop by 500MB. My spreadsheet that was unusable because it was bumping into Excel's memory limit is now usable. Is this surprising to you? It is to me and I haven't read anything about the penalty you pay for using tables. I am now in the process of undoing my use of tables. Nice feature but apparently very expensive!

Thank You


Comment by: Jan Karel Pieterse (27-2-2013 06:40:31) deeplink to this comment

Hi Wayne,

There must be something else wrong with that file. I just created a file with only one table of 60 cols * 40,000 rows.

A plain table gives me a file of 35,096 Kb.
FOrmatting the range as table gives a bit more: 35,097 Kb.


Comment by: Wayne P (28-2-2013 21:50:03) deeplink to this comment

Jan,

It looks like you're referring to the file size which makes sense to me...my file size is very similar in both cases: range and table. What is different is the memory usage in task manager. I have tried this many times and always get the same result: the same data configured as a range uses SIGNIFICANTLY less memory than when configured as a table. I thought file corruption as well initially but my testing leads me to believe otherwise.


Comment by: Jan Karel Pieterse (1-3-2013 15:28:35) deeplink to this comment

Hi Wayne,

I just tried, for me memory usage increased from 72 mb to 73 mb, which is more than the filesize increase, but nothing dramatic.


Comment by: Marv Carson (5-3-2013 19:04:26) deeplink to this comment

When converting a range to a table using Excel 2010, two unwanted things occur by default.

1. The Column widths are re-sized greater than the preferred column widths of the original range.

2. Alternate row 'shaded banding' is applied when my preference to retain the 'No Fill' background format.

I know the steps to readjust both these, but prefer to avoid them by default to avoid the extra 'corrective steps.


Comment by: Hui... (7-3-2013 16:24:30) deeplink to this comment

In Excel 2010 the Name and Address properties have been changed

Instead of: oLo.Range.Address

They are now: oLo.DataBodyRange.Address


Comment by: Jan Karel Pieterse (8-3-2013 13:22:00) deeplink to this comment

Hi Hui,

Actually, Range.Address returns the entire range of the listobject, including the header row, whereas DataBodyRange is the listobject *without* the header row.


Comment by: sah (8-3-2013 18:03:34) deeplink to this comment

I was wondering if there is a way to let the user add rows to a protected table and still get the formulas in the cells that's supposed to have formulas?

I have a table which is protected and users has access to certain "data entry" cells. One user should have the access to add "records". When the "insert row" option is allowed when protecting sheet, the user is allowed to insert a row but the row is blank (without formulas in cells that does calculation. Is there a way to fix this?

Thanks


Comment by: Jan Karel Pieterse (8-3-2013 20:28:17) deeplink to this comment

Hi Sah,

I don't think so. The only way I think you can achieve this is by writing a macro that unprotects, adds a row and re-protects the sheet.


Comment by: Sah (8-3-2013 21:00:21) deeplink to this comment

Thanks Jan, for your response. I am a novice when it comes to writing VBA codes. Is there anywhere i can get the code for doing what you suggested? Any help will be appreciated. Thanks

Sah


Comment by: Jan Karel Pieterse (9-3-2013 13:56:01) deeplink to this comment

Hi Sah,

Something like this:

Sub Demo()
    ActiveSheet.Unprotect Password:="Foo"
    ActiveSheet.ListObjects(1).ListRows.Add
    ActiveSheet.Protect Password:="Foo"
End Sub


Comment by: Toon (11-3-2013 07:16:04) deeplink to this comment

Thanks for this really useful page about Excel tables in VBA. I would like to submit this piece of code that I wrote to return a value from a table by looking up a value. It uses the column headings instead of column numbers.
The code in is my next post...


Comment by: Toon (11-3-2013 07:16:18) deeplink to this comment


Option Explicit

Sub getRowByValue()
'vlookup functionality in a table by using column headings

    Dim rngColumn As Range
    Dim lo As ListObject
    Dim sh As Worksheet
    Dim strLookupValue As Integer
    Dim strColHeading As String
    Dim strReturnColHeading As String
    Dim strTableName As String
    Dim strFound As String

    strLookupValue = 12     'value to be searched for
    strColHeading = "test"    'column to be searched
    strReturnColHeading = "test2" 'column containing the return value
    strTableName = "Testtable" 'name of the table (set via Formulas - Name manager)

    On Error GoTo err_sheet_not_found
    Set sh = shTest     'VBA sheet name (set in VBA IDE)
    On Error GoTo err_table_not_found
    Set lo = sh.ListObjects(strTableName)
    On Error GoTo err_column_not_found
    Set rngColumn = lo.ListColumns(strColHeading).DataBodyRange
    On Error GoTo 0

    If WorksheetFunction.CountIf(rngColumn, strLookupValue) > 1 Then
        MsgBox "Duplicate values found: " & strLookupValue: End
    ElseIf WorksheetFunction.CountIf(rngColumn, strLookupValue) = 1 Then
        On Error GoTo err
        MsgBox lo.ListRows(WorksheetFunction.Match(strLookupValue, rngColumn)).Range.Cells(1, lo.ListColumns(strReturnColHeading).Index).Value
        On Error GoTo 0
    Else
        MsgBox "Value " & strLookupValue & " not found": GoTo normalend
    End If
    GoTo normalend

err_type_or_retcol:
    MsgBox "Value was found but had a different type (string as number or vice versa) or return column heading not found."

err_sheet_not_found:
    MsgBox "Sheet not found"
GoTo normalend

err_table_not_found:
    MsgBox "Table not found"
GoTo normalend

err_column_not_found:
    MsgBox "Column not found"
GoTo normalend

normalend:

End Sub


Comment by: Jan Karel Pieterse (11-3-2013 08:27:44) deeplink to this comment

Hi Toon,

Thanks. One little critique: your code fails if the table is not sorted on the column you are looking up in. An alternative method:

Sub getRowByValue()
'vlookup functionality in a table by using column headings

    Dim rngColumn As Range
    Dim lo As ListObject
    Dim strLookupValue As Integer
    Dim strColHeading As String
    Dim strReturnColHeading As String
    Dim strTableName As String
    Dim oFound As Range

    strLookupValue = 12     'value to be searched for
    strColHeading = "test"    'column to be searched
    strReturnColHeading = "test2" 'column containing the return value
    strTableName = "Testtable" 'name of the table (set via Formulas - Name manager)

    On Error GoTo err_table_not_found
    Set lo = shTest.ListObjects(strTableName)
    On Error GoTo err_column_not_found
    Set rngColumn = lo.ListColumns(strColHeading).DataBodyRange
    On Error GoTo 0

    If WorksheetFunction.CountIf(rngColumn, strLookupValue) > 1 Then
        MsgBox "Duplicate values found: " & strLookupValue: End
    Else
        On Error Resume Next
        Set oFound = rngColumn.Find(strLookupValue, rngColumn.Cells(1, 1), xlValues, xlWhole, xlRowThenColumn, xlNext, False)
        If Not oFound Is Nothing Then
            MsgBox lo.ListColumns(strReturnColHeading).Range.Cells(oFound.Row + lo.Range.Cells(1, 1).Row - 1, 1).Value
        Else
            MsgBox "Value " & strLookupValue & " not found": GoTo normalend
        End If
    End If
    GoTo normalend

err_type_or_retcol:
    MsgBox "Value was found but had a different type (string as number or vice versa) or return column heading not found."

err_sheet_not_found:
    MsgBox "Sheet not found"
    GoTo normalend

err_table_not_found:
    MsgBox "Table not found"
    GoTo normalend

err_column_not_found:
    MsgBox "Column not found"
    GoTo normalend

normalend:

End Sub


Comment by: yan (12-3-2013 11:55:46) deeplink to this comment

Hi,

Is there a vba code to insert 300 rows into the table at one go?

using the following VBA code takes eternity:


For i = 1 to 300    
Selection.ListObject.ListRows.Add AlwaysInsert:=True
next i


Thanks


Comment by: Jan Karel Pieterse (12-3-2013 13:00:29) deeplink to this comment

Hi Yan,

This makes use of the fact that anything you enter beneath the table expands the table:

    With ActiveSheet
        With .Range("A" & .Rows.Count).End(xlUp).Offset(1).Resize(300)
            .Value = 1
            .ClearContents
        End With
    End With


Comment by: Yan (13-3-2013 03:45:46) deeplink to this comment

That's very enlightening.

Thanks a lot! :)


Comment by: Mzweik (13-3-2013 17:41:09) deeplink to this comment

Hi there

Is there a way to create a table and call it up on a dashboard display?

Thanks


Comment by: Jan Karel Pieterse (14-3-2013 08:59:52) deeplink to this comment

Hi Mzweik,

I'm not sure what you need.
Perhaps you can ask your question at http://www.eileenslounge.com


Comment by: Bangu Numbi (25-3-2013 14:15:14) deeplink to this comment

Hi,

I have to transform daily the data from my company system to excel table for other use. But the data is dynamic. From a VBA, instead to change manually the last line and and last column, I need to transform the "select current region" to a table.

Resume : I need Macro to tranform "select current region" to a table instead the range done manually. Your help will be appreciated.


Bangu Numbi


Comment by: Jan Karel Pieterse (25-3-2013 20:37:55) deeplink to this comment

Hi Bangu,

You mean like this?

ActiveSheet.ListObjects.Add(xlSrcRange, Range("A1").CurrentRegion, , xlYes).Name = _
         "Table1"


Comment by: neil peters (25-3-2013 23:37:08) deeplink to this comment

Hi - hope you can help further....
Have tables in excel 2010 worksheets - basically one table in each of multiple sheets that all update as you add a row, delete a row via userform. Problem I have is I can specify what listrow I want to delete using listrow(number) but I want to do is search for a value (via Textbox1.value in userform) in 1st column of table then delete the listrow that this value (which would be unique) is in. Tried many variations but can't seem to do? Can you give any advise
My current code (which deletes listrow) is


Private Sub CommandButton1_Click()
    Dim wrksht As Worksheet
    Dim strLookupValue As String
    Dim lo As ListObject
    strLookupValue = TextBox1.Value
    For Each wrksht In Sheets
    For Each lo In wrksht.ListObjects
    lo.ListRows(strLookupValue).Delete
    On Error Resume Next
    Next lo
    Next wrksht
    MsgBox "Record Deleted", vbOKOnly, "Delete Record"
    Unload Me
End Sub


Comment by: Peter Hodges (26-3-2013 00:50:25) deeplink to this comment

I want to maintain a task list in Excel of all items in a directory (and SubDirectories). I would really only want the filename and path from the file list. In Excel I would add information about that file (like start date, end Date, Assigned To).
If I moved that file to a different directory or renamed the file, I would want the information I entered to be updated - but not loose my notes about the file!

I would expect there is a spreadsheet already designed to do something like this, but I just need to find it.


Comment by: glennpc@cox.net (26-3-2013 02:37:56) deeplink to this comment

I have a data entry worksheet in a workbook, and a master worksheet in another workbook. The both workbooks contain tables on those worksheets: a data entry table in one workbook and a master table in the other workbook. The structures of the tables are NOT identical-- they have 9 columns in common, but the master table has 2 additional columns. Both tables are set up with dropdown lists for data validation. The goal is to copy data from various submitted data entry workbooks to the master table in the master workbook.

Originally, these were not tables, they were named ranges. I would manually copy and paste the data entry range (maybe 5 or 6 rows) onto the worksheet in the master workbook, right next to the master data range (copying only VALUEs, not the formatting). Then I wrote VBA code that looped and selectively copied the data values from the data entry range to the bottom of the master beginning with the first blank row. It left the two extra columns blank, only copying the data they had in common into the correct columns. It worked fine.

I changed the ranges to tables to take advantage of all the benefits of tables, but my VBA code now does not copy anything when I run it. In a nutshell, what am I doing wrong? Because I'm using tables, do I need to reference the cells differently in my VBA code to make this work?

All the examples I see always have you copying the whole table to another worksheet, or to another workbook, and all the tables have identical columns. I've not seen anything that does what I'm trying to do (copying some cells in a row over, but leaving others blank). Do I need to abandon tables all together and go back to ranges? My spreadsheets will lose a lot of nice features if I do.

Any advice?


Comment by: Jan Karel Pieterse (26-3-2013 08:53:10) deeplink to this comment

Hi Neil,

You could use the Find method to locate the row in question:

Private Sub CommandButton1_Click()
    Dim wrksht As Worksheet
    Dim strLookupValue As String
    Dim lo As ListObject
    Dim oFound As Range
    strLookupValue = textbox1.Value
    
    For Each wrksht In Worksheets
        For Each lo In wrksht.ListObjects
            Set oFound = lo.ListColumns(1).Range.Find(strLookupValue, lo.Range.Cells(1, 1), xlValues, xlWhole, , xlNext, False)
            If Not oFound Is Nothing Then
                Intersect(lo.DataBodyRange, oFound.EntireRow).Delete
            End If
        Next lo
    Next wrksht
    MsgBox "Record Deleted", vbOKOnly, "Delete Record"
    Unload Me
End Sub


Comment by: Jan Karel Pieterse (26-3-2013 08:55:51) deeplink to this comment

Hi Glenn,

I don't think you have to abandon tables. But the addressing of an item in a table can be a bit challenging.

If you like you can send me your file by email so I can have a look.


Comment by: Neil Peters (27-3-2013 19:03:44) deeplink to this comment

Many thanks for the code Jan - it worked a treat!


Comment by: Sammy (27-3-2013 23:08:39) deeplink to this comment

Hi Jan,

Let me start by saying Thank You for this excellent post. Post like yours are a boon to novice VBA users like me.

I used a code from here to inser a row in my protected worksheet.
Private Sub CommandButton1_Click()
    ActiveSheet.Unprotect Password:="try"
    Selection.ListObject.ListRows.Add AlwaysInsert:=True
    ActiveSheet.Protect Password:="try", AllowFiltering:=True
End Sub

The problem is when the cursor is outside the table, it gives an error, moreover it removes the protection from file, leaving my data vulnerable. Can you help?

Also is there a way i can create a command button when clicked sorts a column in the protected table?

Thanks,

Sammy


Comment by: Jan Karel Pieterse (28-3-2013 07:26:44) deeplink to this comment

Hi Sammy,

You're welcome of course.

If you replace

Selection

with

Activesheet.ListObjects(1)

or with

Activesheet.ListObjects("TheListObjectsName")

(TheListObjectsName can be seen if you click in the table and then activate the table tab on the ribbon. Far left side of the ribbon shows the name)


Comment by: Sammy (28-3-2013 15:31:44) deeplink to this comment

Thanks, Jan. It works prefectly fine


Comment by: Rafael Stern (28-3-2013 22:13:15) deeplink to this comment

Hi Jan,

First of all thanks for the excelent article. I appreciate if we can communicate by my e-mail above, as its easier for me and I imagine for you as well.

I was wondering if you can help me with two things:

1- do you have a list of methods or functions that can be used along the listobjects?

2- I have one sheet with a few tables, one under the other. How can i delete all the empty rows in each of the tables? The criteria would be the first column empty on each of the tables and they are not necessarily sorted.

Thanks in advanced.


Comment by: Jan Karel Pieterse (29-3-2013 11:52:01) deeplink to this comment

No, I do not have such a list, but Excel VBA help should have one.

Deleting all data from all tables on the activesheet is as simple as:

Sub DeleteAllTableData()
    Dim oLo As ListObject
    For Each oLo In ActiveSheet.ListObjects
        If Not oLo.DataBodyRange Is Nothing Then
            oLo.DataBodyRange.Rows.Delete
        End If
    Next
End Sub


Comment by: Neil Peters (31-3-2013 12:42:14) deeplink to this comment

Hi (again) - I'm trying to amend the code you kindly gave me to delete a row based on a selection from an inputbox. What I'm now trying to do is with that code (this bit works) is load a userform with row details - but the bit I can't get to work is save new amended details from userform into all listobjects in worksheets. Tried various options but can only get to work on the one sheet or not at all. I know where it's going wrong but can't find a solution. Wondered if you could point me in the right direction (I have looked everywhere!) The code that works for one worksheet is


Private Sub CommandButton1_Click()
    Dim wrksht As Worksheet
    Dim lo As ListObject
    Dim FilterCriteria As String
    Dim oFound As Range
    
    FilterCriteria = Me.TextBox6.Value
    
    For Each wrksht In Sheets
    For Each lo In wrksht.ListObjects
    On Error GoTo ErrorHandler
        Set oFound = lo.ListColumns(1).Range.Find(FilterCriteria, lo.Range.Cells(1, 1), xlValues, xlWhole, , xlNext, False)
            If Not oFound Is Nothing Then
                Intersect(lo.DataBodyRange, oFound.EntireRow).Select
            End If

                    oFound.Cells(1, 1).Value = UserForm3.TextBox6.Value
                    oFound.Cells(1, 2).Value = UserForm3.TextBox1.Value 'These work

'    lo.ListRows(oFound).Range.Cells(1, 2).Value = TextBox1.Value (this option will select a row but not right one)
    
    Next lo
    Next wrksht
    MsgBox "One record amended"
    Unload Me
End Sub


What I need I think is how to reference the row I'm in from selection of search inputbox and change that row the selection is in.
Any advice you can suggest.
Realise it's holidays so not expecting you to reply yet! Thanks


Comment by: Bangu Numbi (1-4-2013 15:05:10) deeplink to this comment

Very thanks Peter. I am very sorry for delay feedback. It is working very well.

Regards,

Bangu Numbi


Comment by: Jan Karel Pieterse (2-4-2013 13:58:12) deeplink to this comment

Hi Neil,

Exactly what needs to happen with the found row?

Doesn't this work:


With Intersect(lo.DataBodyRange, oFound.EntireRow)
.Cells(1, 1).Value = UserForm3.TextBox6.Value
.Cells(1, 2).Value = UserForm3.TextBox1.Value
End With


Comment by: Neil Peters (2-4-2013 15:57:18) deeplink to this comment

Jan Karel - once again thank you!!! I'm still very much a VBA novice but your page and help has been invaluable! I had searched the internet and forums but there seems to be a lack of good listobject table vba information out there (except for yourself)! Cheers again Neil.


Comment by: Sjur Grønningsæter (5-4-2013 11:33:11) deeplink to this comment

Is it possible to group a table with VBA so that the group expands with the table when adding new rows? Or use VBA to collapse a table so that only headers are visible?
(the auto outline (grouping) feature does not work with tables)


Comment by: Jan Karel Pieterse (5-4-2013 16:44:41) deeplink to this comment

Hi Sjur,

Well, you could use simple code like this:

Sub HideTableBody()
    With ActiveSheet.ListObjects(1)
        If Not .DataBodyRange Is Nothing Then
            .DataBodyRange.EntireRow.Hidden = True
        End If
    End With
End Sub


Comment by: Sjur Grønningsæter (10-4-2013 08:52:40) deeplink to this comment

Thx Jan! It worked. Can I target specific tables? How do I reverse it?


Comment by: Jan Karel Pieterse (10-4-2013 09:59:34) deeplink to this comment

Hi Sjur,

Each table has a name which you can see in the table ribbon on the far left of the ribbon. You use that name -say it is Table1 in this case- like so:

ActiveSheet.ListObjects("Table1")


or if the worksheet as not the active sheet:

Worksheets("Sheet1").ListObjects("Table1")


Comment by: Jan Karel Pieterse (10-4-2013 10:00:25) deeplink to this comment

You can reverse it by using very similar code and replacing True with False


Comment by: Francois (12-4-2013 17:39:24) deeplink to this comment

Hi Jan,

My question is 2 fold,
1. i have a couple of tables sitting in multiple worksheets, each with a unique name lets say tableX, tableY and tableZ. I might not know in which sheet they are residing. is there a why to access the table directly instead of going through the sheets?

2. Assuming i know in which sheet i am working, my tables do not necessarily start in column A. but could for example start in column T. how do i retrieve the relative position of the column in the table. eg 3 columns, first start in T, if i look for the second column it should return 2, instead of 21.

thank in advance.


Comment by: Jan Karel Pieterse (15-4-2013 11:29:45) deeplink to this comment

Hi Francois,

Yes, you can access a table by its name even though it is on a different sheet like this:

Dim oLo As ListObject
Set oLo = Range("TableName").ListObject


Comment by: Marcia SAcharny (19-4-2013 14:09:36) deeplink to this comment

I am new at this, so forgive me if I am totally off track.

I tried to copy your codes to apply your macros to my spreadsheet. But some do nothing and on others I get errors.
So do you have a sample worksheet with the macros in so I can F8 and see how it works?

Thanks!

Marcia


Comment by: Jan Karel Pieterse (20-4-2013 21:13:36) deeplink to this comment

Hi Marcia,

Which routines are not working for you?


Comment by: David V. (29-4-2013 12:52:21) deeplink to this comment

Hello,

Is it possible to work with a table in another excel file that is closed or do I need to open it first using this method?

Tkank you,
David


Comment by: Jan Karel Pieterse (29-4-2013 14:10:38) deeplink to this comment

Hi David,

Whether or not you can work with a table in a closed file depends on how you are trying to access the data in the table. Can you explain a bit more?


Comment by: David V. (30-4-2013 09:02:29) deeplink to this comment

Hello

Im starting a larger project, which involves a lot of workbooks and tables. But i will try to explain it in a simplified version:
I have a main workbook where the vba code will be
and i have another workbook with a table1, this table will be constantly used by another person who will be putting data in it.
I need to access this table1 from the main workbook and pull some specific data from it into the main workbook.

So is there a way to do this? to refer to a table in another workbook which is not opened on my pc preferably.

Thank you,
David


Comment by: Jan Karel Pieterse (1-5-2013 13:38:57) deeplink to this comment

Hi David,

I think your best bet is to create a range name that spans the entire table and then use Data, From other sources, From microsoft query to pull that named range into your working file.

Then either instruct the other user to save regularly, or write a macro that does it for him. ANd refresh the connection to the other workbook in your own regularly too.


Comment by: David V. (2-5-2013 09:45:22) deeplink to this comment

I was considering that option too, so ADODB it is.
The auto save is already done with a macro.

Thanku you very much Jan


Comment by: Roy (6-5-2013 07:14:50) deeplink to this comment

Hello,

I have created vba code to copy some worksheets from workbook 1 to workbook 2.

However, workbook 1 has a table defined with a name. When I copy the worksheets, everything is copied fine except the definition of the table which is referenced in many of the formulas that are copied.

Could you assist me with the vba code to detect table definitions in workbook 1 (if they exist) and re-create them in workbook 2?

I would prefer it to work without hard coding table names or how many tables to copy, so that the code would always work for any number of tables from zero to n.

Thanks for any help...


Comment by: Jan Karel Pieterse (6-5-2013 09:59:41) deeplink to this comment

Hi Roy,

It is realtively easy to redefine all tables, using code like this:

Sub CopyTableDefs(oSource As Worksheet, oTarget As Worksheet)
    Dim oLo As ListObject
    Dim oLo2 As ListObject
    For Each oLo In oSource.ListObjects
        Set oLo2 = oTarget.ListObjects.Add(xlSrcRange, oTarget.Range(oLo.Range.Address), , xlYes)
        oLo2.Name = oLo.Name
    Next
End Sub


So if you copied data from Sheet1 to Sheet2, you call this sub like this:

CopyTableDefs worksheets("Sheet1"),worksheets("Sheet2")


Comment by: David V. (13-5-2013 16:46:53) deeplink to this comment

Hello Jan,

I have used the ADODB and it works very well, but now Im back to the tables in office 2007.

I have a table that is connected to a access DB and Im trying to copy the values of that table into another sheet but im only getting blanks

I have tried:
ActiveSheet.Range("Table_summar.accdb").Select
Selection.Copy

or using cells

Dim sSHT As Worksheet
Set sSHT = ActiveWorkbook.Worksheets("data")
sSHT.Cells(ifor , 1).Value
'using this with a for to next cycle

or using range

Range("A2:D30").Select
Selection.Copy

but nothin works


Comment by: Jan Karel Pieterse (14-5-2013 10:34:08) deeplink to this comment

Hi David,

After issuing Copy, of course you also have to paste somewhere. The code you posted does not include any paste command.


Comment by: David V. (14-5-2013 12:50:39) deeplink to this comment

for the first one and the third one I have tried a pastespecial-values only:

Sheets("Cover").Select
Range("B10:D21").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
        :=False, Transpose:=False


for the second one i used the same for to next cycle as for getting the data:
all variables are string except the ifor which is integer

For ifor = 2 To 12
Set sSHT = ActiveWorkbook.Worksheets("data")
slinka = sSHT.Cells(ifor, 50).Value
smod = sSHT.Cells(ifor, 51).Value
ssn = sSHT.Cells(ifor, 52).Value
Set sSHT = ActiveWorkbook.Worksheets("Cover")
sSHT.Cells(9 + ifor, 2).Value = slinka
sSHT.Cells(9 + ifor, 3).Value = smod
sSHT.Cells(9 + ifor, 4).Value = ssn
Next


Comment by: Jan Karel Pieterse (14-5-2013 16:05:05) deeplink to this comment

Hi David,

To copy the data from sheet Sheet1 to sheet Sheet2, you could use:

Worksheets("Sheet1").ListObjects(1).Range.Copy
Worksheets("Sheet2").Range("A1").PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False


Comment by: David V. (16-5-2013 09:35:42) deeplink to this comment

Still not working only the headers were pasted not the data.

Worksheets("data").ListObjects(1).Range.Copy
    Worksheets("Cover").Range("B10").PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False


Comment by: Jan Karel Pieterse (16-5-2013 10:10:00) deeplink to this comment

Hi David,

Can you perhaps send your workbook by email? The code worked just fine in my testing.


Comment by: Juan Gallego (27-5-2013 22:14:54) deeplink to this comment

I've got a table (not a sheet) named "TDatos", im trying to execute an SQL
code goes like this:

    rs.Open "SELECT distinct [Ciudad], Sum([Ventas]) as Total FROM [TDatos] WHERE [Ventas]>0 GROUP BY [Ciudad] ", Conexión, , , adCmdText

i've already done the right connection with excel: Connection.open..., but the Sql stops saying that couldent find the object "TDatos"
Thanks


Comment by: Jan Karel Pieterse (28-5-2013 08:39:54) deeplink to this comment

Hi Juan,

Queries do not work with Table names directly. However, you can define a range name that simply points to the entire table and use that range name in the query.


Comment by: David V. (31-5-2013 09:16:44) deeplink to this comment

Hello Jan,

I havent had time for this in the last two weeks, but today i finaly got to it and i have created a copy of the whole thing and started deleting stuff until i got to the line
Worksheets("data").Range("A2:BB50000").ClearContents
my table was somewhere in this range and it was deleted or somethin. and this is why it was not working. but im not sure why, because i have used a refresh for the table right before i have copied it.
Worksheets("data").ListObjects(1).QueryTable.Refresh BackgroundQuery:=True

anyways this Worksheets("data").ListObjects(1).Range.Copy
is not working properly it copies the headers too and i dont want that.
So i have used:
Worksheets("data").Range("Table_summar.accdb").Copy
instead of that.

And finaly everything is working :)

Thak you for your support
bb


Comment by: Jan Karel Pieterse (31-5-2013 12:01:56) deeplink to this comment

Hi David,

You could probably also have used the DataBodyRange property instead of the Range property


Comment by: Tony Lawson (1-6-2013 16:55:59) deeplink to this comment

Hi
I am trying to use your suggestion
"If you need to do something with a newly inserted row, you can set an object variable to the new row"
I would like to do this without the need to select the Table Row. Can you please look at my code below and suggest a way of doing this.


Dim MySheet As Worksheet 'MY WORKSHEET
Dim MyNewRow As ListRow 'NEW ROW IN TABLE

    Set MySheet = Sheet1 'SET MY WORKSHEET
        Set MyNewRow = Selection.ListObject.ListRows.Add 'FAILS HERE

            With MySheet.ListObjects("MY_TABLE_NAME_HERE")
                    .ListRows.Add 'ADD ANOTHER ROW TO THE TABLE

            End With

MyNewRow.Range.Cells(1, 1).Value = "Value For New cell"



Many thanks


Comment by: Tony Lawson (1-6-2013 17:39:53) deeplink to this comment

Hi
I don't wish to waste your time so I wanted to let you know I have a solution to my previous request.


Dim MySheet As Worksheet 'MY WORKSHEET

    Set MySheet = Sheet1 'SET MY WORKSHEET

            With MySheet.ListObjects("MY_TABLE_NAME_HERE") 'MY TABLE
                
                With .ListRows.Add 'ADD NEW ROW TO TABLE
                    .Range(1) = "Value_for_New_Cell" 'FIRST CELL OF NEW ROW
                    .Range(2) = "Value_for_next_New_Cell" 'SECOND CELL OF NEW ROW

                End With

            End With


Comment by: Jan Karel Pieterse (2-6-2013 20:31:24) deeplink to this comment

HI Tony,

Great stuff, I just wait for a bit and people answer their own questions. :-)

Thanks for posting your solution.


Comment by: Varsha (5-6-2013 07:46:05) deeplink to this comment

I want to run the sql query on excel table. Please help.


Comment by: Jan Karel Pieterse (5-6-2013 08:04:12) deeplink to this comment

Hi Varsha,

Unfortunately, MSQuery (nor ODBC) does not recognize Excel 2007-2013 tables natively.

Your best alternative is to name the range the table spans using Formulas, Define name.


Comment by: ganesan (12-7-2013 13:05:46) deeplink to this comment

dear sir,

how to excel vba protect sheet with password. if password is disable then automatical delete protect sheet.

please help.

regards,
Ganesan


Comment by: Jan Karel Pieterse (13-7-2013 19:09:28) deeplink to this comment

Hi ganesan,

I kindly ask you to go here to get an answer to your questions:
www.eileenslounge.com


Comment by: Anvidc (16-8-2013 10:52:15) deeplink to this comment

Hi,
There is another easy way to get data from table:

a = [TableName[ColumnsName]].Cells(NoRowInSideTable)

To Update Table Data:

[TableName[ColumnsName]].Cells(NoRowInSideTable) = YourValue




Comment by: Jan Karel Pieterse (16-8-2013 15:39:53) deeplink to this comment

Hi Anvidc,

Thanks! I am no fan of using the [] syntaxis, as it requires VBA to evaluate the statement before it returns the object. Direct object name referencing as in

Worksheets("Sheet1").ListObjects("ListObjectName").ListColumns("FieldName")


is faster.


Comment by: Dan (21-8-2013 16:23:34) deeplink to this comment

Thanks for this, just saved me a lot of hassle!


Comment by: Yulia (30-8-2013 17:22:10) deeplink to this comment

Please help! How to I auto-refresh an Excel auto-filter when data is changed?

I have my table in "monthly data" tab, sheet1 tab is an autofilter based on criteria. I change the value of one cell in "monthly data" tab and want to see the auto update in sheet1.

I have a recurrent process, where a lot of data would change in the "monthly data" tab, so this auto-refresh would help greatly!


Comment by: Jan Karel Pieterse (1-9-2013 20:15:11) deeplink to this comment

Hi Yulia,

One way would be by right-clicking the sheet tab and selecting View code. Then paste this code:

Private Sub Worksheet_Activate()
    Me.ListObjects(1).AutoFilter.ApplyFilter
End Sub

Make sure you save the file as an Excel workbook with macro's.


Comment by: Yulia (3-9-2013 19:08:11) deeplink to this comment

Hi Jan Karel,
Thank you for your reply.
I pasted the code, then selected "Close and Return to Excel", saved file as .xlsm, re-opened it and nothing changed. Is there something I need to do to "run" it?
I sent you an invite via LinkedIn, maybe we can talk there.


Comment by: Jan Karel Pieterse (4-9-2013 20:13:21) deeplink to this comment

Hi Yulia,

The code only runs when you activate the sheet in question. So go to a different worksheet and then acativate the one with the code again.


Comment by: Rajiv (30-9-2013 10:03:45) deeplink to this comment

It is very good detail


thanks alot


Comment by: Rick (8-11-2013 11:03:39) deeplink to this comment

ooking for a bit of help please?

I have a spreadsheet that basically allows users to input a number of data fields into it, names, numbers etc. It runs on a dynamic list object table that automatically populates todays date into Column A, and then allows the user to input 7 other cells B-J. It then runs through a series of calculations to check for duplicate values throughout the spreadsheet and formats the cells accordingly using colour. It also adds three other cells to RAG the entry to inform the user if they need to double check the entry or not. It performs the calculations through the worksheet change events.

The worksheet i set to autofilter to the last 10days worth of entries.

I have one major issue in that if the user deletes the prepopulated date in column A then the code bugs out, and hides the row. Assuming because the autofilter fails?

I have done some reading to see about protecting the column / cells but it doesn't seem that straight forward and im going round in circles...

I guess i'm left with two options unless someone can come up with something to help, educate users (not easy!) or look at protecting the cells somehow, using some sort of data validation / object box for data entry? Any help / suggestions would be greatly appreciated? The nature of the workbook doesn't allow me to upload it, but i have attached the code but changed the named ranges etc... please go easy, i'm a VBA newbie ;-)

Thanks - code in the next post because its too long ;-/


Comment by: balwant randev (10-11-2013 13:19:48) deeplink to this comment

I want to change the value in a cell if the cell value is 9.I am increasing it to 10 by adding a 0 on the fourth place using vba. the code I am trying is listed below but it gives an error. please help
ActiveCell.Offset(0, 0) = "SUBSTITUTE(ActiveCell.Offset(0, 0),LEFT(ActiveCell.Offset(0, 0),3),LEFT(ActiveCell.Offset(0, 0),3), & "0",1))


Comment by: Jan Karel Pieterse (11-11-2013 07:18:51) deeplink to this comment

Hi balwant,

If I read your question properly, this should do the same as your code:

ActiveCell.Value=Replace(ActiveCell.Value,LEFT(ActiveCell.Value,3),LEFT(ActiveCell.Value,3) & "0", 1))



Comment by: balwant randev (12-11-2013 06:42:17) deeplink to this comment

thanks I will try the same


Comment by: balwant (14-11-2013 13:29:05) deeplink to this comment

Hi Jan
I tried the code
ActiveCell.Value=Replace(ActiveCell.Value,LEFT(ActiveCell.Value,3),LEFT(ActiveCell.Value,3) & "0", 1))
but I still get syntax error end of statement expected.
I have column which has 9 or 10 characters. I am trying to convert the 9 characters to 10 characters by adding a digit in the fourth place.
many thanks in anticipation


Comment by: Jan Karel Pieterse (14-11-2013 15:09:25) deeplink to this comment

Hi Balwant,

Sorry, I did not test my code. I expect removing the

, 1

at the end fixes it.


Comment by: balwant (15-11-2013 14:44:22) deeplink to this comment

Thanks Jan
I removed ,1) and it works
I will use it with activecell.offset(1,0).select
to make the rest of the column work.
thank you again
balwant


Comment by: Brent (18-11-2013 19:14:04) deeplink to this comment

The line

oNewRow .Range.Cells(1,1).Value="Value For New cell"

should not have a space after oNewRow.


Comment by: Jan Karel Pieterse (19-11-2013 07:10:25) deeplink to this comment

Hi Brent,

Thanks!


Comment by: andy (21-1-2014 21:17:43) deeplink to this comment

Hi Jan,
after deleting row in ListObject that contains only one row (the only remaining row),using the code line like:

oListObject.ListRows(iRowNumber).Delete

it deletes only data, but row still figures (List must contain at least one row); it's OK, but then                     ListObject.DataBodyRange lost reference.
In moment when empty ListObject is created, situation seems the same (there is one empty row), but ListObject.DataBodyRange can be referenced.
What's wrong?
Best Regards
Andy


Comment by: Jan Karel Pieterse (22-1-2014 08:45:28) deeplink to this comment

Hi Andy,

I just tried with Excel 2010 and whether he listbox is newly created (with only a header row), or all rows have been deleted makes no difference. In both cases DataBodyRange errors out.
Best to test for Listbox.ListRows.Count > 0 before addressing the DataBodyRange.


Comment by: Brady (6-2-2014 18:21:40) deeplink to this comment

Is there any way to hide a table row without hiding a worksheet row?


Comment by: Jan Karel Pieterse (6-2-2014 19:27:51) deeplink to this comment

Hi Brady,

No I don't think you can do that.


Comment by: edrees yousif (8-2-2014 17:56:54) deeplink to this comment

thanks alot .
these information more important to me


Comment by: Eddie (12-2-2014 12:21:22) deeplink to this comment

Jan, Great article thanks.
I'm constrained to working in Excel 2003. I have a listobject that I write to an array. The data is updated and then I write it back to the list object. Net result is the the number of rows may grow.

'write listobject data + formulas to array
arrCollatedInitiatives = ActiveWorkbook.Sheets("Initiatives").ListObjects("tblInitiatives").DataBodyRange.Formula

'do stuff

'write array back to listobject
Range("tblInitiatives").Resize(UBound(arrCollatedInitiatives, 1), UBound(arrCollatedInitiatives, 2)).Value = arrCollatedInitiatives

This works in Excel 2013 where the range name is the same as the listobject... but it does not work in Excel 2003

If this I need something like...

ActiveWorkbook.Sheets("Initiatives").ListObjects("tblInitiatives").Resize Range("tblInitiatives").Resize(UBound(arrCollatedInitiatives, 1), UBound(arrCollatedInitiatives, 2)).Value = arrCollatedInitiatives

but I can't make it work! Help!


Comment by: Jan Karel Pieterse (12-2-2014 14:42:42) deeplink to this comment

Hi Eddie,

Since your reading the databodyrange, you should be writing back to it as well:


ActiveWorkbook.Sheets("Initiatives").ListObjects("tblInitiatives").DataBodyRange.Resize(UBound(arrCollatedInitiatives, 1), UBound(arrCollatedInitiatives, 2)).Value = arrCollatedInitiatives


Comment by: Eddie (12-2-2014 18:39:58) deeplink to this comment

Legend! Thanks - just couldn't get the syntax right.


Comment by: Christi (14-2-2014 22:10:12) deeplink to this comment

Thanks for the great write-up. I've been doing a lot of VBA work with tables and this has been extremely helpful. Is there a way to pull the name of a selected table column? For example, if I've selected a cell in column I'd love to get back the address of the cell using the column name (i.e. @City) instead of "A12".

Thanks!


Comment by: Jan Karel Pieterse (17-2-2014 06:18:08) deeplink to this comment

Hi Christi,

This should do:

Intersect(Activecell.entirecolumn,Activecell.ListObject.HeaderRowRange).Value


Comment by: John Mamuscia (28-2-2014 13:45:17) deeplink to this comment

How would I delete columns from a table (ListObject) in Excel 2010?


Comment by: Jan Karel Pieterse (28-2-2014 14:07:11) deeplink to this comment

Hi John,

I'd say:

ActiveSheet.ListObjects("Table1").ListColumns("Column1").Delete


Comment by: John Mamuscia (28-2-2014 18:21:54) deeplink to this comment

Thanks....that works!


Comment by: mike fleuette (10-3-2014 22:38:32) deeplink to this comment

I think what I am trying to do is very close to what you have here - but I am just not able to make it work.

In Excel 2010, I have a listobject ("Table_QUery_from_xxx") populated by a query against a SQL server database; it returns 50+ columns.

What I am looking for is:
a) how to identify which columns in the table have an AutoFilter applied, and
b) how to color the column heading (or the cell above it)

If you also have a way to populate the filter criteria to the cell above the column header, that would be great, but just highlighting the header is good enough for now.

I have seen many examples of how to do this with standard Excel, but not for ListObjects...

Any help is greatly appreciated!

Thank you -


Comment by: Jan Karel Pieterse (11-3-2014 07:07:59) deeplink to this comment

Hi Mike,

Indeed, one of the examples is this one:
http://www.ozgrid.com/VBA/autofilter-criteria.htm

I modified it a bit to:

Option Explicit

Function AutoFilter_Criteria(Header As Range) As String
    Dim strCri1 As String, strCri2 As String
    Application.Volatile
    With Header.ListObject.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.Cells(1, 1)) & ": " & strCri1 & strCri2
End Function

Sub ShowAutofilters()
    Dim oCell As Range
    Dim sCrit As String
    For Each oCell In ActiveSheet.ListObjects(1).HeaderRowRange
        sCrit = AutoFilter_Criteria(oCell)
        oCell.Offset(-1).Value = sCrit
    Next
End Sub
    


Comment by: mike fleuette (11-3-2014 22:19:26) deeplink to this comment

Thank you so much - that is exactly the information I was seeking!

Quick question - how does VBA account for multiple criteria? In Excel 2007+, you can have multiple filter criteria (e.g. =A, B, or C)

Is there a trick to capturing that criteria? I notice that it pops-up when I hover over an autofilter arrow on the header line...)

Again, thank you!


Comment by: Maree (12-3-2014 08:39:07) deeplink to this comment

Hi Jan

This would seem a very general question but what would be most likely cause of a macro written in Excel 2003 using the List functionality not running over in Excel 2013 if left unmodified?


Comment by: Jan Karel Pieterse (12-3-2014 09:32:51) deeplink to this comment

Hi Mike,

The code I gave is basically for Excel 2003. Excel 2007 and up have more elaborate filter capabilities, the code would need a rewrite to cater for those.


Comment by: Jan Karel Pieterse (12-3-2014 09:33:41) deeplink to this comment

Hi Maree,

Without seeing the code that is impossible to answer I'm afraid.


Comment by: mike fleuette (12-3-2014 14:31:21) deeplink to this comment

Jan - thank you for your help. Is there a good online reference for ListObject(s) and its attributes and methods? Specifically, I am looking for one that describes how multiple criteria are stored and what flags/attributes indicate what about the criteria...

Again, thank you -


Comment by: Jan Karel Pieterse (12-3-2014 17:25:25) deeplink to this comment

Hi Mike,

I wouldn't really know. But the macro recorder does help when you're trying to figure out stuff like this :-)


Comment by: Maree (13-3-2014 00:43:04) deeplink to this comment

Hi Jan

What this is doing is that it is copying rows from a Pivot Table into a table (ListObject). This is dynamic so the number of rows can vary (the columns do not). There is another procedure which will clear out any surplus lines. I can't quite see where this is going wrong.

Private Function Macro _Try_To_Match_A_Row_In_Destn_Table()
    
    activeProcedureName = " Macro _Try_To_Match_A_Row_In_Destn_Table "
    
    On Error Resume Next
    
    'note that there appears to be a functionality difference in lists between Excel 2003 and 2007, in that the range in 2003 includes the insert row whereas presumably in 2007 it does not.
    matchResult = Application.Evaluate("MATCH(" & subProjectDescription & "," & destnListObject.ListColumns(4).Range.Address(, , , True) & ", 0)")
        
    If IsError(matchResult) Then
            On Error GoTo 0
             aRowMatchWasFoundForSubProjectDescription = False
        
        Call Macro_Insert_Data_From_Source_Row_At_End_Of_DestnTable()
    Else
        On Error GoTo 0
        aRowMatchWasFoundForSubProjectDescription = True
        
    End If
End Function


Comment by: Maree (13-3-2014 00:43:33) deeplink to this comment

Hi Jan

Part 2 of 2 (continued from above)

Private Function Macro_Insert_Data_From_Source_Row_At_End_Of_DestnTable()
    
    activeProcedureName = " Macro_Insert_Data_From_Source_Row_At_End_Of_DestnTable()"
                
    destnListObject.Parent.Parent.Activate
    destnListObject.Parent.Activate
    destnListObject.Range.Activate
        
    matchResult = destnListObject.InsertRowRange.Row - destnListObject.Range.Row + 1
    
    
    destnListObjectRowCountB4InsertingARow = destnListObject.ListRows.Count
    If destnListObjectRowCountB4InsertingARow = 0 Then
        GoTo ThereIsNotADummyRowAlreadyAtTheEndOfTheTable
    ElseIf IsError(destnListObject.ListRows(destnListObjectRowCountB4InsertingARow).Range.Cells(1).Value) Then
        GoTo ThereIsNotADummyRowAlreadyAtTheEndOfTheTable
    ElseIf destnListObject.ListRows(destnListObjectRowCountB4InsertingARow).Range.Cells(1).Value <> "dummy value" Then
        GoTo ThereIsNotADummyRowAlreadyAtTheEndOfTheTable
    End If
    
    If False Then
ThereIsNotADummyRowAlreadyAtTheEndOfTheTable:
        Set destnCell = Nothing: Set destnCell = destnListObject.InsertRowRange.Cells(1)
                destnCell.Value = "dummy value"
        If destnListObject.ListRows.Count < destnListObjectRowCountB4InsertingARow + 1 Then
            MsgBox "There was a problem adding a row to the destination table.", vbOKOnly
            Call Macro_Stop
        End If
    End If
    
    If IsEmpty(aRowMatchWasFoundForSubProjectDescription) Then
        aRowMatchWasFoundForSubProjectDescription = False
    End If
    
    Exit Function

End Function


Comment by: Jan Karel Pieterse (13-3-2014 06:52:32) deeplink to this comment

Hi Maree,

For Excel 2007 and up there is no such thing as an InsertRowRange for a listobject, so you have to act accordingly:

Private Function Macro_Insert_Data_From_Source_Row_At_End_Of_DestnTable()
    Dim bNoDummyRow As Boolean
    activeProcedureName = " Macro_Insert_Data_From_Source_Row_At_End_Of_DestnTable()"
    Set destnListObject = ActiveSheet.ListObjects(1)
    destnListObject.Parent.Parent.Activate
    destnListObject.Parent.Activate
    destnListObject.Range.Activate
    If Val(Application.Version) = 11 Then
        matchResult = destnListObject.InsertRowRange.Row - destnListObject.Range.Row + 1
    Else
        matchResult = destnListObject.Range.Cells(destnListObject.Range.Rows.Count, 1).Offset(1).Row
    End If


    destnListObjectRowCountB4InsertingARow = destnListObject.ListRows.Count
    If destnListObjectRowCountB4InsertingARow = 0 Then
        bNoDummyRow = True
    ElseIf IsError(destnListObject.ListRows(destnListObjectRowCountB4InsertingARow).Range.Cells(1).Value) Then
        bNoDummyRow = True
    ElseIf destnListObject.ListRows(destnListObjectRowCountB4InsertingARow).Range.Cells(1).Value <> "dummy value" Then
        bNoDummyRow = True
    End If

    If bNoDummyRow Then
        Set destnCell = Nothing
        If Val(Application.Version) = 11 Then
            Set destnCell = destnListObject.InsertRowRange.Cells(1)
        Else
            Set destnCell = destnListObject.Range.Cells(destnListObject.Range.Rows.Count, 1).Offset(1)
        End If
        destnCell.Value = "dummy value"
        If destnListObject.ListRows.Count < destnListObjectRowCountB4InsertingARow + 1 Then
            MsgBox "There was a problem adding a row to the destination table.", vbOKOnly
            'Call Macro_Stop
        End If
    End If

    If IsEmpty(aRowMatchWasFoundForSubProjectDescription) Then
        aRowMatchWasFoundForSubProjectDescription = False
    End If
End Function


Comment by: Maree (14-3-2014 04:35:09) deeplink to this comment

Hi Jan
Thank you for your response. I am trying to read a table range of cells from one table to another. Transitioning over from Excel 2003 to 2007 there is an error however which prevents this from happening for me.

preexistWksh.Evaluate("OFFSET(" & ActiveSheet.Evaluate("TopLeftCornerOfTheListOfReportData").Address(referenceStyle:=IIf(ReferenceStyleA1, xlA1, xlR1C1)) & ",1,0,COUNTA(" _
                & ActiveSheet.Evaluate("TopLeftCornerOfTheListOfReportData").Address(referenceStyle:=IIf(ReferenceStyleA1, xlA1, xlR1C1)) & ":INDEX(" _
                & nameInReportOfResultsOutputSheet & "!" _
                & RangeReference _
                & ",65536,1))-1,MATCH(""" & cHeadernameOfColumnOnSourceAndDestSheetsToUpdateWithDateTimeRowWasLastProcessed _
                & """," & ActiveSheet.Evaluate("TopLeftCornerOfTheListOfReportData").Address(referenceStyle:=IIf(ReferenceStyleA1, xlA1, xlR1C1)) & ":INDEX(" _
                & nameInReportOfResultsOutputSheet & "!" _
                & RangeReference _
                & ",ROW(TopLeftCornerOfTheListOfReportData),256),0))" _
            ).Select
            
            If Err.Number <> 0 Then
                MsgBox "Could not identify the range of cells containing data on worksheet '" & nameInReportOfResultsOutputSheet & "' in the workbook which contains the previous report..", vbCritical
                Call Macro_Stop
            End If


Comment by: Jan Karel Pieterse (14-3-2014 11:59:33) deeplink to this comment

Hi Maree,

Rather than trying to figure out what your macro is doing: can you perhaps decribe what it is supposed to do?


Comment by: Maree (17-3-2014 01:01:35) deeplink to this comment

Hi Jan

The macro is updating a report between Month A and Month B.

Some code is already in place to populate Month B and this code here is to select the full range from the report of Month A. You will see that this is an offset (sans one row, the title row) formula that should be capturing all rows and all applicable columns.

When this range is selected, Month B's report is updated with certain fields from the report of Month A (more code). I can't get to this point however.


Comment by: Jan Karel Pieterse (17-3-2014 06:48:10) deeplink to this comment

Hi Maree,

If the range you are about to copy is a listobject, the code can be greatly simplified I expect. To select just the data of a listobject all you need is:

Worksheets("TheSheetToCopy").ListObject("ListObjectName").DataBodyRange


If it isn't a listobject, it depends whether there is more on the sheet. If all information is needed, you simply use:

With Worksheets("TheSheetToCopy")
    .UsedRange.Offset(1).Resize(.usedrange.rows.count-1)
End With


That's it.


Comment by: John Mamuscia (24-3-2014 23:41:09) deeplink to this comment

JKP, thanks for the tutorial on using tables. I am using Excel 2010 and have a table defined. I then apply an autofilter to it and now want to loop through the visible rows and utilize the column names to get their values. I have been unable to find anything in web searches and was hoping you could explain how to do this. Thus far in my code I am able to get the autofilter to work and get the number of rows left visible, but how do I then loop through those visible rows and use the column names that are defined in the table? Thanks for any leads or ideas.


Comment by: Jan Karel Pieterse (25-3-2014 07:09:55) deeplink to this comment

Hi John,

You can use the specialcells method of the range object:

    Dim oCell As Range
    For Each oCell In ActiveSheet.ListObjects(1).ListColumns(1).DataBodyRange.SpecialCells(xlCellTypeVisible)
        MsgBox Intersect(oCell.EntireRow, ActiveSheet.ListObjects(1).ListColumns("a").DataBodyRange).Value
    Next


Comment by: John Mamuscia (25-3-2014 15:55:58) deeplink to this comment

Thank you, that works!


Comment by: Scott Marshburn (30-3-2014 23:24:13) deeplink to this comment

How would one go about finding the next empty row in a table in excel 2013


Comment by: Jan Karel Pieterse (31-3-2014 06:56:46) deeplink to this comment

Hi Scott,

This is irrespective whether you are in a true table or on a normal range. Suppose you have a variable oCell pointing to a cell. This line of code pust the next empty cell (downwards) into that variable:

If Not IsEmpty(oCell.Offset(1).Value) Then
Set oCell = oCell.End(xlDown).Offset(1)
Else
Set oCell = oCell.Offset(1)
End If


Comment by: Zack Barresse (21-4-2014 20:51:49) deeplink to this comment

Remember, Jan Karel's code will bring you to either the next empty cell, or the last cell of the table, or the total row if it is showing. If you're setting a Range object, I'd recommend setting the ShowTotals property to False, then adding your rows, then turning it back on. Otherwise you're just changing the total cell.


Comment by: Jason Gross (24-4-2014 16:09:43) deeplink to this comment

How can we select a single cell, or get the value. This does not seem to work.
Range("Table1[[5],[Column2]]").Select
Range("Table1[[5],[Column2]]").Value
Thanks,
Jason


Comment by: Jan Karel Pieterse (28-4-2014 10:10:42) deeplink to this comment

Hi Jason,

Like so:

ActiveSheet.ListObjects("Table1").ListColumns("Column1").databodyrange.Rows(5).Value


Comment by: Zack Barresse (28-4-2014 20:20:35) deeplink to this comment

You can get it many ways. Most common is the DataBodyRange object, which you can use like the Cells object, as it returns a Range object. An example of this is something like...

    ActiveSheet.ListObjects("Table1").DataBodyRange(1, 1)

You won't get intellisense from the above though, just like the Cells object.

As far as your specific request, Jason, there's really no need to select anything. If you want to work with an entire column, you can use the ListColumns object. This (as well as the ListRows object) is a little different from the DataBodyRange, in that it doesn't return a Range object. To access that you must specify it. Also note this method will include the header and total rows if they're showing. Here is an example...

    ActiveSheet.ListObjects(1).Range.Select

If you *only* want the body, specify it like so...

    ActiveSheet.ListObjects(1).DataBodyRange.Select

These examples are selecting the ranges, but like I said, you don't need to select it to work with it.


Comment by: Jon DeBas (11-5-2014 06:09:29) deeplink to this comment

Hi Jan,
A great article.
Simple question which nobody seems to be addressing. I'm selecting only some rows in a Table trying to sort only these rows (e.g. rows 11 thru 75 in a 200 row table), but when I hit Sort Excel always overrides my selection by selecting all the rows in the table instead. The same thing happens in VBA. I'm using Excel 2007.
Am I missing something
Thanks,
Jon


Comment by: Jan Karel Pieterse (11-5-2014 20:26:06) deeplink to this comment

Hi Jon,

I don't think you can sort only part of a table. Perhaps an easy workaround is to copy those rows elsewhere, sort them and then copy them back into the table.


Comment by: PONS Patrice (12-5-2014 12:15:52) deeplink to this comment

Hi Jan
Sorry for my english.

I have a table with 100000 rows and i wanna delete many rows with criteria about a date in column 3.
That table is a MSQUERY extract and format date is AAAAMMJJ (ex 20140423).
I just wanna have datas from now to 90 days past.
Very difficult challenge for me.
I can apply filters manually but can't in vba.
Thanks for your support


Comment by: Jan Karel Pieterse (12-5-2014 13:58:56) deeplink to this comment

Hi Patrice,

Your English is fine :-)

Can't you apply a filter to the query, instead of doing this afterwards in Excel? That would make more sense to me.


Comment by: PONS Patrice (12-5-2014 15:28:09) deeplink to this comment

Hi Jan

Thank you for your answer
I try to filter my query in vba and it works.
The problem was date format.

Best Regards :)


Comment by: Rudi (5-6-2014 09:25:05) deeplink to this comment

Hi Jan Karel,

TX for an awesome website. I use the info on your site quite frequently for reference purposes. I have one question. In this article about using the ListObject, you have some great references to selecting parts of a table. Is there a chance that you can include a reference to selecting the last row and the last column of a ListObject too.

Above you have this...

With oSh.ListObjects("Table1")
     'Select just row 4 (header row doesn't count!)
     .ListRows(4).Range.Select
End With


I tried to select the last row like this (If memory serves correctly, but it debugged)

     .ListRows(.ListRows.Count).Range.Select


How does one select the last row and last column of a table.
TX


Comment by: Jan Karel Pieterse (5-6-2014 10:27:49) deeplink to this comment

Hi Rudi,

I suppose this should work (it did on my system):

With ActiveSheet.ListObjects("Table1")
'Select last row
.ListRows(.listrows.count).Range.Select
End With

    With ActiveSheet.ListObjects("Table1")
        'Select last cell of table
        Intersect(.ListRows(.ListRows.Count).Range, .ListColumns(.ListColumns.Count).Range).Select
    End With


Comment by: Rudi (5-6-2014 12:26:08) deeplink to this comment

TX for the speedy reply.

I must have had something else wrong in the macro that I recall...
Anyways, TX for your examples. I'll be sure to try them again.

Cheers


Comment by: Cody (22-7-2014 16:12:44) deeplink to this comment

Hi,

I am working with Tables in VBA and had a question.

What I am trying to do is autofill a table range with the upper left corner fixed and the bottom right corner variable. Variable only in terms of more or less columns, rows are fixed with my method.

In my code I set the first cell equal to a formula
Next line I select the cell
Next line I attempt to auto fill

Here is a code I know works but is not variable:


Range("C15").Select
ActiveCell.FormulaR1C1 = "=Inputs!R3C5-Table1[@1]*R1C29"
Range("C15").Select
Selection.AutoFill Destination:=Range("Table3[[1]:[14]]"), Type:= _
        xlFillDefault



Here is the code I am trying to change it into:


Dim num as integer
num = 14                 '''Could be any input number'''

Range("C15").Select
ActiveCell.FormulaR1C1 = "=Inputs!R3C5-Table1[@1]*R1C29"
Range("C15").Select
Selection.AutoFill Destination:=Range(Table3[[1]:[num]]), Type:= _
        xlFillDefault



I took away the "" within the range because it would not treat num as an integer and said invalid object.

Any suggestions on how to make this work, or an alternate approach?

Thanks in advance!!


Comment by: Ted Pettit (25-7-2014 17:50:22) deeplink to this comment

Let's say you have a table (we'll call it Table1) with the following columns:
|Date|Time|Area|Technician|Activity|Completed|Delayed|Canceled|Reason|Current Status|
But you only want to perform some VBA function on the columns labeled Completed, Delayed, and Canceled, use this syntax:
Range("Table1[Completed]:Table1[Canceled]")
To include an additional column that is not adjacent i.e. the "Current Status" column to the previous selection, use this syntax:
Range("Table1[Completed]:Table1[Canceled],Table1[Current Status]")


Comment by: Eugenio (29-7-2014 04:49:34) deeplink to this comment

Please,
Using a unique cell, how do i get its index row in a table (not the row number into the sheet) and how do i get a cell value by this index and a column name?
Thank you very much.


Comment by: BobJordanB (3-8-2014 06:01:56) deeplink to this comment

Tables seem to be just what I want but cannot quite see how to do it.

I want to create a table of operations in Excel with half a dozen columns describing the parts of each operation.
In my vba I want to process each of the rows in turn using the column values as 'instructions'.

to be specific the table (here Named as Table) defines a sequence of parameter changes ( a start, an end, an increment and a sheet location as columns) (and a sequence of these in the rows) which in turn control a chart display. At each parameter change I save the modified chart as a png and then combine the pngs (in an AVI) to become a movie.
But cannot see a tidy way to refer to the columns by name.

Here is some pseudocode that uses a constructed table access command


For Each thisRow in Table.Rows
For myParam = Table.thisRow.[pStart] to Table.thisRow.[pEnd] step Table.thisRow.[pinc]
     Range(Table.thisRow.Locn) = myParam
     Calculate
     Export Chart
    Next myParam
Next thisRow


But cannot seem to find a way to tidily refer to the row elements using their column names.

Is their a way of referring to a row column intersection using something like:

    Table,thisrow,columnName OR Table(thisrow,[columnName]) etc.?

I could use Offsets to get to the row/column but this will require me to name each column separately which seems to be unnecessary as the naming has already been done

You can for example refer to the 'Thisrow' or @ row but that is for formulae in the table itself.

Is this possible?

The code works in a non-table format but I believe it can be made more readable

Any thoughts appreciated

Bob J.


Comment by: Bob Jordan (7-8-2014 13:02:33) deeplink to this comment

I tried to post a question on accessing the rows of a table but it seems to have got lost

meantime I have solved this problem and would like to share it with you.

How to work through the values in the rows of a table in vba

Turns out there is a nice construct.

row 3 of column ID of table Table1 is: Range("Table1[ID]")(3)

Rows go from 1 to Range("Table1").Rows.Count

If you have a single row table (for parameters etc you can skip the Row number part ie Range("Params[Colour]") gives you the first item below the heading.

This is very readable and conforms to the usual rules. you can append .text, .formula etc

You might like to add this to your help page.I searched hard to find this and it seems unknown. Please help others like me.

Bob JordanB


Comment by: Jan Karel Pieterse (11-8-2014 19:14:47) deeplink to this comment

Hi Bob,

Thanks for sharing!


Comment by: Jan Karel Pieterse (12-8-2014 11:12:33) deeplink to this comment

Hi Cody,

I expect this is a better approach:

Range("C15").resize(num).FormulaR1C1 = "=Inputs!R3C5-Table1[@1]*R1C29"


Comment by: Prateek Chauhan (22-8-2014 09:54:22) deeplink to this comment

why do my links are not being copyied to a table of a worksheet, from another table.... though it can be copied to a cell that is not in table in the same sheet. i.e only value is getting in a cell of a table not the link through vba( link := true, has been used while paste).
Please do help on urjent basis.
thankyou


Comment by: Adam (27-8-2014 17:17:21) deeplink to this comment

Hello,

This is great article - thank you! Unfortunately I did not find any PASTE tips. Could you explain how to paste into existing table, into specific column?

What I try to do is deleting data part of table and pasting new data into the table. Part of my table is data, another part is formulas calculating the data.

The code for deleting is working:

    

With Sheet1.ListObjects("Tabela1").Range
        Set Rng = .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible)
        Rng.Delete
    End With


Copying is working too.

Then I try to paste with code:
With Sheet1.ListObjects("Tabela1")
        .ListColumns("[DataColumn]").PasteSpecial Paste:=xlPasteValues
     End With


Unfortunately it shows 'Subscript out of range' :[

Could you advice where the problem is?


Comment by: Jan Karel Pieterse (27-8-2014 17:27:49) deeplink to this comment

Hi Adam,

Perhaps it should be:

.ListColumns("DataColumn").PasteSpecial Paste:=xlPasteValues


so without square brackets.


Comment by: Adam (27-8-2014 21:50:06) deeplink to this comment

Thanks. In fact the name of column is "Data Column" (with space between words). That's why there are square brackets.
Still I do not have an idea why it does not work.


Comment by: Adam (27-8-2014 21:55:18) deeplink to this comment

Is it possible the problem is because I copy 5 columns and in the code I mention only one (as a 'start point' for pasting)?


Comment by: Jan Karel Pieterse (28-8-2014 10:15:05) deeplink to this comment

Hi Adam,

What if you rewrite that to:

    With Sheet1.ListObjects("Tabela1")
        .ListColumns("Data Column").DataBodyRange.Cells(1, 1).PasteSpecial Paste:=xlPasteValues
    End With


Comment by: Adam (28-8-2014 10:50:22) deeplink to this comment

Thank you. I ended up with pasting into table as no luck with this solution. I will try to paste 'normally' without a table.
The solution that worked for me (with unaccepted, poor efficiency) is: (deleting, copying, pasting)


With Sheet3.ListObjects("Tabela1")
    If .ListRows.Count > 0 Then
        .DataBodyRange.Delete
    End If
End With

    With Sheet1
        LR = .Range("B" & Rows.Count).End(xlUp).Row
        .Range("B5:M5" & LR).Copy
    End With

With Sheet3.ListObjects("Tabela1")
    .Range.Resize(1, 1).Offset(1).PasteSpecial Paste:=xlPasteValues
End With

Unfortunately it takes 15 minutes for 2K rows. When I do it manually it takes 30 seconds. The problem is the columns that include formulas are spread into 50K rows after pasting (I do not know why) and then 2K rows of pasted data are added. The table that should be 2K rows big is 52K rows big after pasting. 50K of rows are empty but include formulas, which makes the file slow and inefficient.


Comment by: Jan Karel Pieterse (28-8-2014 11:31:45) deeplink to this comment

Hi Adam,

Perhaps the copy operation copies more than you are expecting? STep through the code up until the Copy command, execute the copy step and go to Excel to see which area is surrounded by the marching ants marquee.


Comment by: Adam (28-8-2014 12:16:16) deeplink to this comment

You are right Master!
This last "5" in .Range("B5:M5" & LR).Copy
made the range so big.
Corrected - works.
Thank you verrry much!


Comment by: Alberto Viveros (28-8-2014 19:32:30) deeplink to this comment

Hi, I have an Excel file that contains 105,000 records, and the user adds manually more data daily; but some of the new data have incomplete fields, and I need to create a macro to update the missing fields automatically when the user saves the file. For example, some of the missing data are the material group and the customer class, but I can read the values from the existing records in the same file. So I am thinking in create a macro that converts all the spreadsheet in a table, and in some way updates the missing fields from the existing records that contain information. For example, the table contains in some record the material "ABC" with the Category "Finish Products", and the user adds new records with material "ABC" with the Category empty. How can I update the new records from the existing records using tables?. If I design a macro to update one by one record, it will be very slow. In SQL I would use something like this:

Update n
SET n.Category = o.Category
From Table n
INNER JOIN Table o ON
n.Material = o.Material And o.Category <> ''
Where n.Category = ''

Is it possible to update in mass something like this in Excel Tables?

I appreciate you help, thanks.


Comment by: Jan Karel Pieterse (30-8-2014 11:49:10) deeplink to this comment

Hi Alberto,

In Excel you would select the blank cells and enter a VLOOKUP formula in them to get the missing information and after that copy, past sepcial values to "fix" them. This is not hard to code.


Comment by: Alberto Viveros (2-9-2014 15:12:03) deeplink to this comment

Thank you Jan, at the end I included in the macro a VLOOKUP formula for all the cells that had incomplete data, and it worked well. The code is simpler and the macro works faster. Thank you very much.


Comment by: Alonso (22-10-2014 14:31:12) deeplink to this comment

Hi Jan,
Your page looks great.
I'm trying to insert 3 new columns to an existing Table (Table1).
I named the first new column as [NEWCOLUMN1] and has a formula "= (RIGHT(TABLE1[ACCOUNT ],3))" ACCOUNT already exist into Table1. When I add it, it works perfectly well.
The Second new Column named as [NEWCOLUMN2] has a formula =""
The Third new Column named as [NEWCOLUMN3] has a formula
which refers to NEWCOLUMN2 and NEWCOLUMN1
.Formula = "=(IF(TABLE1[NEWCOLUMN2]="""",TABLA1[NEWCOLUMN1],TABLA1[NEWCOLUMN2]))".
When I add the columns though VBA the table calculates perfectly well the result in the first step but It occurs something very strange. When I change an ACCOUNT data NEWCOLUMNS1 refresh perfectly well the new result but NEWCOLUMNS3 as it is referred to newColumns added it seems they do not recognize it and there is no refresh. It only works if you press F2 into the cell, then it recalculate.
I thought the problem was to resize the table after adding columns but even in such case it doesn't works.
Finnaly I tried to create the formula after adding and resizing table but It does not refresh anyway.
Where can be the problem?
Thank you in advance for the support


Comment by: K. Robert Rhodes (22-10-2014 17:24:46) deeplink to this comment

I'm wondering when and whether we *must* use the Listobject Methods and Properties when dealing with Excel Tables versus the usual range-based programming we have developed over the years.

So far it looks like they are interchangeable, as long as you don't need the specific methods and properties unique to the ListObject. For example, ActiveCell.EntireRow.Delete seems to work fine when the ActiveCell is inside a ListObject's range. I.e., the table size (including changes to visible formats regarding banding) seems to properly update.

But I have seen no documentation from Microsoft that indicates when it is "safe" to do such things, or if treating ranges within a table as if they were simply ranges can get us into trouble or perhaps corrupt the ListObject.

If any of that made sense, I'm wondering if anyone has any thoughts on the matter.


Comment by: Jan Karel Pieterse (23-10-2014 07:05:26) deeplink to this comment

Hi Robert,

Valid question of course. As far as I can tell, you are not obliged to use the ListObject when working with ranges inside a table, if you don't want to.

It is just that the ListObject gives you things like named columns and you do not need to know the starting address of a listobject to work with it. It sort of makes it unnecesary to use named ranges when addressing specific areas of an Excel model from VBA if you want to have the flexibility of changing the design of the Excel model without wrecking code like this:

sSomeValue = Worksheets("Sheet1").Range("B25").Value


Comment by: Jan Karel Pieterse (23-10-2014 07:10:36) deeplink to this comment

Hi Alonso,

It looks like your formulas are all addressing entire table columns and not working with the row in question, perhaps that is the problem? Try adjusting the formulas so they point to the table row in question by adding @:

"= (RIGHT(TABLE1[@ACCOUNT ],3))"


Comment by: Alonso (23-10-2014 12:55:38) deeplink to this comment

Hi Jan
I also did a second test as follows

Sub Test2()
Dim Table As ListObject
Dim LC As ListColumn
Dim col As Integer
Dim i As Integer
' I have a table named Table1 with a column named ACCOUNT

' I want to add 3 Columns
For i = 1 To 3
         'Refer the tables
            Set Table = ActiveSheet.ListObjects("Table1")
         'Add a column to Table1
            Set LC = Table.ListColumns.Add

Select Case i 'Set the header and write the formulas
        Case 1
        LC.Name = "NCOL1"
        LC.DataBodyRange.Formula = "=(RIGHT(TABLE1[@ACCOUNT],3))"
        Case 2
        LC.Name = "NCOL2"
        LC.DataBodyRange.Formula = ""
        Case 3
        LC.Name = "NCOL3"
        LC.DataBodyRange.Formula = "=(IF(TABLE1[@NCOL2]="""",TABLE1[@NCOL1],TABLE1[@NCOL2]))"
End Select

Next i

'With this test you can see the same result. NCOL1 gives a result which refresh when you change column Account but NCOL 3 Does not refresh
End Sub


None of them Works.
Is there any kind of refresh for List Object which I didn't consider or any extra code that I can include to refresh automatically results from table once I change any data inside?


Comment by: Jan Karel Pieterse (23-10-2014 16:59:52) deeplink to this comment

Hi Alonso,

Weird. Seems like a bug.
If you hit control+alt+shift+F9, then the formula starts to behave itself and updates on changes as expected. In VBA you could add an

Application.CalculateFullRebuild

at the end to make it work.


Comment by: Diana G (11-12-2014 05:26:53) deeplink to this comment

I am so glad I found this page! I have been hunting high and low for my exact scenario and this is the first page that is not years old. I am no VB expert, but I am trying to write a macro using VB that will create a table, HOWEVER, the table is one of 3 on the same sheet AND it will vary in size each time it is created.

The table will always reside in A and B.
The rows may span anywhere from 5 to 85 down.

So the entire range will vary each time the macro is run.
The other table macros work perfect - their size is absolute, but the columns A & B will always have different results.

I have tried the {Dim lastRow As Long}
I have tried the {Set DynamicRange = Range("A2").CurrentRegion}

Neither are working.
Every time, the range table will be named Table3.

My code looks like:

Range("A1").Select
    ActiveSheet.ListObjects.Add(xlSrcRange, Range("$A$1:$B1"), , xlYes).Name = _
        "Table3"

I am beginning to believe you cannot use a Macro or VBA to insert a table if the range varies each application.

Any guidance would be greatly appreciated.
This is the only code I am missing to complete my project.


Comment by: Jan Karel Pieterse (11-12-2014 07:04:26) deeplink to this comment

Hi Diana,

So how would we know what range of cells must be converted to a table? How would you select the cells using your keyboard? (e.g. select A1 and press control+shift+down, shift right, or select the last row on column A and press control+Up)


Comment by: Diana G (11-12-2014 18:10:51) deeplink to this comment

Thank you for responding Jan.
I apologize, I know very little about VBA - I am not a programmer.

I think I understand your inference with the (select A1 and press control+shift+down, shift right, or select the last row on column A and press control+Up) scenario.

I suspect you are telling me that code can be written for these instructions. Unfortunately I have no idea what those are.

I did find this code last night and it was working. However, when I run it, it always wants to rename my table from Table3 to Table3_1 Then I changed everything in my code to reference Table3_1 and now when I run it changes the table name to Table3_2. I am at a loss. Perhaps someone can tell me where my code is getting these instructions and how to fix?

Here is my entire code for this particular action:


{Sub EICAddTable3Sheet2()

    Dim FinalRowNew As Integer

    FinalRowNew = Range("A" & Rows.Count).End(xlUp).Row

    Range("A1:B" & FinalRowNew).Select

    ActiveSheet.ListObjects.Add(xlSrcRange, Range("$A$1:$B$" & FinalRowNew), , xlYes).Name = _
    "Table3"
        
    Range("Table3[#All]").Select
    ActiveSheet.ListObjects("Table3").TableStyle = "TableStyleMedium17"

End Sub


Comment by: Jan Karel Pieterse (12-12-2014 06:40:47) deeplink to this comment

Hi Diana,

I intended that as a question to find out how to propose the right code :-)

About the name: I suspect there already is a table named Table3 in your workbook, table names must be unique across the entire workbook.


Comment by: Jim K (16-12-2014 06:19:06) deeplink to this comment

Hi Jan,

Excellent page! Very helpful indeed.
I have a question however that isn't dealt with yet. I have a table that has different formats for different columns. I want to insert columns at column position 30 that copies the format from the column that is already there, which becomes column 31, but also automatically set the totalrow to calculation type=1.(that is to sum the data content). I have been trying a few things but the code does not appear to execute ( being ignored). Can you make any suggestions?
Many thanks in advance

Jim


Comment by: Jan Karel Pieterse (16-12-2014 17:22:34) deeplink to this comment

Hi Jim,

What does the macro recorder give you and what happens if you run the recorded macro?


Comment by: Jim K (17-12-2014 05:52:16) deeplink to this comment

when I do the macro recording I get for the subtotal
<VB>ActiveSheet.ListObjects("Table3").ListColumns("Column2").TotalsCalculation = _
        xlTotalsCalculationSum</VB>
But when I run this code, at the end of the column insert the column gets inserted but the subtotal not
(By the way the same applies to the copy of the table header format. VBA ignores it)

Jim


Comment by: Jan Karel Pieterse (17-12-2014 09:05:11) deeplink to this comment

Hi Jim,

I suspect this is because the newly inserted column's name is different from the one your code recorded?


Comment by: Jim K (17-12-2014 12:18:29) deeplink to this comment

No, that is not it.
The newly inserted column's name is Column2
I have a hidden column to the left of it which name is Column1, all other columns have more specific names.


Comment by: Jan Karel Pieterse (17-12-2014 12:48:00) deeplink to this comment

Hi Jim,

The code in itself runs just fine if I try it on a listobject named Table3 on any worksheet.

Is the totalrow set to show for that table?


Comment by: Jim K (20-12-2014 13:29:38) deeplink to this comment

Hi Jan,

Thank you for your help. I've got it working now.
My insert routine, did not insert a table column but a sheet column. Once that part was fixed, the code for the total worked fine. The only thing I still need to do is color the header cell the same as the neighbouring cell in position 31

Cheers,
Jim


Comment by: Tom-S (20-12-2014 22:45:39) deeplink to this comment

Hi Jan,

The following is a snip from an Excel VBA module. On the "Data Ranges" sheet are several Excel Tables and I want to go through a subset of them, where the first 2 steps are to clear out the data and then resize the Tables so they have header row plus 1 blank data row.

The code seemed to be running ok until I tried it again recently in Excel 2013. The data clear out works ok but at the last line of code shown Excel crashes and shuts down. Any idea why it crashes and how to fix this?


Option Explicit

Dim LobjA(1 To 5) As ListObject
Dim w As String, x As String, z As Integer

Sub db_update()

w = Application.GetOpenFilename

Workbooks.Open (w)

x = Right(w, Len(w) - InStrRev(w, "\", , vbTextCompare))

Set LobjA(1) = Workbooks(x).Sheets("Data Ranges").ListObjects("Table1")
Set LobjA(2) = Workbooks(x).Sheets("Data Ranges").ListObjects("Table4")
Set LobjA(3) = Workbooks(x).Sheets("Data Ranges").ListObjects("Table6")
Set LobjA(4) = Workbooks(x).Sheets("Data Ranges").ListObjects("Table7")
Set LobjA(5) = Workbooks(x).Sheets("Data Ranges").ListObjects("Table8")


For z = 1 To 5

    LobjA(z).DataBodyRange.Value = ""

    LobjA(z).Resize LobjA(z).Range.Resize(2)


Regards, Tom


Comment by: Jerry Paladino (21-12-2014 16:46:11) deeplink to this comment

Hi Jan,

When creating a Table in VBA with something like...

ActiveSheet.ListObjects.Add(xlSrcRange, Range("$B$1:$D$16"), , xlYes).Name = "Table1"


The table becomes a Defined Name in the Name Manager. Is it possible to make this Defined Name hidden to prevent the end user from seeing its contents and location.

Thank You,
Jerry


Comment by: Jim K (21-12-2014 21:45:43) deeplink to this comment

Hi Jan

Below code works in Excel 2013 but not in Excel 2010
Any suggestions

Sub insCol()
Dim oSh As Worksheet
    Set oSh = ActiveSheet
    oSh.ListObjects("Table3").ListColumns(3).Range.Select
    Selection.ListObject.ListColumns.Add Position:=3
    oSh.ListObjects("Table3").HeaderRowRange(3) = InputBox("Enter the month and year for the header of this new column", "inserting new month column") & " No of Sessions"
    oSh.ListObjects("Table3").ListColumns(3).TotalsCalculation = xlTotalsCalculationSum
End Sub


Comment by: Jan Karel Pieterse (22-12-2014 11:05:51) deeplink to this comment

Hi Jim,

Your code works fine for me on my Excel 2010. Are you sure:

- The activesheet contains the listobject
- The listobject has the correct name
- The listobject has the totalrow showing?


Comment by: Jan Karel Pieterse (22-12-2014 11:07:28) deeplink to this comment

Hi Jerry,

I'm afraid table names cannot be hidden.


Comment by: Jan Karel Pieterse (22-12-2014 11:13:21) deeplink to this comment

Hi Tom,

Rather than putting an empty string in the databodyrange (which does not actually empty the cells!) I would clear them:
LobjA(z).DataBodyRange.Clear

Other than that, I suspect a problem with the workbook in question, the code seems alright to me.


Comment by: Jim K (23-12-2014 02:34:37) deeplink to this comment

Hi Jan,

I checked that the active sheet contains the listobject
The listobject has the correctname and the totalrow is showing
I have noticed that the code works in a spreadsheet with less rows but the same number of columns
The funny thing is that the column gets inserted in the right position however it stretches way beyond the table
There is some activity prior to the insert and then the code just exits. Putting a trace and using a variable to store the new column, this variable never gets "filled"


Comment by: Jan Karel Pieterse (23-12-2014 11:33:09) deeplink to this comment

Hi Jim,

What if you copy the table and paste-special values to another workbook and then format that as a table and rename the table accordingly? Does the code run without a problem then?


Comment by: Jim K (24-12-2014 05:15:42) deeplink to this comment

Hi Jan

Just discovered that the problem was a UDF that I used to conditionally format all cells with a formula in it. Once I removed this UDF, the code ran without problems

Have a great Christmas and all the best for 2015

Jim


Comment by: Jan Karel Pieterse (24-12-2014 10:00:29) deeplink to this comment

Hi Jim,

Thanks for letting us know!


Comment by: Chad (9-1-2015 21:13:09) deeplink to this comment

I am trying to build an excel document that pulls information based of the following:
I have multiple "kits" that are made up of number of parts with descriptions, part numbers and quantities which are stored in a table form and fall under a specific kit name.
I would like to select a kit by name from a Listbox and the information pertaining to that kit is placed inside a specified table location.
I have been working on this for a number of days without success.
Thank you for any help you may be able to give.


Comment by: Jan Karel Pieterse (9-1-2015 22:19:41) deeplink to this comment

Hi Chad,

Sounds like you would need a pivot table for that which you filter for the kit name.


Comment by: John (23-1-2015 20:16:17) deeplink to this comment

I have a macro i run to place the contents of the cell into a comment on the cell. Macro was included in the cell menu. I have read your other menuing articles and I can add the menu to the cell menu .. as long as it is not in a table.. Suggestions?


Comment by: Jan Karel Pieterse (27-1-2015 07:26:09) deeplink to this comment

Hi John,

The right-click menu you need is called "List" I expect.


Comment by: Jeff Weir (11-3-2015 06:36:09) deeplink to this comment

Hi Jan Karel. Good idea in your RemoveFormattingOfTable() to create a copy of the normal style with its Number checkbox to false. I didn't know you could do that (the number false thing).

But in regards to the macro approach, I don't see the need to to apply .TableStyle = "TableStyleLight1" at all - after all, a TableStyle will already be applied.

And rather than add the NormalNoNum style simply to delete it again, it strikes me that because this stlye is so handy, you may as well just add it and leave it there for future use. In fact, if NormalNoNum is available to the user, then the only advantage of the macro is to apply it to the whole table. But a user could just as easily select the whole table and apply NormalNoNum themselves.

What are your thougts? ALso, I don't mean this to sound critical...I learnt something valueable from this macro.

Regards

Jeff


Comment by: Jan Karel Pieterse (11-3-2015 09:37:29) deeplink to this comment

Hi Jeff,

Good point, there really is little need to get rid of that style. And setting the tablestyle isn't needed in that code either because the table will already have a style, which the macro rudely overrides :-)

And -of course- you can also uncheck other boxes for that style if there are certain parts of the formatting you wish to keep.


Comment by: Jeff Weir (11-3-2015 10:33:13) deeplink to this comment

Cool, thanks for the reply. I just added in a subsection in my book about making a 'Keep Number Format Only' cell style, and also showing how to save it to a Template file (.XLTX or .XLTM) stored in the XLSTART folder, so that this cell style is available for all new workbooks. (I get readers to do the same with an amended TableStyle too).

Thanks for the inspiration, Jan Karel. I wish I had known about the ability to turn off that Numbers option years ago.


Comment by: Leanne (2-4-2015 03:02:14) deeplink to this comment

My "Dashboard" worksheet displays drop down boxes to allow viewers to customize what PROJECT AREA is displayed.
Excel 2013 performance has dropped significantly from Excel 2010 eg click dropdown box to choose different view, takes 20-55secs to run code & display new graphs 2010 was instant.
Have optimized the code eg Events, Calculation, Screen updating etc at start of code and enable again at end.
Excel’s INQUIRE tool says a number of Errors in my formulas: Max(#Ref)
but look at formula in spreadsheet it looks correct: Max(tbl_Performance[DirectMhrs])
Wherever I refer to table lists INQUIRE says there’s a #Ref error.
I'm wondering whether this is the reason the performance has dropped. Does Excel 2013 have issues with working with table list references?


Comment by: Jan Karel Pieterse (2-4-2015 07:15:32) deeplink to this comment

Hi Leanne,

I have not heard about problems with table references in 2013. That 2013 is slower than previous versions is a known "issue". Excel 2013 is particularly slow when (un)protecting sheets. Does your code do a lot of that?


Comment by: LJ (5-4-2015 20:10:16) deeplink to this comment

Whenever I try to add a new row to a table and add data to it, I get the header row first. How do I have the new row come after the header row?


Set newRow = lstAllTrans.ListRows.Add(Position:=lstAllTrans.ListRows.Count + 1, AlwaysInsert:=True)

'The first time this is a header row
lstAllTrans.Range(newRow.Index, lstAllTrans.ListColumns("Amount").Index).Value

I've also tried:
Set newRow = lstAllTrans.ListRows.Add( AlwaysInsert:=True)


Comment by: Leanne (6-4-2015 13:09:25) deeplink to this comment

Hi Jan
I tried removing the protection code but performance still same so, I've put it back in at the moment. It only runs once.
Are there Excel settings maybe that I need to check? Its only been put onto my laptop recently as IT are doing testing to see how it performs before upgrading everyone.
My Supervisor is considering asking that our laptops in our section aren't upgraded due to the performance issues.


Comment by: Jan Karel Pieterse (7-4-2015 10:45:21) deeplink to this comment

Hi Leanne,

Without seeing the workbook it is hard to advise. You might consider sending it? My email address is at the bottom of this page.


Comment by: Jan Karel Pieterse (7-4-2015 10:46:18) deeplink to this comment

Hi LJ,

How is that object variable lstAllTrans defined?


Comment by: Ken Witchel (31-8-2015 18:53:17) deeplink to this comment

I would like to loop through the tables on the active sheet (more than 20 tables, all the same structure). I was considering using one of the following to snippets.


    For Each tbl In ActiveSheet.ListObjects
        Range("tblIncome[Mth2]").Copy
        Range("tblIncome[Mth3]").PasteSpecial
    Next tbl


OR

    For Each tbl In ActiveSheet.ListObjects
        ActiveSheet.ListObjects("Table1").ListColumns(3).DataBodyRange.Copy
        ActiveSheet.ListObjects("Table1").ListColumns(4).DataBodyRange.Paste
    Next tbl


How do I make it generic without having to write the actual table names?

Your help would be most appreciated.

Ken


Comment by: Jan Karel Pieterse (1-9-2015 08:52:21) deeplink to this comment

Hi Ken,

That is realtively easy:

For Each tbl In ActiveSheet.ListObjects
tbl.ListColumns(3).DataBodyRange.Copy
tbl.ListColumns(4).DataBodyRange.Paste
Next tbl


Comment by: Ab (10-9-2015 13:40:41) deeplink to this comment

Hi Jan,

In my Vba i have made some tables. In Names from Excel 2010 I see Table1 than {"XXX"etc.} and =Bestanden!$H$2:$H$14 and underneathe Range/Bereik I see Werkmap but i have no range called that way. How can I remove this Table1 with vba?

Thanks for your help :-)

Regards Ab


Comment by: Rob (10-9-2015 15:48:26) deeplink to this comment

    ActiveSheet.ListObjects("Table1").Range.AutoFilter Field:=3, etc

JK, is it possible to change hardcoded Field:=3 to e.g. Field:=Range("Table1[City]")

If I currently have an NAW table Field3 is ok but If I insert column Pcode before Woonplaats I need to update VBA code. Not very efficient.
PC, Windows 7 Office 2010.
Thx!


Comment by: Jan Karel Pieterse (11-9-2015 11:43:58) deeplink to this comment

Hi Rob,

The way to do that is to calculate the right column number from the name of the columns table, so you need something like:

lColNum = Range("Table1[City]").Cells(1,1).Column - Range("Table1").Cells(1,1).Column + 1
ActiveSheet.ListObjects("Table1").Range.AutoFilter Field:=lColNum, etc


Comment by: Jan Karel Pieterse (11-9-2015 11:46:19) deeplink to this comment

Hi Ab,

You can only remove table names from Name Manager by converting the tables back to ranges.


Comment by: Elizabeth (16-9-2015 01:47:19) deeplink to this comment

Is there a way I can extract values from a table? I am currently trying to list which columns in the table are filtered.

Here is my code:


Function CheckFilters(r As Range)
Dim r As Range

fstate = ""
c = ""

If Worksheets("Summary").ListObjects("SummaryTable").ShowAutoFilter Then
    c = Worksheets("Summary").Cells(1, Columns.Count).End(xlToLeft).Column
    c = c - 1


    'go through each column and check for filters
    For i = 1 To c Step 1
     If Worksheets("Summary").ListObjects("SummaryTable").FilterMode(i).On Then
     'If aws1.AutoFilter.Filters(i).On Then
            fstate = fstate & r(i).Value & ", "
     End If
    Next i

    'removes the last comma
    fstate = Left(fstate, Len(fstate) - 2)
Else
    fstate = "NO ACTIVE FILTERS"
End If

'CheckFilters = fstate
MsgBox fstate
End Function


The part that is messing me up is

If Worksheets("Summary").ListObjects("SummaryTable").FilterMode(i).On

Thank you in advance for your assistance.

Elizabeth


Comment by: Jan Karel Pieterse (16-9-2015 10:53:17) deeplink to this comment

Hi Elizabeth,

You can display the columns that are filtered like so:

Sub ShowFilteredColumns()
    Dim oLo As ListObject
    Dim oF As Filter
    Dim lCol As Long
    Set oLo = ActiveSheet.ListObjects(1)
    For Each oF In oLo.AutoFilter.Filters
        lCol = lCol + 1
        If oF.On Then
            MsgBox "Column '" & oLo.ListColumns(lCol).Name & "' is filtered"
        End If
    Next
End Sub


Comment by: Erin Rinen (16-9-2015 17:23:40) deeplink to this comment

Hi,

I am trying to list the criteria used in AutoFilter in a table. There are many tutorials on how to do this with a range but not with a table. Could you help out with that?
Here is what I have so far based off of tutorials for ranges:


Sub AutoFilter_Criteria2()
Dim str1 As String
Dim str2 As String

Dim f As Filter

Application.Volatile

For Each f In Worksheets("Sheet1").ListObjects("Table1").HeaderRowRange(1).AutoFilters.Filters

    'With .Filters(Header.Column - .Range.Column + 1)
        'If Not .On Then Exit Sub
         ' str1 = .Criteria1
        If .Operator = xlAnd Then
            str2 = " AND " & .Criteria2
        ElseIf .Operator = xlOr Then
            str2 = " OR " & .Criteria2
        End If
' End With
End With
End Sub


Comment by: gindia (17-9-2015 17:35:25) deeplink to this comment

Hi, thanks for sharing all the details about tables, that was really eye-opening...!

I applied some of this stuff to my vba project successfully, and I really like the tables concept, it seems to keep the code easier to read than without it.

Unfortunately I'm stuck with one item: I am trying to apply a simple loop through the table rows to check whether column N holds a specific string. If yes: delete row, if not: continue the loop.

I keep getting an error message called 'compile error: type mismatch'... any hints are really appreciated because I've spent hours on this now without getting a real brainwave...

Please find below my piece of work so far:


Sub hire_sheet()

Dim hirelist As ListObject
Dim hiresheet As Worksheet

Set hiresheet = Worksheets("hires")
Set hirelist = hiresheet.ListObjects(1)
    
hiresheet.Activate
    
finalrow = hirelist.ListRows.Count

    ' delete all action reasons 'EIL' in column N

    I = 1
        Do While I <= finalrow
         If hiresheet.Range("N" & I).Value Is "EIL" Then
                hirelist.ListRows(I).Delete
                I = I - 1
                finalrow = finalrow - 1
            End If
            I = I + 1
        Loop

End Sub


Comment by: Jan Karel Pieterse (18-9-2015 11:19:26) deeplink to this comment

Hi Gindia,

This code should work better:

Sub hire_sheet()

    Dim hirelist As ListObject
    Dim hiresheet As Worksheet
    Dim finalrow As Long
    Dim i As Long
    Set hiresheet = Worksheets("hires")
    Set hirelist = hiresheet.ListObjects(1)

    hiresheet.Activate

    finalrow = hirelist.ListRows.Count

    ' delete all action reasons 'EIL' in column N

    For i = finalrow To 0 Step -1
        If hirelist.ListRows.Count > 0 Then
            If hirelist.ListColumns("a").DataBodyRange.Cells(i).Value = "EIL" Then
                hirelist.ListRows(i).Delete
            End If
        End If
    Next
End Sub

NOte that you'll have to replace the "a" with the column name in your listobject


Comment by: Jan Karel Pieterse (18-9-2015 11:26:46) deeplink to this comment

Hi Erin,

Try this:

Sub AutoFilter_Criteria2()
    Dim str1 As String
    Dim oLo As ListObject
    Dim f As Filter
    
    Set oLo = ActiveSheet.ListObjects(1)
    For Each f In oLo.AutoFilter.Filters
        With f
            If .On Then
                'If Not .On Then Exit Sub
                str1 = .Criteria1
                If .Operator = xlAnd Then
                    str1 = str1 & " AND " & .Criteria2
                ElseIf .Operator = xlOr Then
                    str1 = str1 & " OR " & .Criteria2
                End If
            End If
        End With
    Next
    MsgBox str1
End Sub


Comment by: gindia (19-9-2015 11:05:09) deeplink to this comment

Hi Jan,
thanks so much for your help, your code worked like a charm!!
interesting how you changed it, I wouldn't have thought of this... one day I really need to attend a VBA class instead of all this autodidactic muddling through ;-)

you saved my weekend!!
gindia


Comment by: Jan Karel Pieterse (19-9-2015 16:06:08) deeplink to this comment

Hi Gindia,

You're welcome.
NB: I'm planning an advanced VBA class, see the survey here:
http://1drv.ms/1nzY7ZF


Comment by: Poul Madsen (excelent) (21-10-2015 21:00:27) deeplink to this comment

Hi Jan
Im trying to update my table from another sheet in VBA
Im using folowing code :

Sheets("PeriodeAnalyse").ListObjects("Tabel4610").Range.AutoFilter Field:=3, Criteria1:="<>0", Operator:=xlFilterValues

It wont Work

change sheet with : Sheets("PeriodeAnalyse").Activate dosent help either

What to do ?

Regards Poul


Comment by: Jan Karel Pieterse (22-10-2015 12:00:21) deeplink to this comment

Hi Poul,

The syntax appears correct to me. What error do you get?


Comment by: Poul Madsen (excelent) (22-10-2015 22:43:45) deeplink to this comment

Hi Jan

uff im embarrassed. i forgot a "exit sub" the code vorking fine now
ill try finding a Deep hole to jump in :-(

sry. for ur time Waste

p.m


Comment by: vikas (14-11-2015 19:14:54) deeplink to this comment

hi please guide for VBA code to find how many rows and columns given into table data, table name will be given by the user.


Comment by: Jan Karel Pieterse (16-11-2015 15:37:52) deeplink to this comment

Hi Vikas,

Option Explicit

Public Function GetTable(sName As String, Optional oWb As Workbook) As ListObject
    Dim oSh As Worksheet
    Dim oLo As ListObject
    If oWb Is Nothing Then Set oWb = ActiveWorkbook
    For Each oSh In oWb.Worksheets
        For Each oLo In oSh.ListObjects
            If LCase(oLo.Name) = LCase(sName) Then
                Set GetTable = oLo
                Exit Function
            End If
        Next
    Next
End Function

Sub Demo()
    Dim oLo As ListObject
    Set oLo = GetTable("Table1")
    If Not oLo Is Nothing Then
        MsgBox "ListObject 'Table1' has " & oLo.ListRows.Count & " rows and " & vbNewLine & oLo.ListColumns.Count & " columns"
    End If
End Sub


Comment by: Damian (26-11-2015 08:41:02) deeplink to this comment

Hi Jan,
I'm trying to loop through rows of a filtered table, and trap the error if there are no rows in the filter.

Sub mcr_FilteredData()
Dim oFilteredRange As Range
Dim oCell As Range
Dim str_AcctCode As String
str_AcctCode = "6-1790"

    'Selection.AutoFilter
    ActiveSheet.ListObjects("t_Actuals").Range.AutoFilter Field:=2, Criteria1:= _
        str_AcctCode & Chr(13) & ""
    Range("t_Actuals[[#Headers],[FY-FM]]").Select
    'ActiveSheet.ListObjects("t_Actuals").Range.AutoFilter Field:=16, Criteria1 _
    '    :=Array("15-07", "15-08"), Operator:=xlFilterValues
    ActiveSheet.ListObjects("t_Actuals").Range.AutoFilter Field:=16, Criteria1:= _
        ">=1507", Operator:=xlAnd, Criteria2:="<=1606"
    Set oFilteredRange = ActiveSheet.ListObjects("t_Actuals").DataBodyRange
    If oFilteredRange.SpecialCells(xlCellTypeVisible) Is Nothing Then
        Exit Sub
    End If
    
    For Each oCell In oFilteredRange.SpecialCells(xlCellTypeVisible).Rows
        Debug.Print Cells(oCell.Row, 6).value
    Next
End Sub

Your help would be much appreciated.
Regards
Damian


Comment by: Jan Karel Pieterse (26-11-2015 09:32:39) deeplink to this comment

Hi Damian,

I think this should work:

On Error Resume Next
Set oFilteredRange = ActiveSheet.ListObjects("t_Actuals").DataBodyRange.SpecialCells(xlCellTypeVisible)

If oFilteredRange Is Nothing Then
    Exit Sub
End If
On Error Goto 0


Comment by: Damian (26-11-2015 11:08:56) deeplink to this comment

Hi Jan,
Your tip worked a treat. Thank You.
Regards
Damian


Comment by: Dave (9-12-2015 13:49:25) deeplink to this comment

I have an excel sheet with thousands of records. In column A it has a unique code (like item code) and column B shows a quantity for that code. (kind of stock taking).

Very often I need to update the quantity for a few items. usually i get an external list of codes (these values are existing in Column A) and I need to either increase or decrease the quantity of that code (usually its either -1 or +1)

What VB code can i use that:
1) when I open the sheet it asks me if i want to update. (or alternatively I can have a button that I click myself to activate the macro)
2) If i want to update, it prompts me to input the codes (ideally allowing a copy and paste) and selecting if I want to increase or decrease the quantity for that code. (its ok to update all the matching codes with same +1 or -1). (i never add and subtract in same session). So there could also be two buttons, one for adding and one subtracting, this is fine

as an example.
Column A has Codes AA, BB, CC, DD, EE. Column B Values are 10, 20, 30, 40 and 50 respectively.

Now I want to update CC and EE with a +1. The VB should ask me to paste which values need updating (in this case CC, EE) and it should make the values in column B as 10, 20, 31,40,51.

If you can help me it will save me hours per day! Many thanks
Dave


Comment by: Jan Karel Pieterse (9-12-2015 16:45:50) deeplink to this comment

Hi Dave,

What about a manual method.

1. Enter a 1 in any cell
2. Filter the table so only the ones that need an update are visible
3. Copy the cell with the 1
4. Select the numbers in your filtered table
5. Press control+alt+v to open the paste sepcial dialog
6. Select "Values" and "Subtract" or "Add" and click OK.


Comment by: Tarpan (13-12-2015 19:31:52) deeplink to this comment

Hello Jan, I have following questions regarding table using VBA, your comments would be helpful. Thanks.
1. How to determine the empty row in table to paste the data.
2. If there is not enough rows in table to paste the data, how to make that happen with VBA or In this situations VBA will automatically add the rows and paste the data?


Comment by: Jan Karel Pieterse (14-12-2015 09:03:39) deeplink to this comment

Hi Tarpan,

This should find the first empty row starting from the top of the table:

    With ActiveSheet.ListObjects("Table1")
        If .ListRows.Count = 0 Then
            .HeaderRowRange.Columns(1).Offset(1).Select
        Else
            .HeaderRowRange.Columns(1).End(xlDown).Offset(1).Select
        End If
    End With


Comment by: gindia (18-12-2015 14:37:06) deeplink to this comment

Hi all,

I´m currently struggeling with a piece of vba code and I am really running out of ideas, but maybe you can help.

As a part of my code, I want excel to write a formula into a column of an excel table. Pretty simple, and it´s running perfectly fine on my excel 2013, but whenever it´s run on excel 2007, it returns an error message at this piece of code:

Set psdata = ActiveSheet.ListObjects("data")
x = psdata.ListColumns.Count
...
psdata.ListColumns(x).DataBodyRange.Cells.formula = _
"=IF(ISNUMBER(SEARCH(""regional"",[@[scope]])),""regionally relevant"","""")"

So the code keeps getting stuck at inserting the formula, but I have no idea why - I always thought that excel 2007, 10 and 13 are pretty similar, and only previous versions will struggle with listobject commands(?)

Glad about any ideas from your end...


Comment by: Jan Karel Pieterse (18-12-2015 15:01:56) deeplink to this comment

Hi Gundula,

The syntax for referencing "this row" is different in Excel 2007. In 2010 and up it is:


[@[scope]]


in 2007 it is:

[[#This Row];[scope]]


Comment by: gindia (5-1-2016 09:31:15) deeplink to this comment

hi Jan,
thanks for highlighting the differences about 2010 and 2007 to me, that's extremely helpful!!
have a great week


Comment by: Jan Karel Pieterse (5-1-2016 09:56:20) deeplink to this comment

Hi Gundula,

You're welcome!


Comment by: Ali Khan (9-1-2016 21:09:34) deeplink to this comment

Hi,

Can u help me to provide with vba code to insert table in worksheet based on the available data which can be dynamic.

Similarly if the table is already available in the sheet then it should be deleted and new table be inserted on the data available in sheet

Thanks a lot in advance.

Regards,


Comment by: Jan Karel Pieterse (11-1-2016 13:22:45) deeplink to this comment

Hi Ali,

If there already is a table on a sheet, then what data would be available for the new table?


Comment by: Jeff Carnahan (12-1-2016 14:50:36) deeplink to this comment

Great post! I have two question; 1) how do I delete all the rows in a table without using a loop? and 2) how does excel 'remember' formulas I have entered in some columns after deleting all the rows? eg. I deleted all the rows in a table, then I add some data (rows) back, and the formulas magically reappear in the columns where they were. But it only does it for some columns. Thank you!


Comment by: Jan Karel Pieterse (12-1-2016 15:05:54) deeplink to this comment

Hi Jeff,

1:


Sub Example()
    Dim oLo as ListObject
    Set oLo = ActiveSheet.ListObjects(1)
    oLo.DatabodyRange.Delete
End Sub


2:
Excel stores the formula internally. Odd enough, there is no userinterface to get at that stored formula. The only way is to make sure there is a row in the table and then one can edit the formula. If the same formula is in all rows that will replace the "default".


Comment by: Stayce (13-1-2016 18:52:40) deeplink to this comment

I've developed a code to insert a user-specified number of lines utilizing a message box. I would like to use

Selection.ListObject.ListRows.Add AlwaysInsert:=True
to add the lines at the bottom of the table but I also need it to add the number of lines entered by the user, so combining it with
Selection.ListObject.ListRows.Add (11)
. Is there a simple way to do this?

Thank you!


Comment by: Jan Karel Pieterse (13-1-2016 21:30:16) deeplink to this comment

Hi Stayce,

Seems you can only add one row at the time so you'd need to loop as many times as entered:


    Dim lRows as long
    Dim lCt as long
    lRows=INputbox("How many Rows")
    For lCt=1 to lRows
        Selection.ListObject.ListRows.Add
    Next
End Sub


Comment by: Dr. Demento (22-1-2016 20:23:50) deeplink to this comment

Jan, great stuff!! I really enjoy your site!!

I've developed a function to create pivot tables where the user provides source, destination, & pvt tbl name. However, I'd like to be able to specify the style for each table so they have something to distinguish them. I was hoping that there was a TableStyle index analogous to ColorIndex (pre-defined styles that I could assign using a For Next loop (i.e., a numerical assignment for each TableStyle)). If there is, what is the syntax to assign a style to each table?

Much appreciated.


Comment by: Jan Karel Pieterse (22-1-2016 21:18:12) deeplink to this comment

Hi,

The syntax is something like this:

ActiveSheet.PivotTables("PivotTable1").TableStyle2 = "PivotStyleMedium2"

and the stylenames you see here are derived from the staylename you see when you hover your mouse over them when you manuallly change the PT style (just remove the spaces from the names).


Comment by: DaveM (2-2-2016 05:48:04) deeplink to this comment

I'm trying to work with tables that are not in the activesheet. If I have my_table on Sheet1 and Sheet1 is the activesheet then this code works.

Dim oLo As ListObject
Set oLo = Range("my_table").ListObject

oLo.ListColumns(1).DataBodyRange.Select


However if I run the code (in xl2013) and Sheet2 is active I get a runtime error. Just wondering what the correct method is?


Comment by: Jan Karel Pieterse (2-2-2016 15:54:39) deeplink to this comment

Hi Dave,

In that case you need to tell VBA what sheet it needs:

Dim oLo As ListObject
Set oLo = Worksheets("Sheet1").Range("my_table").ListObject
'The next line may fail because you cannot select cells if they are on a different sheet
oLo.ListColumns(1).DataBodyRange.Select
'To ensure this works, use Goto instead:
Application.Goto oLo.ListColumns(1).DataBodyRange


Comment by: Bryan (9-2-2016 04:34:47) deeplink to this comment

I want to control the size of a table with a formula, like an offset formula or an aggregate formula. It seems the table feature doesn't remember the formula i put into the size box (oddly enough it calculates the formula correctly so it works once and quits) so i want to try and control it with VBA is there a code to set the number of rows


Comment by: Jan Karel Pieterse (9-2-2016 11:07:09) deeplink to this comment

HI Bryan,

I'm not sure I understand what you need?
You can resize a table using VBA:

ActiveCell.ListObject.Resize Range("A1:D100")


Comment by: Dino (17-2-2016 12:02:47) deeplink to this comment

Hello,

I was trying to get the a value from same row of the active cell in the table but from another column "name".

Is this possible?
e.g if my active cell in the table was d:4 and I wanted to return the value from the same row but from a:4 (lets say the column name was called "Colour"), what would the VBA look like for this?

Kind Regards


Comment by: Jan Karel Pieterse (17-2-2016 14:00:50) deeplink to this comment

Hi Dino,

Here is one way:

MsgBox Intersect(ActiveCell.ListObject.ListColumns("Colour").Range, ActiveCell.Entirerow).Value


Comment by: damian (18-2-2016 23:41:12) deeplink to this comment

Hi Jan,
I want to run a sql type command in VBA over an excel table like
Select [name] from table where [country]='Australia'
I have got this to work OK:
With sheets("Sheet1").ListObjects("t_Employees")
    For i = 1 To .DataBodyRange.Rows.Count
        if .ListColumns("Type").DataBodyRange.Cells(i, 1).value="Australia"
            do something
        end if
    next i
end with
But it is slow, specially if there are a large number of records in the table.
Using filters doesn't give me reliable results.
Regards
Damian




Comment by: Jan Karel Pieterse (19-2-2016 11:55:48) deeplink to this comment

Hi Damian,

You say filtering does not produce reliable results. Can you elaborate? It should work as expected


Comment by: Damian (20-2-2016 04:12:32) deeplink to this comment

Hi Jan,
I used .DataBodyRange.SpecialCells(xlCellTypeVisible), but when I changed the filter using VBA
for the next loop (ie all employees In USA, it didn't reset it correctly.

Regards
Damian


Comment by: Jan Karel Pieterse (20-2-2016 15:01:04) deeplink to this comment

Hi Damian,

Without seeing your code this is really hard to comment on :-)


Comment by: AD (23-2-2016 08:40:53) deeplink to this comment

HI

Thanks for this post, very useful

Is it possible to make us an introduction to the use of the table as the data source for a ListBox in UserForm for example.

Thanks in advance


Comment by: Jan Karel Pieterse (24-2-2016 08:30:43) deeplink to this comment

Hi AD,

Sure. To add one column to the listbox:

Listbox1.List = Worksheets("Sheet1").ListObjects("Table1").ListColumns("Column1").DataBodyRange.Value


Comment by: RENZO (1-3-2016 12:53:29) deeplink to this comment

there is a way to do something like that, I have a table with 2 columns Item and Quantity:

Item Quantity
Item1 4
Item2 3

I need to copy each Item (and entire related row) and paste in another sheet for many times as quantity field:

Item1
Item1
Item1
Item1
Item2
Item2
Item2

Thank you


Comment by: Jeff Carnahan (4-3-2016 17:11:42) deeplink to this comment

I have a table where I refresh the data daily. First, I delete all the rows using the code below that was provided in this forum previously (thanks again!). I then paste the new data into a blank row just below the table and then resize the table to include the new rows. I would like to automate the resizing of the table. I realize I could paste the data directly into the table by pasting my new data starting from the first row of the table but this causes problems; 1) because I am pasting 200K to 500K rows of data, this causes Excel to get stuck/freeze regularly, and 2) this is partly because Excel automatically tries to populate/copy down some of, but not all, the columns that contain formulas. When I paste the data just below the table and then manually resize it, Excel doesn’t freeze and it doesn’t try to copy down the formulas. What I would like is a script that looks at the new data below the table, gets the last row number, and then uses that number to resize the table. All suggestions welcome!


Sub DeleteTableRows()
    Dim oLo As ListObject
    Set oLo = ActiveSheet.ListObjects(1)
    oLo.DataBodyRange.Delete
End Sub


Comment by: Jan Karel Pieterse (4-3-2016 20:51:43) deeplink to this comment

Hi Jeff,

The last row in for example column A can be determined as follows:


Dim lLastRow as long
lLastRow = Cells(1, ActiveSheet.Rows.Count).End(xlUp).Offset(1).Row


Comment by: Warren Hall (7-4-2016 19:24:45) deeplink to this comment

Thank you for the wonderful, clear, and concise write-up on Tables. It is most helpful.


Comment by: AlexBY (28-4-2016 18:17:35) deeplink to this comment

Hi Jan,

Thank you for this article.
Could you confirm that after applying the filter to the table
by
....
oSh.Range("Table1[[#All],[Column2]]").AutoFilter _
field:=1, _
Criteria1:="Otis", _
VisibleDropDown:=True

....

I will get exactly the data I want (visible part of the iceberg)?
by
....
'select entire data section of table
oSh.Range("Table1").Select
....

or do I try to achieve in a wrong way?

thank in advance.


Comment by: Jan Karel Pieterse (29-4-2016 11:51:00) deeplink to this comment

Hi Alex,

The best way to ensure your code is right is by testing. One thing to look out for is whether it makes a difference if the table is already filtered on another column of the table.


Comment by: Jake (13-5-2016 22:25:34) deeplink to this comment

Thank you so much! This is the best breakdown I have found yet on VBA for tables in Excel. I really really appreciate it, my company too!


Comment by: randolf.Kaempfer@messefrankfurt.com (30-5-2016 18:13:27) deeplink to this comment

Hello,

I try to pick out of the table "tab.Ref.Kontakte" an email-address. But something is wrong. To record the code it is easy, but it only works as formual in a cell. It try to translate in VBA-Code, but i did something wrong.
1. To set the "rng", what is here the best code
2. In Column 11 there is my match-Name. I read out the row and in column 8 in the same table there is the email-address I wanted.

I search for an idea in the WWW, but the function of tables is not very often listen.

Set wbk = ActiveWorkbook
'Set shtContact = wbk.Sheets("Ref-Kontakt")
'Set tblContact = shtContact.ListObjects("tab.Ref.Kontakte")
Set tblContact = wbk.Sheets("Ref-Kontakt").ListObjects("tab.Ref.Kontakte")
Set rng = tblContact.ListColumns.Item(11).Range.Address

blnPersonsSelect = False


' Ermittlung, ob überhaut ein Eintrag aktivert ist.
For n = 0 To Me.lst_PersonInCharge.ListCount - 1
If Me.lst_PersonInCharge.Selected(n) = True Then
     blnPersonsSelect = True
     i = i + 1                                             ' Anzahl der Empfänger für das Array z. B.
     ReDim arrAddressee(i, 0)                             ' Array set to Addressee
     strAddressee = Me.lst_PersonInCharge.List(n)
     strEmailAddress = wsf.Index(tblContact, wsf.Match(strAddressee, rng, 0), 8)
                         'INDEX(tab.Ref.Kontakte,MATCH(""Miller, Manfred"",tab.Ref.Kontakte[NameAufgabe],0),8)
     arrAddressee(i, 0) = strEmailAddress
End If


Comment by: Jan Karel Pieterse (7-6-2016 13:48:22) deeplink to this comment

Hi Randolf,

I haven't studied your code entirely, but there is one problem I see:

Set rng = tblContact.ListColumns.Item(11).Range.Address


You are trying to store a string property into a range object. The proper syntax would be:

Set rng = tblContact.ListColumns(11).DataBodyRange


It might be better to use the column name, rather than the index number (11) here.


Comment by: Agni (10-6-2016 09:00:59) deeplink to this comment

How to create a table without a default auto filter and header.


Comment by: Jan Karel Pieterse (11-6-2016 16:08:30) deeplink to this comment

Hi Agni,

After creating, turn off headers and filters in the table settings ribbon?


Comment by: Jordan Lambert (27-6-2016 10:43:00) deeplink to this comment

Hi Jan,

Many thanks for this article.

I'm trying to have filters on tables auto-refresh when new data is entered? Tried a few 'solutions' without any success.

Any help would be much appreciated.

Thank you in advance.


Comment by: Jan Karel Pieterse (27-6-2016 17:41:14) deeplink to this comment

Hi Jordan,

I would probably add a change event to the worksheet in question and have it refresh the filter of the table.
The syntax for reapplying a listobjects filter is:

ActiveSheet.ListObjects("Table1").AutoFilter.ApplyFilter


Comment by: SACHIN (17-7-2016 11:29:23) deeplink to this comment

How to convert DD:H:MM format in To H:MM format
For Ex. 02:06:15(02 days, 06 houres, 15 min.)convert in Hours format


Comment by: Jan Karel Pieterse (17-7-2016 16:43:26) deeplink to this comment

Hi,

I would make sure there are some empty columns next to your data and use Data, Text to columns to split the data into columns by the : .


Comment by: envirodat (3-8-2016 00:42:17) deeplink to this comment

This was very helpful! Thanks for the great reference.


Comment by: Marc van Ginhoven (10-8-2016 09:29:42) deeplink to this comment

How can I use VBA to select the following in a table

- Column 11 - 18
- Not the header
- Not the 1st data row
- The 2nd data row untill the end of the table.

I already have code that works it's clunky.

The selection is needed to copy an insane amount of formulas and paste their values in the same table. The selection saves the formulas in the 1st data row so that they are saved and used in future table updates.

This greatly improves the pivottable sheet.




Comment by: Jan Karel Pieterse (10-8-2016 10:44:25) deeplink to this comment

Hi Marc,

This can be done using the structured formula syntax:

    Dim oRng As Range
    Set oRng = Range("table1[[col 11]:[col 18]]")
    Set oRng = oRng.Offset(1).Resize(oRng.Rows.Count - 1)
    oRng.Select


Alternative method:

    Dim oLo As ListObject
    Dim oRng As Range
    Set oLo = ActiveSheet.ListObjects(1)
    Set oRng = oLo.ListColumns(11).DataBodyRange.Resize(, 8)
    Set oRng = oRng.Offset(1).Resize(oRng.Rows.Count - 1)
    oRng.Select


Comment by: JohnBen (26-9-2016 11:12:57) deeplink to this comment

Thanks but how to copy all value from the table and copy to another table?

:)


Comment by: Jan Karel Pieterse (26-9-2016 18:00:27) deeplink to this comment

Hi JohnBen,

Copying the content of a table is simple:

Suppose you want to copy from Sheet1, Table1 to Sheet2, Table2:

Sub Demo()
Worksheets("Sheet2").ListObjects("Table2").DataBodyRange.Delete
Worksheets("Sheet2").ListObjects("Table2").ListRows.Add
Worksheets("Sheet1").ListObjects("Table1").DataBodyRange.Copy
Worksheets("Sheet2").Paste Worksheets("Sheet2").ListObjects("Table2").DataBodyRange.Cells(1,1)
End Sub


Comment by: ggv (11-10-2016 08:47:35) deeplink to this comment

How do I enter formula in a table from vba? Things that work on a plain spreadsheet do not work in tables. I.e. this doesn't work for me in vba:

Range("B3").Formula = "=C3+D3"

But I can enter this formula manually and it works.



Comment by: Jan Karel Pieterse (11-10-2016 09:30:47) deeplink to this comment

Hi ggv,

That routine does work and enters the formula into a cell, regardless whether that cell is inside a table or not.


Comment by: Robin (25-10-2016 20:17:22) deeplink to this comment

I am trying to compare differences between 2 cells ( Via a key- unique identifier) between 2 tables in 2 sheets. The sheet can have multiple columns

The formula works well (below)

=SUM(SUMIFS(Table1[Col1],Table1[Key],A4))-(SUMIFS(Table2[Col1],Table2[Key],A4))

A4 is the key(unique identifier connecting both tables)

For Col1, Cell B4 =formula is =SUM(SUMIFS(Table1[Col1],Table1[Key],A4))-(SUMIFS(Table2[Col1],Table2[Key],A4))

For Col2, cell C4 =formula is =SUM(SUMIFS(Table1[Col2],Table1[Key],A4))-(SUMIFS(Table2[Col2],Table2[Key],A4))


For Col3, cell C4 =formula is =SUM(SUMIFS(Table1[Col3],Table1[Key],A4))-(SUMIFS(Table2[Col3],Table2[Key],A4))

Formula works fine if I copy this manually but is there any way I can update the formula through VBA /Macro , I tried auto fill but then I am unable to keep Table1[Key] as fixed.
Do excel have something static e.g $A$2 like Table1[$Key]

Any thoughts ?


Comment by: Jan Karel Pieterse (26-10-2016 21:34:28) deeplink to this comment

Hi Robin,

There is a way to make a table reference absolute, but it is counter intuitive:

=[[Key]:[Key]]


Comment by: Robin (27-10-2016 19:30:40) deeplink to this comment

You are so awesome !! I want to hug you and cry !Thank you Jan. Let me know if you need anything from Toronto :)


Comment by: Jan Karel Pieterse (28-10-2016 12:12:35) deeplink to this comment

Hi Robin,

<blush> thanks!


Comment by: Simon (15-11-2016 01:54:07) deeplink to this comment

I just found an Excel bug working with tables and vba that drove me crazy for ages.
My issue was that I had the first two columns formatted as Australian date format - but when the workbook was saved using vba: Activeworkbook.SaveAs or Save, then closed, when reopened the table would lose that formatting when a new line was autocreated (for the new line - the date would be a random custom format)
It ONLY happened using vba. It NEVER happened when saving manually. And once the table was corrupted, the only way to fix it was to convert all tables to Ranges, and then re-create them. Don't ask me how many hours it took me to find the source of this problem.
An additional problem was copying and pasting the table (once corrupted) into a new workbook using vba and then saving the workbook would crash Excel every time.
A 'fix' for now that I am about to implement, is to use VBA to convert tables to range before the user saves, then recreate them in the next stage. Hope this helps someone


Comment by: Jan Karel Pieterse (15-11-2016 07:51:09) deeplink to this comment

Hi Simon,

That is odd. Can you perhaps email a copy of your file?


Comment by: Ian H (15-11-2016 12:32:46) deeplink to this comment

Hi, Great piece of work, I've been playing with this for hours!! Wondering if you can advise on a project I'm doing.

Basically I want to take data from table 1 on sheet 1 & table 2 on sheet 2 (Both columns are called "Names"), put all the data from the 2 tables into (Minus blanks) into table 3 on sheet 3 (Column "Names").

I can do it from 1 table but not the 2nd

Thanks in advance


Comment by: Ian H (16-11-2016 21:03:11) deeplink to this comment


Hi Jan, I managed to do what I wanted using "bits" of other code ...... But only partially with listobjects..... I would appreciate if you show me how to change this.

Sub Do_Sheets()

Dim oSh3 As Worksheet
Set oSh3 = Sheet3
Set wsSummary = Worksheets("Sheet3")


Application.EnableEvents = False


wsSummary.Select

With oSh3.ListObjects("Table3")
oSh3.Range("Table3[Name]").Select

    Selection.ClearContents


sheetlist = Array("Sheet1", "Sheet2")
For i = LBound(sheetlist) To UBound(sheetlist)
Worksheets(sheetlist(i)).Activate



'Code Goes here

Application.EnableEvents = False

'Select ranges
Range("A1").Select
Selection.CurrentRegion.Select

' Selects the current data area without the top row.
Selection.Offset(1, 0).Resize(Selection.Rows.Count - 1).Select




' Selects the visible cells of the selection.
' This is useful if data is filtered or contains hidden rows.
Selection.SpecialCells(xlVisible).Copy
wsSummary.Activate
Range("A1").Select

Do Until ActiveCell.Value = ""
ActiveCell.Offset(1, 0).Select


Loop
ActiveCell.PasteSpecial xlPasteAll



Range("A1").Select

Application.CutCopyMode = False

Application.EnableEvents = True


Next

End With



End Sub


Comment by: Jan Karel Pieterse (17-11-2016 11:50:51) deeplink to this comment

Hi Ian,

Something like this?

Sub CopyTheNames()
    Worksheets("Sheet1").ListObjects("Table1").ListColumns("Names").DataBodyRange.Copy
    With Worksheets("Sheet3")
        If Not Worksheets("Sheet3").ListObjects("Table3").InsertRowRange Is Nothing Then
            .Paste .ListObjects("Table3").InsertRowRange
        Else
            .Paste .Range("A" & .Rows.Count).End(xlUp).Offset(1)
        End If
    End With
    Worksheets("Sheet2").ListObjects("Table2").ListColumns("Names").DataBodyRange.Copy
    With Worksheets("Sheet3")
        .Paste .Range("A" & .Rows.Count).End(xlUp).Offset(1)
    End With
End Sub


Comment by: Ian H (21-11-2016 16:49:06) deeplink to this comment

Hi Jan, Thanks for you reply. Unfortunately your code pastes blanks making the table much bigger each time the code is run. But I can use some of it I think.

Thanks again


Comment by: Pascal (22-11-2016 15:22:46) deeplink to this comment

Hi Robin I ahve the following problem, I want to edit a table by first searching for a certain sample ID. I then want to edit that row of the table with other data like the 5000 as seen below. The address converter does not work properly, because it does not convert the adress to a listrow, but just a row, so that the 5000 appears in the wronr row. Could you please help me. I have been working on this for hours!

'Search
Set ref = Sheets("Tests Results").ListObjects("Test_results").ListColumns("#") 'reference to sheet, tablecolumn1
Set rgFound = Range("A4:A1000000").Find(SampleID.Value)        'Search for a SampleID WATCH OUT! This is static!
MsgBox "Row is " & rgFound.Address
Debug.Print rgFound.Address
'END Search


'ADRESS CONVERTER
Dim address1 As String, myrow As Long

address1 = rgFound.Address(0, 0)
myrow = Range(address1).Row
MsgBox "MyRow is " & myrow
'END ADRESS CONVERTER
'x = Rows(ActiveCell.Row).Select

Set Obj = Worksheets("Tests Results").ListObjects("Test_results")
Obj.ListColumns("Test Lab").DataBodyRange(myrow) = 5000


Comment by: Jan Karel Pieterse (22-11-2016 19:25:26) deeplink to this comment

Hi Pascal,

your variable myrow will contain the current row number, which is NOT the same as the Nth row of the databodyrange of the table.
If for instance your table starts on row 10, the databodyrange starts at row 11.
The way around that is by using the object variable rgFound you already have.

rgFound.Value = 5000


will set the value of the found cell to 5000

If you want to change a value of another column of the listobject, you can use the INtersect function:

Intersect(rgFound.EntireRow, Worksheets("Tests Results").ListObjects("Test_results").ListColumns("Test Lab").DataBodyRange).Value = "FooBar"


Comment by: Pascal (23-11-2016 09:37:39) deeplink to this comment

Wow very helpful thanks!


Comment by: ALEJANDRO HERNANDEZ NARANJO (6-12-2016 03:29:00) deeplink to this comment

please

I can't run this code:

Function CrearQueryTable(Conexion, Query, Hoja, Destino, Nombre, CommandType)
Sheets(Hoja).Select
With ActiveSheet.ListObjects.Add(SourceType:=0, Source:=Conexion, Destination:=Destino).QueryTable
        .CommandType = xlCmdSql
        .CommandText = Query
        .RowNumbers = False
        .FillAdjacentFormulas = False
        .PreserveFormatting = True
        .RefreshOnFileOpen = False
        .BackgroundQuery = True
        .RefreshStyle = xlInsertDeleteCells
        .SavePassword = False
        
        .SaveData = True
        .AdjustColumnWidth = True
        .RefreshPeriod = 0
        .PreserveColumnInfo = True
        .ListObject.Name = Nombre
        .Refresh BackgroundQuery:=False
    End With
End Function

anybody can help me

Im so desperate, im not a developer


Comment by: Paul John (6-12-2016 10:01:59) deeplink to this comment

Ian H

Try this (a mod of Jan's code)...

Sub CopyTheNames()

With Worksheets("Sheet1").ListObjects("Table1")

    .Range.AutoFilter Field:=1, Criteria1:="<>"

    .ListColumns("Names").DataBodyRange.SpecialCells(xlCellTypeVisible).Copy

End With



With Worksheets("Sheet3")

    If Not Worksheets("Sheet3").ListObjects("Table3").InsertRowRange Is Nothing Then

     .Paste .ListObjects("Table3").InsertRowRange

    Else

     .Paste .Range("A" & .Rows.Count).End(xlUp).Offset(1)

    End If

End With

With Worksheets("Sheet2").ListObjects("Table2")

    .Range.AutoFilter Field:=1, Criteria1:="<>"

    .ListColumns("Names").DataBodyRange.SpecialCells(xlCellTypeVisible).Copy

End With

With Worksheets("Sheet3")

    .Paste .Range("A" & .Rows.Count).End(xlUp).Offset(1)

End With

Worksheets("Sheet1").ListObjects("Table1").Range.AutoFilter

Worksheets("Sheet2").ListObjects("Table2").Range.AutoFilter

End Sub


Comment by: Jeff (31-12-2016 16:03:43) deeplink to this comment

Greetings Jan! I have successfully fount the code to add a new column and name it but I cannot get it to insert the formula without cheating like this:

        With ActiveSheet.ListObjects("Table1")
        .ListColumns.Add.Name = "LEN"
    End With
    Range("Table1[LEN]").Formula = "=LEN([@[Outstanding Docs]])"

What I am trying to achieve is something like this:


        With ActiveSheet.ListObjects("Table1")
        .ListColumns.Add.Name = "LEN"
        .ListColumns("[LEN]").Formula =LEN([@[Outstanding Docs]])" '<< this fails
    End With

but I can't find the proper proper syntax.

Many thanks and have a Happy New Year!


Comment by: Jan Karel Pieterse (31-12-2016 19:15:29) deeplink to this comment

Hi Jeff,

I think the correct syntax is:


.ListColumns("LEN").Formula ="LEN([@[Outstanding Docs]])"


Comment by: Jeff (31-12-2016 22:36:34) deeplink to this comment

.ListColumns("LEN").Formula ="LEN([@[Outstanding Docs]])" was one of the first things I tried. Unfortunately it generates a "Object doesn't support this property or method" error.


Comment by: Jan Karel Pieterse (1-1-2017 21:20:56) deeplink to this comment

Hi Jeff,

Apologies, it should've been

.ListColumns("LEN").databodyrange.Formula ="LEN([@[Outstanding Docs]])"


Comment by: zainul ulum (27-1-2017 02:42:19) deeplink to this comment

briefly clear explanation


Comment by: Milos (20-2-2017 14:23:16) deeplink to this comment

Hi.
What I wanna do is to select a row in a table and filter it contents based on the second third and fourth coloumn value.
I can't figure it out how to get the right code.
For any help or hint I would be deeply grateful.


Comment by: Jan Karel Pieterse (27-2-2017 10:03:54) deeplink to this comment

Hi Milos,

Have you tried recording a macro while setting up the filter?


Comment by: Milos (28-2-2017 21:19:50) deeplink to this comment

Hi Jan,
Yes I have. And when I chandged it into a code it did not work. I got empty rows as a result.

In the meantime I found out that the date was the culprit. Dates are tricky. Now it works.

Namely, for the date part of filtering I must use two criteria instead of one. The code is sth like this:


Var1 = Selection.Value
ActiveCell.Offset(0, 1).Select
Var2 = Selection.Value
ActiveCell.Offset(0, 3).Select
dDate = Selection.Value
dDate = DateSerial(Year(dDate), Month(dDate), Day(dDate))
lDate = dDate

ActiveSheet.ListObjects("clients").Range.AutoFilter Field:=1, Criteria1:=Var1
ActiveSheet.ListObjects("clients").Range.AutoFilter Field:=2, Criteria1:=Var2
ActiveSheet.ListObjects("clients").Range.AutoFilter Field:=5, Criteria1:=">=" & lDate, _
                                                Operator:=xlAnd, Criteria2:="<" & lDate + 1


Comment by: Jan Karel Pieterse (1-3-2017 10:03:16) deeplink to this comment

Hi Milos,

Great that you got it sorted!


Comment by: Ray (7-3-2017 22:48:38) deeplink to this comment

Great article on explaining tables and styles, but how do I access the data in the table?

For example, I want to read the set of values in Column 2 and based on the value, perform an action. Is it any easier to access and manipulate the cell contents if I turn the data into a table?


Comment by: Jan Karel Pieterse (8-3-2017 09:46:16) deeplink to this comment

Hi Ray,

It is easier becaus it is simple to always work with all rows/columns of the table, no need to use End(xlDown) and hope for the best (or start from row 1,000,000 and do end(xlUp)). To work with all cells of the column called "Column_X":

Dim oCell As Range
For Each oCell in Worksheets("Sheet1").ListObjects("Table1").ListColumns("Column_X").DataBodyRange

The only snag is that the table might be empty, causing a runtime error. To avoid that you first count the number of ListRows:
If Worksheets("Sheet1").ListObjects("Table1").ListRows.Count>0 Then
'Remainder of Code
End If


Comment by: Namrata Lohia (16-3-2017 12:01:41) deeplink to this comment

hi, i have to write a macro code for creating a table with user defined number of rows and columns.


Comment by: John Dunsmuir (20-5-2017 17:44:19) deeplink to this comment

Is there a method or event to detect when a user has deleted the last row of a table? I'm currently using worksheet_change event and the intersect method but when deleting a row the event target is outside the table range and does not intersect. A workaround may be to track the table size but I'm looking for something less clunky.
Thanks


Comment by: Jan Karel Pieterse (22-5-2017 09:52:33) deeplink to this comment

Hi John,

This seems to work:

Private Sub Worksheet_Change(ByVal Target As Range)
    If Not Target.Offset(-1).ListObject Is Nothing Or _
     Not Target.ListObject Is Nothing Then
        If Application.CommandBars("Standard").FindControl(ID:=128, recursive:=True).List(1) = "Delete Row" Then
            MsgBox "Deleted a row in a table"
        End If
    End If
End Sub


Comment by: akis tzortzis (29-5-2017 16:58:19) deeplink to this comment

After you have added a totals row, how do you then access individual columns' totals (from VBA) ?


Comment by: Jan Karel Pieterse (29-5-2017 17:42:32) deeplink to this comment

Hi Akis,

This gets you the entire row with totals:

ActiveSheet.ListObjects("Table1").TotalsRowRange


Or, to get the total cell's value of a specific column:

ActiveSheet.ListObjects("Table1").ListColumns("ColumnCaption").Total.Value


Comment by: Fadas (10-6-2017 07:57:59) deeplink to this comment

hi
first sorry me for awful english
i have a table in excel, and I want change validation massage of for example cells in column 2 & 3
when may table have one data row it work, but when it have tow or three data row it dosnt work
i have a code like this


If tbl.ListRows.Count = 1 And ActiveWorkbook.Worksheets(SH_NUM).CodeName = Sh.Cells(Z, Col) And Sh.Cells(Z, 3) = tbl.HeaderRowRange(1, I).Address Then
If Sh.Cells(Z, 6) = "VALT" Then
tbl.DataBodyRange.Cells(1, I).Validation.ErrorTitle = Sh.Cells(Z, Col + 3)
Else
If Sh.Cells(Z, 6) = "VALM" Then
tbl.DataBodyRange.Cells(1, I).Validation.ErrorMessage = Sh.Cells(Z, Col + 3)
End If
End If
End If


Comment by: Fadas (10-6-2017 08:01:43) deeplink to this comment

sorry me
code is this:


If ActiveWorkbook.Worksheets(SH_NUM).CodeName = Sh.Cells(Z, Col) And Sh.Cells(Z, 3) = tbl.HeaderRowRange(1, I).Address Then
If Sh.Cells(Z, 6) = "VALT" Then
tbl.DataBodyRange.Cells(1, I).Validation.ErrorTitle = Sh.Cells(Z, Col + 3)
Else
If Sh.Cells(Z, 6) = "VALM" Then
tbl.DataBodyRange.Cells(1, I).Validation.ErrorMessage = Sh.Cells(Z, Col + 3)
End If
End If
End If



it dosnt work, why?


Comment by: Jan Karel Pieterse (11-6-2017 19:25:37) deeplink to this comment

Hi Fadas,

If you record a macro changing the validation of a cell which already has an existing validation rule you will see why your code fails; you have to delete the validation rule and add it back again.


Comment by: Gajendra kumar (20-6-2017 06:56:46) deeplink to this comment

in microsoft excel 2007 created database table record in only field
how can make disable field cell


Comment by: Jan Karel Pieterse (20-6-2017 10:02:56) deeplink to this comment

Hi Gajendra,

I'm afraid I don't understand your question, can you please try to rephrase it?


Comment by: Jonathan (21-6-2017 21:41:27) deeplink to this comment

I want to set a table range to a dynamic Named Range

Table is "Data_Table"
Work Sheet is "Data"
Named Range is "ALL_Data" = Data!$D$2:INDIRECT("K" & Data!$M$4)

I want the table to constantly resize

Please Help....


Comment by: Jan Karel Pieterse (22-6-2017 09:49:47) deeplink to this comment

Hi Jonathan,

If you define your range name and simply point it to the entire table everything is handled by Excel, no need for dynamic range names anymore.


Comment by: Jonathan (22-6-2017 13:48:34) deeplink to this comment

The data in the table is created by formula
I have 1000 rows but most are blank
using =IFERROR (***,****,"")

I need the table to resize to contain the non blank entries.

I was hoping to use VBA to change the table range using the result of a count

Any Ideas....


Comment by: Jan Karel Pieterse (22-6-2017 14:00:03) deeplink to this comment

Hi Jonathan,

In that case I'm afraid I am not sure I follow your intentions.

Perhaps better to ask your question at www.eileenslounge.com where you can attach a sample workbook to your question to clarify things.


Comment by: Paul (27-6-2017 02:50:27) deeplink to this comment

Hi Jan Karel

I have a macro which converts a range into a table, then adds a column to it. I am also trying to insert a formula into the new column, but keep getting an error message.

I'm hoping you could help me understand why please.

The error is "Runtime error 91. Object Variable or With block variable not set".

Note - this seems to occur when my new table has no data rows in it; it seems to run OK when I have data rows in the table.

If I go into Debug, I can put the formula in the new column's cell without an issue.

I've checked the formula parameter names, and they are all OK.

My code is:


Sub Make_Actions_table()
'---------------------
Dim colSrce     As ListColumn

'NB - the following variables have been declared at Project level

Set shtSrce = Sheet31
Set rngSrce = shtSrce.Range("A1").CurrentRegion
Set tblSrce = shtSrce.ListObjects.Add(xlSrcRange, rngSrce, , xlYes)

tblSrce.Name = "ActionsData"

Set colSrce = tblSrce.ListColumns.Add
colSrce.Name = "Overdue"

colSrce.DataBodyRange.FormulaR1C1 = _
    "=IF(AND(dteReportMonth > [@ActionNextDue], [@ActionLastClosed]="""")," & _
     """Y"", ""N"")"
end sub


Many thanks, Paul


Comment by: Jan Karel Pieterse (27-6-2017 10:13:35) deeplink to this comment

Hi Paul,

You cannot add a formula to a table without any data, so the trick is to temporarily add a row. I also declared the variables in your code which did not have a Dim statement and removed the sheet variable and used Sheet31 instead.

Sub Make_Actions_table()
'---------------------
    Dim colSrce As ListColumn
    Dim tblSrce As ListObject
    Dim rngSrce As Range
    
    Dim bAddRow As Boolean
    'NB - the following variables have been declared at Project level

    Set rngSrce = Sheet31.Range("A1").CurrentRegion
    Set tblSrce = Sheet31.ListObjects.Add(xlSrcRange, rngSrce, , xlYes)

    tblSrce.Name = "ActionsData"

    Set colSrce = tblSrce.ListColumns.Add
    colSrce.Name = "Overdue"
    If tblSrce.ListRows.Count = 0 Then
        bAddRow = True
        tblSrce.ListRows.Add
    End If
    colSrce.DataBodyRange.FormulaR1C1 = _
    "=IF(AND(dteReportMonth > [@ActionNextDue], [@ActionLastClosed]="""")," & _
                                        """Y"", ""N"")"
    If bAddRow Then
        tblSrce.DataBodyRange.Delete
    End If
End Sub


Comment by: karen (17-8-2017 00:19:06) deeplink to this comment

Quote requests come in and I record them in a table.
Based on the status cell, I need the rows to move to the appropriate table on a different sheet
I am not great at VBA - know just enough to be dangerous.
I've been able to move an entire row to the correct sheet but it pastes the data BELOW the actual table instead of inserting a new table row and pasting the data.
To keep it simple here are my columns
Date, Name, ID, Status
Here are my sheet/table names:
Quote, FollowUp, Awarded, Lost
This can get very large very quick so I'd prefer a simple code that didn't eat up resources.
Any help is GREATLY appreciated.


Comment by: Jan Karel Pieterse (17-8-2017 11:55:18) deeplink to this comment

Hi Karen,

Instead of moving quotes to a new table each time their status changes, why not use one table and add a status column to it which you update?


Comment by: karen (17-8-2017 20:29:47) deeplink to this comment

I need separate tables for different departments to work with. There would be thousands of projects in one table and we need to have them separate


Comment by: Jan Karel Pieterse (18-8-2017 15:01:49) deeplink to this comment

What about adding a slicer on the new status column and one on the dept column, that way people can easily filter. An alternative is to create pivot tables from the main table and simply refresh them. That way you can keep input on one sheet and use the PTs as display per dept/status.


Comment by: Karen (18-8-2017 22:33:45) deeplink to this comment

I don't understand. Is there a problem with moving table rows around? It sounds like there isn't a way to do it because you keep trying to suggest other things. Is that the case?


Comment by: Jan Karel Pieterse (21-8-2017 07:00:10) deeplink to this comment

Hi Karen,

No of course not, I was just suggesting to reconsider your design as it might make things easier in a lot of aspects.

This routine copies the current row to the appropriate sheet:

Sub MoveQuote(oCell As Range)
    Dim oSourceLo As ListObject
    Dim oTargetLo As ListObject
    Set oSourceLo = Worksheets("Quote").ListObjects("Quote")
    Set oTargetLo = Worksheets(oCell.Value).ListObjects(oCell.Value)
    With oTargetLo.ListRows.Add
        Intersect(oSourceLo.DataBodyRange, oCell.EntireRow).Copy
        .Range.Cells(1, 1).PasteSpecial xlPasteValuesAndNumberFormats
    End With
End Sub


And if you add this to the worksheet module of sheet "Quote" it works automatically:

Private Sub Worksheet_Change(ByVal Target As Range)
    If Intersect(Target, Me.ListObjects("Quote").ListColumns("Status").Range) Is Nothing Then Exit Sub
    MoveQuote Target.Cells(1, 1)
End Sub

Mind you, this copies a line as a new line everytime the status column is changed. There is no check if that quote was copied before, not to the same status sheet, nor to another one. You end up with that quote on every status tab which you have chosen to enter in the Status column. To avoid that you also need code that removes the line from all status sheets before copying.


Comment by: Tim (22-9-2017 07:24:23) deeplink to this comment

Hi Karen
This has been very helpful, however it still lacks one element that I have failed to track down anywhere....
How does one COPY the formulae from one ListColumn DataBodyRange to another ListColumn DataBodyRange in the same Table.
I am trying to copy the formula from the first column into a newly added column. I can copy the first column....
eg


Dim oSh As Worksheet
Set oSh = ActiveSheet
Dim oLc As ListColumn
Set oLc = ActiveSheet.ListObjects("Table1").ListColumns.Add
ActiveSheet.ListObjects("Table1").ListColumns(1).DataBodyRange.Copy


But how do I paste the copied formulae into the newly added column?
Any suggestions would be extremely welcome.


Comment by: Jan Karel Pieterse (22-9-2017 15:48:20) deeplink to this comment

Hi Tim,

YOu only have to copy the first cell of the column to the first cell of the other. In fact, no need to use Copy at all, just set the formula. This "copies" the formula from column 2 to the last one:

    Set oLo = ActiveCell.ListObject
    oLo.ListColumns(oLo.ListColumns.Count).DataBodyRange.Cells(1, 1).Formula = oLo.ListColumns(2).DataBodyRange.Cells(1, 1).Formula


Comment by: JAZIA (3-10-2017 15:12:44) deeplink to this comment

I have a template
1. the template has a vlookup that uses the data table from another sheet called "datadealer" the table called "DealerDataTab"
2. I wrote a code on open workbook to make a table called "DealerDataTab" on "datadealer" sheet
3. as long as I used the data table on open workbook it looks like excel supposed the "DealerDataTab" table is already there and automatically created a data range with the name "DealerDataTab_1",
4. I don't want excel to suppose that the data table is already created when it reads the "DealerDataTab" in the formula.
5. how to do that?


Comment by: Jan Karel Pieterse (3-10-2017 16:52:12) deeplink to this comment

Hi Jazia,

I'm not sure I understand your problem. Can you share a part of the code?


Comment by: JAZIA (3-10-2017 17:10:53) deeplink to this comment

Thank you Jan
the following code should create the "DealerDataTab" data table, as long as I used the same data table name in my template formula if I open the workbook Excel supposed that data table is already there so it creates the same data range with _1 I don't want excel to suppose that the data range is already there because it's used in the formula, I want it instead to create the data table based on my code then I want it to make the calculations on template:

Sub MKTableDData()

Dim ws As Worksheet
Dim ob As ListObject
Dim Lrow1 As Long

    Range("A531:C531").Select
    ActiveSheet.ListObjects.Add(xlSrcRange, Range("$A$531:$C$531"), , xlNo).Name = "DealerDataTab"
    Stop
Lrow1 = Sheets("DealerData").Cells(Rows.Count, "C").End(xlUp).Row
Set ws = ActiveWorkbook.Worksheets("DealerData")
Set ob = ws.ListObjects("DealerDataTab")

ob.Resize ob.Range.Resize(Lrow1)
ActiveSheet.ListObjects("DealerDataTab").Resize Range("$A$531:Lrow1")

End Sub


Comment by: Jan Karel Pieterse (4-10-2017 11:18:12) deeplink to this comment

Hi Jazia,

So am I correct that you would like the code to:

- Check if the listObject is already there
- If not, create it
- If it is there, use it?

To test if it is already there:

Dim oLo as ListObject
On Error Resume Next
Set oLo = ActiveSheet.ListObjects("DealerDataTab")
On Error Goto 0
If oLo Is Nothing Then
    'Table wasn't there yet, create it
Else
    'Table is already present, use the oLo variable to manipulate it
End If


Comment by: JAZIA (4-10-2017 14:25:39) deeplink to this comment

Excel supposed the table is there because I am using it in a formula at the template, so actually as long as I used the table name in the template excel supposed it is there, I am not sure if there is anyway to make excel create the table with the same name that used in the template formula


Comment by: Jan Karel Pieterse (4-10-2017 15:26:55) deeplink to this comment

No, creating a table with a name already present in a workbook is not allowed. You could replace the content of the table however.


Comment by: JAZIA (4-10-2017 19:02:24) deeplink to this comment

Thank you for helping,
it looks like I didn't explain it correctly, I know that I will never be able to create two data tables with same name, the question is: is there anyway to execute the vba code before reading the sheets formulas by Excel?


Comment by: Jan Karel Pieterse (5-10-2017 07:35:12) deeplink to this comment

Hi Jazia,

No that isn't possible. And adding a new table and removing the existing one will wreck your formulas. You will have to update the existing table.


Comment by: JAZIA (5-10-2017 14:41:50) deeplink to this comment

Thank you for your help, I will try that.


Comment by: Jake Burns (9-11-2017 01:54:13) deeplink to this comment

Hey there, this page is so helpful! I have looked at it so many times, over and over again. Thank you for sharing so much knowledge! Jake


Comment by: bahmani.851402302@gmail.com (17-11-2017 18:47:26) deeplink to this comment

hi this is nima. I have a question
How can delete all rows of tables,without deleteing headers and resize table to first row.thanks alot


Comment by: Jan Karel Pieterse (17-11-2017 20:51:51) deeplink to this comment

Hi Nima,

Worksheets("Sheet1").ListObjects("Table1").DataBodyRange.Delete


Comment by: Mario Diaz (7-12-2017 16:29:54) deeplink to this comment

Hi, Thanks for sharing your knowledge, is there a way in VB to look for a Raw within a table, using the data? Like a select statement in SQL.


Comment by: Jan Karel Pieterse (7-12-2017 22:42:50) deeplink to this comment

Hi Mario,

You can use the MATCH worksjheetfunction from within VBA to find the matching row number:
Dim lRow As Long

lRow = Application.WorksheetFunction.Match("WhatYouWantToFind", ActiveSheet.ListObjects(1).ListColumns("ColumnA").DataBodyRange, 0)


Comment by: Damian (8-12-2017 01:22:55) deeplink to this comment

Hi Jan,
I'm refreshing a table from sql server and have the following set
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
Application.AutoCorrect.AutoFillFormulasInLists = False

When the table is refreshed with the sql data I'm getting a warning running slicer operation. There are approx 21,000 row in the table, with 8 slicers attached.
I only want the slicers refreshed at the end of the load process,once all the data is loaded. Any thoughts?
Regards
Damian


Comment by: Jan Karel Pieterse (12-12-2017 14:48:31) deeplink to this comment

Hi Damian,

Perhaps removing the filter prior to reefreshing helps?


Comment by: Damian (15-12-2017 03:29:04) deeplink to this comment

Hi Jan
Perfect. Refresh time went down by over 60% with the filters off
Thanks Damian


Comment by: Dars (30-1-2018 14:57:08) deeplink to this comment

How to create table in excel and insert the data of exported data in vb


Comment by: Jan Karel Pieterse (30-1-2018 17:15:32) deeplink to this comment

Hi Dars,

What is the source of that data? Perhaps Excel can pull the data in using the Data tab of the ribbon?


Comment by: Jeff Carnohan (14-3-2018 05:25:43) deeplink to this comment

Hi Jan,

I receive a number of text files (csv) each day with no headers/column labels which I import into a master table

The first 38 columns are static and I know what the headers are so no problem. The data in Columns 39-50, however, can vary in order and number between 20 or so possible headers. Sometimes I get one additional column and sometimes a dozen. I would like to make a script that tests the data in these columns and then apply the column label.

Example, several of the columns contain phone numbers. So I would like to apply labels "Phone1", "Phone2" ... when the average cell length (non-blank) is 10 digits. Since the data is a little dirty, I would like to apply it if the average length is between 9.2 and 10.2. I have the following array formula that gives me the avg length of the cells but I don't know how to use it to change the name of the label.

Avg. LEN
=AVERAGE(IF(MainTable[Column label]<>0,LEN(MainTable[Column label])))

Note: the columns will already have an existing name, it may be right or wrong. I have the following formula to get the name of the selected column.

strCurrentColName = Cells(Columns.ListObject.Range.Column, ActiveCell.Column).Offset(2, 0).Value

I'm getting close but can't quite tie it altogether. Any assistance greatly appreciated! regards, Jeff


Comment by: Jeff Carnohan (14-3-2018 05:37:31) deeplink to this comment

Hi Jan, I should have converted that avg length formula above to vba notaion similar to this:

Range("A1").FormulaArray = _ WorksheetFunction.Round(WorksheetFunction.Average(Range("Activetable[Colun label]")), 2)


Comment by: Jan Karel Pieterse (14-3-2018 09:15:24) deeplink to this comment

Hi Jeff,

Unfortunately, you cannot have formulas in the header of a table.
You could indeed use a formula like the one you posted to test for content of the column. If you have that formula either return "Phone" if the average is as expected for a phone number, or the existing column name if not you can simply copy that row of formulas and paste-special it on top of the table header row.


Comment by: Ben Todres (14-3-2018 16:00:00) deeplink to this comment

I have an excel file with 62 individual tabs, on 56 of those tabs there is one table. I want to change each table name based on a list (cells: A1:A56) one one of the other tabs in sequential order using VBA.

thx in advance for any guidance you may have (I have searched many sites trying to find a solution but have been unsuccessful)!!


Comment by: Jan Karel Pieterse (14-3-2018 16:10:00) deeplink to this comment

Hi Ben,

Assuming your list has two columns:
Col A containing the worksheet names
Col B containing the associated new table names
And assuming each sheet has no more than one table:
- Select sheet with names
- Select cells in column A with sheet names
- Run this macro:

Sub RenameAllTables()
    Dim oCell As Range
    For Each oCell in Selection
        Worksheets(oCell.Value).ListObjects(1).Name = oCell.Offset(,1).Value
    Next
End Sub


Comment by: Ben Todres (14-3-2018 17:13:46) deeplink to this comment

Jan - thank you sooooo much! - I have been trying for days to come up with what you just provided - extremely helpful!


Comment by: Jan Karel Pieterse (14-3-2018 17:28:03) deeplink to this comment

Hi Ben,

Just as a FJI: questions like this one are often quickly addressed by posting a question at www.eileenslounge.com


Comment by: Jeff Carnohan (18-3-2018 17:05:36) deeplink to this comment

Hi Jan, thank you for replying. I must not have been clear in my question. I am not trying to place a formula in the column header, I am trying to use VBA to write the column headers based on the contents of the column. For example, if the column contains phone numbers, label it 'Phone1', if it contains 4-6 digit alpha sequences, label it 'Product codes', 16 digit numerics would be 'Credit card number' and so on. Basically a series of IF statements that looks at the column data and then writes/inserts the headers from a list of 20-30 pre-defined possible headers. Thanks again!

https://jkp-ads.com/Articles/Excel2007TablesVBA.asp?AllComments=True#26100


Comment by: Jan Karel Pieterse (19-3-2018 10:29:38) deeplink to this comment

Hi Jeff,

Perhaps code like this helps you to get going?

Option Explicit

Sub UpdateTableHeaders()
    Dim oLo As ListObject
    Dim oCell As Range
    Dim lColCt As Long
    Set oLo = ActiveSheet.ListObjects("Table1")
    If oLo.ListRows.Count = 0 Then Exit Sub
    For lColCt = 1 To oLo.ListColumns.Count
        Select Case ContentType(oLo.ListColumns(lColCt).DataBodyRange.Value)
        Case "Phone"
            oLo.HeaderRowRange.Cells(1, lColCt).Value = "Phone"
            'Excel will automatically add suffixes to the names to avoid duplicates
        Case "Text"
            'Do something smart here
        Case Else
        End Select
    Next
End Sub

Function ContentType(vData As Variant) As String
    Dim lRow As Long
    Dim lFoundTxt As Long
    Dim lFoundNum As Long
    Dim lFound10Digits As Long
    For lRow = LBound(vData, 1) To UBound(vData, 1)
        If IsNumeric(vData(lRow, 1)) Then
            lFoundNum = lFoundNum + 1
            If Len(vData(lRow, 1)) >= 9 And Len(vData(lRow, 1)) <= 10 Then
                lFound10Digits = lFound10Digits + 1
            End If
        Else
            lFoundTxt = lFoundTxt + 1
        End If
    Next
    If lFound10Digits = lFoundNum And lFoundTxt = 0 Then
        ContentType = "Phone"
    ElseIf lFoundNum = 0 And lFoundTxt > 0 Then
        ContentType = "Text"
    Else
        ContentType = "Mixed"
    End If
End Function


Comment by: Jeff Carnohan (20-3-2018 18:55:25) deeplink to this comment

This is fantastic Jan!! I'll let you know how it works out! Thank you so much :)


Comment by: Jason (23-3-2018 17:55:49) deeplink to this comment

Hello

I am trying to figure out the code needed to paste values data from 1 workbook into a table in another workbook. My current macro runs perfectly but when the data is pasted into the next row below the table the table does not automatically expand to include the new data. Essentially my code looks for the next blank row of the table worksheet and paste/values the data into it. Do you have a solution that will make the table expand to include the new data?

Any help/advice you have is greatly appreciated.

Thank you


Comment by: Jan Karel Pieterse (26-3-2018 10:42:23) deeplink to this comment

Hi Jason,

I expect your code doesn't actually paste into the blank row immediately beneath the table but rather one row below that. You could force the paste into a newly inserted row at the bottom of the table like so:

Sub Demo()
    PasteAdd2Table ActiveSheet.ListObjects(2).DataBodyRange, ActiveSheet.ListObjects(1)
End Sub

Sub PasteAdd2Table(oRng2Copy As Range, oTargetTable As ListObject)
    With oTargetTable.ListRows.Add
        oRng2Copy.Copy
        .Range.Cells(1, 1).PasteSpecial xlPasteValuesAndNumberFormats
    End With
End Sub


Comment by: M. Amir Ashraf (3-4-2018 21:38:11) deeplink to this comment

Hi Jan,
I just stumbled on to your page a couple of days back. Its extremely helpful for a novice like me. Well, I'll jump right to my question. Your last example is exactly what I've been struggling with. Can you please explain ListObjects(2) and ListObjects(1). Do they refer to 2 tables on the same sheet? Are the numbers assigned automatically? How do I know which table is linked to which ListObjects?

Thanks a million in advance


Comment by: Jan Karel Pieterse (4-4-2018 11:18:31) deeplink to this comment

Hi M. Amir Ashraf,

Yes 1 and 2 are two listobjects on the same worksheet. They are numbered in order of creation. To avoid ambiguity you can also refer to them by their names, which you can see on the Tables contextual tab of the ribbon if you select a table (far left of the ribbon):

Activesheet.ListObjects("Table1")


Comment by: M. Amir Ashraf (4-4-2018 15:03:02) deeplink to this comment

Hi Jan,

Thanks for the prompt response. The tables I'm working with are on different sheets. How do I incorporate that into your example? I tried replacing Activesheet with the sheet name and replacing number with the table name

PasteAdd2Table ActiveSheet.ListObjects("tblCustBill").DataBodyRange, _
    Worksheets("InventoryMovement").ListObjects("tblInventoryMovement")

but this gave the error script out of range.

Please advise.

Thanks again







Comment by: Jan Karel Pieterse (5-4-2018 07:48:22) deeplink to this comment

Hi M. Amir Ashraf,

I expect the table called "tblCustBill" is not on the activesheet like your code suggests?


Comment by: M. Amir Ashraf (5-4-2018 14:18:16) deeplink to this comment

Hi Jan,

Thanks again and again. You were right. I now have it running.

Best wishes


Comment by: M. Amir Ashraf (5-4-2018 21:12:42) deeplink to this comment

Hi Jan,
Thanks for all the help. Here's another one. Is there a way to hide the header row - i.e. column names - of a table?


Comment by: Jan Karel Pieterse (9-4-2018 08:45:06) deeplink to this comment

Hi M. Amir Ashraf,

Yes there is. Record a macro while you toggle that setting for a table


Comment by: Dale (17-5-2018 16:55:50) deeplink to this comment

I have setup an Excel Table for manual data entry. The table has a few thousand blank rows for entering data throughout a year’s time. I would like to know the VBA code to find and take the user to the first empty row within the table (as oppossed to scrolling down). The only code I know takes the user to the first row below the table. This is not what I want to do.


Comment by: Jan Karel Pieterse (17-5-2018 17:44:31) deeplink to this comment

Hi Dale,

I would not have empty rows in the table (as that is not needed at all), but rather use code which adds a row and then takes the user there:

    With ActiveSheet.ListObjects(1).ListRows.Add
        Application.Goto .Range.Cells(1, 1)
    End With


Comment by: Ipsit (20-9-2018 13:57:44) deeplink to this comment

Hi,

Please can someone share the VBA code required to update a specific cell in an Excel Table.

I have a Row number and I want to go to that row in a table and update a specific column

Regards
Ipsit


Comment by: Jan Karel Pieterse (20-9-2018 16:29:46) deeplink to this comment

Hi Ipsit,

Suppose your table is on Worksheet "Sheet1" and the table is called "Table1". Also suppose you need to access row 24 of a column called "TheColumn". This single line of code writes 123 into that cell:

Worksheets("Sheet1").ListObjects("Table1").ListColumns("TheColumn").DataBodyRange.Cells(24, 1).Value = 123


Comment by: Areeba Amjad (27-9-2018 13:15:57) deeplink to this comment

Aoa,
How can I give the specific table reference in VBA for input my data


Comment by: Jan Karel Pieterse (27-9-2018 15:35:14) deeplink to this comment

Hi Areeba,

It depends on what precsiely you need to achieve. Can you please try to describe what you need to do?


Comment by: Hermes (14-12-2018 11:02:31) deeplink to this comment

Public Function mi(ByVal srcData As Variant) As String
Dim wsSummary As Worksheet
Set wsSummary = Worksheets("Tbl_Sniffer_Review")
Dim inRange As Integer
Dim LO As ListObject
Set LO = Application.Range("ReviewSniffer").ListObject
inRange = --((LO.DataBodyRange.Columns(4) <= srcData) * (LO.DataBodyRange.ListColumns(5) >= srcData))
If inRange = 1 Then
mi = WorksheetFunction.SumProduct(inRange, LO.ListRows.Count) - WorksheetFunction.Row(LO.HeaderRowRange.Count)
mi = "Not Found"
End If
End Function

I did not manage to work correctly. It always returns zero, or the program freezes.
Thanks for being here


Comment by: Jan Karel Pieterse (14-12-2018 15:36:48) deeplink to this comment

Hi Hermes,

What is that function supposed to do precisely?


Comment by: Hermes Rozsa (14-12-2018 21:29:14) deeplink to this comment

The problem that I present is that I can not adress the Star and End columns of the "ReviewSniffer" table. I need to compare the value (Date) of the srcData variable with each column to find out if it is within some of the rows row of the table [Star (1), End (1); ...; Star (n), End (n)

all the data has the format "dd/mm/yyyy hh:mm:ss"
In a worksheet this is the function:


=IF(SUMPRODUCT(--(ReviewSniffer[Star]<=srcData)*(ReviewSniffer[End]>=srcData))=1,"Row no. "&SUMPRODUCT(--(ReviewSniffer[Star]<=srcData)*(ReviewSniffer[End]>=srcData),ROW(ReviewSniffer))-ROW($G$2),"Not Found")

$G$ 2 is where the header of the ReviewSniffer table is located

I have tried to reproduce in the VBA code some of the ideas here exposed on how to access the data of the objectList table and without result


Comment by: Jan Karel Pieterse (17-12-2018 07:55:40) deeplink to this comment

Hi Hermes,

What precisely do you want the function to return as its result? You could do somthing like this:

Dim vReviewSniffer_Star as Variant
Dim vReviewSniffer_End as Variant
Dim lRow As Long
vReviewSniffer_Star = Worksheets("YourSHeetNameGoesHere").ListObjects("ReviewSniffer").ListColumns("Star").DataBodyRange.Value
vReviewSniffer_End = Worksheets("YourSHeetNameGoesHere").ListObjects("ReviewSniffer").ListColumns("End").DataBodyRange.Value
'Now you can loop through these two varian arrays:
For lRow = LBound(vReviewSniffer_Star,1) To Ubound(vReviewSniffer_Star,1)
    If vReviewSniffer_Star(lRow,1) <= srcData And vReviewSniffer_End(lRow,1) >= srcData Then
'Found a match, do something like record the row variable lRow
Next


Comment by: JCabral (22-1-2019 14:02:01) deeplink to this comment

How can I use Find in a Table to give me the row number of a certain value in that table, i.e. I want to know the number of the table row where a certain value is using FIND method


Comment by: Jan Karel Pieterse (2-2-2019 16:20:31) deeplink to this comment

Hi J,

Like so:

    ActiveSheet.ListObjects(1).ListColumns(1).DataBodyRange.Find(What:="2", LookIn:=xlValues, LookAt:= _
        xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
        , SearchFormat:=False).row


Comment by: Luis (8-4-2019) deeplink to this comment

Hi Jan.
Yes I know :). But I thought it was possible to get the same result that we see in "refers to" on Names table.

Thanks for your attention.

Luis.


Comment by: Luis (8-4-2019) deeplink to this comment

Hi Jan,
When we use ".range.address" we only get the cells address of the table but not the sheet name where is the table.
How we can get the sheet name like in "refers to".
Many thanks in advance for your help.
Luis


Comment by: Jan Karel Pieterse (8-4-2019) deeplink to this comment

Hi Luis,

The worksheet is the direct parent of both the range object and the ListObject, so suppose you already have a cell ref to an object named TheCell or a reference to a ListObject named TheList, you would use:

TheCell.Parent
or
TheList.Parent


Comment by: Luis (8-4-2019) deeplink to this comment

Hi, Jan,
Thank you for your quick reply.
Already tried to use .parent many times but I always get an error.
Please tell me how to insert this command in this code:

Sub ListaTabelas()
    Dim tabela As ListObject
    Dim MyWs As Worksheet
    Dim i As Single, j As Single
    
    Set MyWs = Sheets.Add
    i = 1
    For Each MyWs In Worksheets
        For Each tabela In MyWs.ListObjects
            Range("A1").Cells(i, 1).Value = tabela.Name
            Range("A1").Cells(i, 2).Value = tabela.Range.Address
            i = i + 1
        Next tabela
    Next MyWs
End Sub

Thanks for your patience with me.

Luis


Comment by: Jan Karel Pieterse (8-4-2019) deeplink to this comment

Hi Luis,

You already have a reference to the worksheet, it is your MyWs variable (the loop variable) :-)


Comment by: Jan Karel Pieterse (10-4-2019) deeplink to this comment

Hi Luis,

Well, the parent object of a listobject is the worksheet, so:

ActiveCell.ListObject.Parent.Name


gives the name of the sheet the listobject is on.


Comment by: Nidal (27-4-2019 00:46:00) deeplink to this comment

Hi,
I have a data table in excel , i need a macro to activate an imbedded chart based on column selection (y-axis), x-axis will be the first column of my data table?
Thanks


Comment by: Jan Karel Pieterse (29-4-2019 17:13:00) deeplink to this comment

Hi Nidal,

I would like to suggest you ask this question on https://www.eileenslounge.com


Comment by: William (29-6-2019 01:09:00) deeplink to this comment

Hi Jan,

Your efforts here are truly heroic, so I hope you are able to help me too... :)

I'm attempting to add multiple rows to the end of a table using the ListObject DataBodyRange method.

I know that I can add a single row with:

MyTable.ListRows.Add


And that I can delete multiple rows with something like:

MyTable.DataBodyRange.Offset(1,0).Resize(MyTable.DataBodyRange.Rows.Count - 1, MyTable.DataBodyRange.Columns.Count).Rows.Delete


So I was hoping to use something similar to add multiple rows, for example:

MyTable.DataBodyRange.Resize(MyTable.DataBodyRange.Rows.Count + 10, MyTable.DataBodyRange.Columns.Count).Rows.Add


BUT, there is no .Add method available to use and this code fails, so I was wondering if there was an elegant one-liner that would do this?

Many thanks in advance,
Will


Comment by: Jan Karel Pieterse (1-7-2019 11:03:00) deeplink to this comment

Hi William,

Sure, the trick is to use the resize method of the ListObject:

Option Explicit

Function AddRows2Table(Lo As ListObject, NumRows As Long, bPrompt As Boolean) As Range
'Adds new listrows to a listobject (Table), at the bottom
    Dim CurrentTableRng As Range
    Dim bDo As Boolean
    Set CurrentTableRng = Lo.Range
    If bPrompt Then
    Lo.Range.Offset(Lo.ListRows.Count + 1).Resize(NumRows).Select
        If Application.CountA(Lo.Range.Offset(Lo.ListRows.Count + 1).Resize(NumRows).Value) > 0 Then
            bDo = MsgBox("Rows below table contain data, continue?", vbYesNo + vbQuestion, "Adding rows to a table") = vbYes
        End If
    Else
        bDo = True
    End If
    If bDo Then
        Set AddRows2Table = Lo.Range.Offset(Lo.ListRows.Count + 1).Resize(NumRows)
        Lo.Resize CurrentTableRng.Resize(CurrentTableRng.Rows.Count + NumRows, CurrentTableRng.Columns.Count)
    End If
End Function

Sub demo()
    Dim AddedListrows As Range
    Set AddedListrows = AddRows2Table(ActiveSheet.ListObjects(1), 3, True)
    AddedListrows.Select
    MsgBox "These rows were added: " & AddedListrows.Address
End Sub


Comment by: William Bell (1-7-2019 19:01:00) deeplink to this comment

Hi Jan,

Thank you very much!

Will


Comment by: Jeff C (28-7-2019 07:12:00) deeplink to this comment

Hi, I thought this was going to be simple …hoping for some help :)

I have list-object of customer addresses - columns like Customer, Address, City etc. Assume a cell (row) has already been selected in the "Address" column.

What I am after is a pair of macros that will increment/decrement the selected row up/down a row based on which macro is run. (will be assigned to menu buttons). The catch is that once the last row has been reached (if you're going down), it should select the top (first row) and then continue down again. Similarly, if you've been incrementing up, and reach the 1st row, then the last row should be selected and then it starts going up again.

These macros will work in conjunction with a Worksheet_SelectionChange event macro that currently formats the selected cell in the address column and also copies the value of it to another cell. Note that I added 'SelectedAddress' as a variable because I thought it might be useful for the Up/Down macros I'm trying to make. It's not actually used in the worksheet change event shown below.


Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim ws As Worksheet
Dim SelectedAddress As String ' for potential use in the UP/DOWN macros

    If Not Intersect(Target, Range("tbl_Addresses[Address]")) Is Nothing Then ' if cell not blank/selected do this:
                                        Range("tbl_Addresses[Address]").Interior.ColorIndex = xlColorIndexNone ' set color to nothing

        With Target 'when cell in target range/column is selected do this:
            Range("F4").value = ActiveCell.value 'copy address to F4
                With Selection.Interior 'change interior colour
                    .Color = 6750207 '=yellow
                End With            

            SelectedAddress = Target.Address ' for use in UP/DOWN macros    

            Call AddressSubmit 'processes address in F4

        End With
    End If
End Sub


Comment by: Jan Karel Pieterse (30-7-2019 11:29:00) deeplink to this comment

Hi Jeff,

Something like this perhaps?

Option Explicit

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    Dim ws As Worksheet
    Dim SelectedAddress As String    ' for potential use in the UP/DOWN macros
    With Range("tbl_Addresses[Address]")
        'If selection hits header, move to last cell of table
        If Not Intersect(Target, Range("tbl_Addresses[[#Headers],[Address]]")) Is Nothing Then
            .Cells(.Rows.Count).Select
        End If
        'If selection hits first cell below table, select first cell of table
        If Not Intersect(Target, .Cells(.Rows.Count).Offset(1)) Is Nothing Then
            .Cells(1, 1).Select
        End If
    End With

    If Not Intersect(Target, Range("tbl_Addresses[Address]")) Is Nothing Then    ' if cell not blank/selected do this:
        Range("tbl_Addresses[Address]").Interior.ColorIndex = xlColorIndexNone    ' set color to nothing

        With Target    'when cell in target range/column is selected do this:
            Range("F4").Value = ActiveCell.Value    'copy address to F4
            With Selection.Interior    'change interior colour
                .Color = 6750207    '=yellow
            End With

            SelectedAddress = Target.Address    ' for use in UP/DOWN macros

            'Call AddressSubmit    'processes address in F4

        End With
    End If
End Sub


Comment by: Stefan (13-8-2019 06:52:00) deeplink to this comment

Hello,

I have a problem with numbers in a table. I fill the table from an acces querie but my weeknumbers are text and I need them as numbers. how can i change this for a column quickly?

thanks

Stefan


Comment by: Jan Karel Pieterse (27-8-2019 10:49:00) deeplink to this comment

Hi Stefan,

In the query make sure the weeknumbers are set to the proper format. How to do that depends on what your datasource is precisely and which option of Excel you used to pull in the data.


Comment by: Liesbeth VR (9-9-2019 21:30:00) deeplink to this comment

Hello!

I wonder how I can use the variable TableName in the Range formula? I want to have a robust macro and I am having troubles with the naming of the tables.


Dim oSh As Worksheet
    Dim oLo As ListObject
    Set oSh = ActiveSheet
    For Each oLo In oSh.ListObjects
        Application.Goto oLo.Range
        MsgBox "Table found: " & oLo.Name & ", " & oLo.Range.Address
        TableName = oLo.Name
    Next
    MsgBox TableName

    
    Range("Table1[[#Headers],[Kolom1]]").Select
    ActiveCell.FormulaR1C1 = "Winkel"


=> i already tried
Range("TableName[[#Headers],[Kolom1]]").Select
but this doesn't work.

thanks

Liesbeth


Comment by: Jan Karel Pieterse (10-9-2019 09:53:00) deeplink to this comment

Hi Liesbeth,

You haven't mentioned what you are trying to achieve precisely. That will determine how to use TableName. THe code sample you gave just loops through all worksheets and subsequently through all tables on a worksheet, selecting them in turn and displaying their names. In the code sample you show, TableName always contains the name of the last table that was found in the loop.


Comment by: liesbeth van raemdonck (10-9-2019 10:10:00) deeplink to this comment

Hi Jan,

I am sorry I haven't been clear.

Sometimes I have some open excel files and then my table is Table2 or Table13 or...

So I want to write a macro that always works even though my Table is not called Table1.
That is why I started with searching for the TableName and then I want to work with this table name to select parts of it.

Is this more clear?

thanks

Liesbeth


Comment by: Jan Karel Pieterse (10-9-2019 10:40:00) deeplink to this comment

Hi Liesbeth,

You can also access a table by its index, if there is only one table on the worksheet this would be 1. This example changes the name of the first column of the first table on worksheet "Blad1":

Worksheets("Blad1").ListObjects(1).ListColumns(1).Name = "Winkel"


Comment by: Yash (21-10-2019 15:12:00) deeplink to this comment

Hi, I want to access the heading of every table and store it as a column in an excel sheet. Is there a way to do this?


Comment by: Jan Karel Pieterse (21-10-2019 17:05:00) deeplink to this comment

Hi Yash,

Code like this should do the trick:

Sub ListTableFields()
    Dim Sh As Worksheet
    Dim LO As ListObject
    Dim ReptSht As Worksheet
    Dim Ct As Long
    ActiveWorkbook.Worksheets.Add
    Set ReptSht = ActiveSheet
    For Each Sh In Worksheets
        For Each LO In Sh.ListObjects
            Ct = Ct + 1
            With ReptSht.Cells(1, Ct)
                .Value = Sh.Name
                .Offset(1).Value = LO.Name
                .Offset(2).Resize(LO.ListColumns.Count).Value = Application.Transpose(LO.HeaderRowRange.Value)
            End With
        Next
    Next
End Sub


Comment by: Peter Atallah (4-11-2019 13:28:00) deeplink to this comment

Hi, Very interesting coding, i would like to know what can i use to rename all tables in the workbook by changing a certain part of the name of the table:
for example, I have 31 sheets (for a month) and each sheet contain 5 different tables (repeated daily with difference in naming by the date), in sheet one i have tbl.cash.190701
(July 1, 2019) and other names for tables but all includes the "190701" for sheet two it's tbl.cash.190702, and so on.
i have 5 tables x 30 or 31 days that i change their names every month, it's hectic, following on the above example for August it tbl.cash.190801 and so on.

thank you,


Comment by: Jan Karel Pieterse (14-11-2019 11:02:00) deeplink to this comment

Hi Peter,

Apologies for the tardy reply :-)
Perhaps this does the job?

Sub RenameTables()
    Dim Sh As Worksheet
    Dim Tbl As ListObject
    Dim NewName As Variant
    For Each Sh In Worksheets
        For Each Tbl In Sh.ListObjects
            NewName = Tbl.Name
            'Split the name by period character
            NewName = Split(NewName, ".")
            'Strip out the last item from the name (the 190701 bit)
            ReDim Preserve NewName(UBound(NewName) - 1)
            'Put name back together and add the sheet name
            NewName = Join(NewName, ".")
            NewName = NewName & "." & Sh.Name
            'rename the table
            Tbl.Name = NewName
        Next
    Next
End Sub


Comment by: Sal (7-1-2020 00:59:00) deeplink to this comment

Hello,

I have a table 18 columns and 20 rows of input data. I am checking for any missing data by checking each cell value in the 18x20 data range. Is there a better (easier, faster) way than the for-next method?

Thank you,

Sal


Comment by: Sal (7-1-2020 01:21:00) deeplink to this comment

Hi,

Sorry, I neglected to mention the column headers are 1 through 18, if it matters.

Sal


Comment by: Jan Karel Pieterse (7-1-2020 14:41:00) deeplink to this comment

Hi Sal,

Suppose you want to check for empty cells in columns [First] to [Last], then this code will loop through the empty cells in a table called Table1:

Sub CheckEmpties()
    Dim EmptyCells As Range
    Dim EmptyCell As Range
    On Error Resume Next
    Set EmptyCells = Range("Table1[[First]:[Last]]").SpecialCells(xlCellTypeBlanks)
    On Error GoTo 0
    If Not EmptyCells Is Nothing Then
        For Each EmptyCell In EmptyCells
            Application.Goto EmptyCell
            MsgBox "Cell is empty!"
        Next
    End If
End Sub


Comment by: Sal (8-1-2020 03:41:00) deeplink to this comment

Thank you.


Comment by: Jestine (29-1-2020 16:10:00) deeplink to this comment

Hi Bro,

Can someone show me how to find the last row in table to transfer some data from a userform into?


Comment by: Jan Karel Pieterse (4-2-2020 11:20:00) deeplink to this comment

Hi Jestine,

This puts 123 into the first cell of the last row of a table named Table1:

With ActiveSheet.ListObjects("Table1")
    .Listrows(.ListRows.Count).Range.Cells(1,1).Value="123"
End With


Comment by: SKats (10-2-2020 19:06:00) deeplink to this comment

Hello,
Thank you for this great information. I have a question, I would like to select and copy Column# 1,2 an 5 only from Worksheet1.table1 and paste into worksheet2.table2. Please suggest me some solution.
Best,
SKats


Comment by: Jan Karel Pieterse (17-2-2020 09:37:00) deeplink to this comment

Hi SKats,

Where do these columns need to go? At the bottom of the other table?


Comment by: Michael Gramm (7-3-2020 19:19:00) deeplink to this comment

I am using a table in excel 2010 as a check register.
I use userforms to enter data. Would like to reference columns by name in cell references.
When I use the column name vs the column number
Code:

[Cells(emptyrow, [Register[Payee]].Column).Value = cboPayee.Value]
[Cells(emptyrow, 2).Value = txtDate.Value]

the sheet that the info is copied to will have the arrow keys locked.
The conditional formatting is also messed up.
If I only use the column number as a reference no problem.
If I switch to another sheet and then back again the arrow keys unlock.


Comment by: Jan Karel Pieterse (9-3-2020 10:21:00) deeplink to this comment

Hi Michael,

Odd, this does not happen for me. Is there any other code that manipuales that sheet which might be interfering?
I tried running this and it did not change the behavior of my arrow keys at all:

Sub EnterSomething()
    Dim emptyrow As Long
    emptyrow = 2
    Cells(emptyrow, [Register[Payee]].Column).Value = "JKP"
    Cells(emptyrow, 2).Value = Date
End Sub


Comment by: Michael Gramm (10-3-2020 16:15:00) deeplink to this comment

Regarding my post of using column names instead of numbers.
I have a conditional format [=OR(CELL("row")=CELL("row",B6))]
that hightlights the current row in the table. In the VBA code on the worksheet under the worksheet change event the code
[target.calculate] got deleted. Once that was fixed the naming of the columns worked.
Thanks for your time.
Mike


Comment by: Mr. M F Painter (3-4-2020 13:58:00) deeplink to this comment

how to select part/row of a FILTERED defined table


Comment by: Jan Karel Pieterse (3-4-2020 14:52:00) deeplink to this comment

Hi M F,

Can you elaborate a little on that please?


Comment by: Scott (30-4-2020 13:59:00) deeplink to this comment

Please can anyone help me, I'm new to vba and so far everything has been going well. That is until I tried to create a dynamic raw data sheet in a table by copying the required data from my other sheets.

I've hit 2 problems, I can get the data under my table, I can get the data into the table but it only writes it in the first line and ignores the offset. I've never worked with tables in VBA but I need to build a dashboard and I can't do it without a table.

My issue appears to be with the Destination:=

Sub GetRawData()
Worksheets("RAW Data").Range("A3:AJ1000").ClearContents
Worksheets("Search Results").Range("A3:AJ1000").ClearContents
Application.ScreenUpdating = False
Worksheets("Raw Data").Activate

Dim FirstAddress As String, WhatFor As String
Dim Cell As Range, Sheet As Worksheet
Dim lstObj As ListObject

WhatFor = "BCI"
If WhatFor = Empty Then Exit Sub
'
For Each Sheet In Sheets
If Sheet.Name <> "Search Results" Then '<Ignore worksheets called Search Results
With Sheet.Columns(11)
Set Cell = .Find(WhatFor, LookIn:=xlValues, LookAt:=xlPart)
If Not Cell Is Nothing Then
FirstAddress = Cell.Address
Do

Cell.EntireRow.Copy _
Destination:=Selection.ListObject.ListRows.Add

'Destination:=Sheets("Raw Data").Range("A" & Rows.Count).End(xlUp).Offset(1, 0)
Set Cell = .FindNext(Cell)
Loop Until Cell Is Nothing Or Cell.Address = FirstAddress
End If
End With
End If
Next Sheet
'
Set Cell = Nothing

'sort that mess out

Worksheets("Dashboard").Activate
Application.ScreenUpdating = True

End Sub


Comment by: Jan Karel Pieterse (30-4-2020 14:24:00) deeplink to this comment

Hi Scott,

FIrst add the new listrow, then do the copy, like so:

    Dim NewListRow As Range
    Set NewListRow = Selection.ListObject.ListRows.Add.Range.Cells(1, 1)
    Cell.EntireRow.Copy NewListRow


Comment by: Scott (30-4-2020 16:55:00) deeplink to this comment

Thank you soooooooooo much, I would not have got that on my own. You're amazing. I will be adding you to my favorite websites for the future and telling my colleagues.


Comment by: Cari (13-5-2020 22:15:00) deeplink to this comment

Hello,
I'm tried using the: Removing formatting from an Excel Table code. However, I'm trying to loop it through all the worksheets with the same Table Style, and I run into a snag with ActiveSheet.ListObjects(1) - Subscript out of Range.


Comment by: Jan Karel Pieterse (14-5-2020 11:10:00) deeplink to this comment

Hi Cari,

You would need a slight variation of that code which loops through all sheets and subsequently through all tables on each sheet, like so:

Sub RemoveFormattingOfAllTables()
    Dim oStNormalNoNum As Style
    Dim oSh As Worksheet
    Dim oLo As ListObject
    On Error Resume Next
    Set oStNormalNoNum = ActiveWorkbook.Styles("NormalNoNum")
    On Error GoTo 0
    If oStNormalNoNum Is Nothing Then
        ActiveWorkbook.Styles.Add "NormalNoNum"
        Set oStNormalNoNum = ActiveWorkbook.Styles("NormalNoNum")
        oStNormalNoNum.IncludeNumber = False
    End If
    For Each oSh In Worksheets
        For Each oLo In oSh.ListObjects
            With oLo
                .Range.Style = "NormalNoNum"
                'Now apply tablestyle:
                .TableStyle = "TableStyleLight1"
            End With
        Next oLo
    Next oSh
    ActiveWorkbook.Styles("NormalNoNum").Delete
End Sub


Comment by: João Figueiredo (29-5-2020 16:52:00) deeplink to this comment

Sorry, for my English.
I have a table and I managed to apply a filter to the <> "sent" column and I would like to export what is <> SENT and fill that column with the text sent to not send again.
the filter I can apply but I was able to export to another workbook but the filter is not applied.
Any idea .
Difficulties: Export filtered table and fill what is exported with Sent text.
THX.


Comment by: Jan Karel Pieterse (29-5-2020 18:55:00) deeplink to this comment

Hi João,

Have you already written some VBA code? If so, perhaps you can post that here and indicate what bit of it does not work as expected?


Comment by: João Figueiredo (29-5-2020 19:12:00) deeplink to this comment

Sub COPYS()
''' Copy and Export
' Filter first for check if existe new rows do sent(2 conditions);
Dim Xra As ListObject
Set Xra = Sheet1.ListObjects(1)
Xra.Parent.Activate

'1. Apply Filter
Xra.Range.AutoFilter Field:=6, Criteria1:="ON_URG"
Xra.Range.AutoFilter Field:=26, Criteria1:=""

    Dim myRangex As Double
    On Error Resume Next

myRangex = Xra.DataBodyRange.SpecialCells(xlCellTypeVisible).count ' COunt how many rows are!

On Error GoTo 0
If myRangex < 1 Then
    MsgBox "Não tem existe entregas novas, verifique ficheiro consumos(txt)"
    Exit Sub
Else

' if OK THEN GO to Copy and export
' same filter aplied, but now to copy and export to same folder

Dim Dxprt As String
Dxprt = Format(Now(), "yyyymmddhhmm")
Dim lo As ListObject
Set lo = Sheet1.ListObjects(1)
lo.Parent.Activate 'Activate sheet that Table is on.
lo.AutoFilter.ShowAllData
'1. Apply Filter
lo.Range.AutoFilter Field:=6, Criteria1:="ON_URG"
lo.Range.AutoFilter Field:=26, Criteria1:=""


Dim wb As Workbook, wbNew As Workbook
    Dim ws As Worksheet, wsNew As Worksheet
    Dim wbNewName As String
        
Set wb = ThisWorkbook
Set ws = ActiveSheet

Set wbNew = Workbooks.Add

With wbNew
     Set wsNew = wbNew.Sheets("Sheet1")
     wbNewName = "CdT_On_Urg" & Dxprt
     ' ws.ListObjects(1).Range.COPY ' myRange
     lo.Range.SpecialCells(xlCellTypeVisible).COPY ' myRange
    
     wsNew.Range("A1").PasteSpecial Paste:=xlPasteAll
     .SaveAs Filename:=wb.Path & "\" & wbNewName & ".xlsx", _
             FileFormat:=xlWorkbookDefault, CreateBackup:=False
End With
ActiveWorkbook.Close
MsgBox "ficheiro Exportado para a pasta" ' OK Done!!
End If
End Sub


Comment by: João Figueiredo (29-5-2020 19:14:00) deeplink to this comment

thanks!!! the only thing than is missing is the part to fill the column with the text "sent",


Sub COPYS()
''' Copy and Export
' Filter first for check if existe new rows do sent(2 conditions);
Dim Xra As ListObject
Set Xra = Sheet1.ListObjects(1)
Xra.Parent.Activate

'1. Apply Filter
Xra.Range.AutoFilter Field:=6, Criteria1:="ON_URG"
Xra.Range.AutoFilter Field:=26, Criteria1:=""

    Dim myRangex As Double
    On Error Resume Next

myRangex = Xra.DataBodyRange.SpecialCells(xlCellTypeVisible).count ' COunt how many rows are!

On Error GoTo 0
If myRangex < 1 Then
    MsgBox "Não tem existe entregas novas, verifique ficheiro consumos(txt)"
    Exit Sub
Else

' if OK THEN GO to Copy and export
' same filter aplied, but now to copy and export to same folder

Dim Dxprt As String
Dxprt = Format(Now(), "yyyymmddhhmm")
Dim lo As ListObject
Set lo = Sheet1.ListObjects(1)
lo.Parent.Activate 'Activate sheet that Table is on.
lo.AutoFilter.ShowAllData
'1. Apply Filter
lo.Range.AutoFilter Field:=6, Criteria1:="ON_URG"
lo.Range.AutoFilter Field:=26, Criteria1:=""


Dim wb As Workbook, wbNew As Workbook
    Dim ws As Worksheet, wsNew As Worksheet
    Dim wbNewName As String
        
Set wb = ThisWorkbook
Set ws = ActiveSheet

Set wbNew = Workbooks.Add

With wbNew
     Set wsNew = wbNew.Sheets("Sheet1")
     wbNewName = "CdT_On_Urg" & Dxprt
     ' ws.ListObjects(1).Range.COPY ' myRange
     lo.Range.SpecialCells(xlCellTypeVisible).COPY ' myRange
    
     wsNew.Range("A1").PasteSpecial Paste:=xlPasteAll
     .SaveAs Filename:=wb.Path & "\" & wbNewName & ".xlsx", _
             FileFormat:=xlWorkbookDefault, CreateBackup:=False
End With
ActiveWorkbook.Close
MsgBox "ficheiro Exportado para a pasta" ' OK Done!!
End If
End Sub


Comment by: João Figueiredo (29-5-2020 19:15:00) deeplink to this comment

hello.. where did my text and VB go?? :)


Comment by: Jan Karel Pieterse (30-5-2020 11:23:00) deeplink to this comment

Hi Joao,

comments are not published automatically, I have to approve them (which I just did)


Comment by: João Figueiredo (30-5-2020 11:53:00) deeplink to this comment

Sorry:) i didin´t know.


Comment by: Jan Karel Pieterse (30-5-2020 11:54:00) deeplink to this comment

Hi Joao,

Filling the visible cells of a table column with a value is simple:

lo.ListColumns("SentColumnName").DataBodyRange.SpecialCells(xlCellTypeVisible).Value = "Sent"


Comment by: Paul Deaton (28-3-2021 00:12:00) deeplink to this comment

Wonderful website!! Thanks for your MANY contributions.

I am using Excel 2016 on Windows 10 Pro.

When using the *.ListRows.Add method, I find the results to be oblivious to the AlwaysInsert parameter. Even when I specify AlwaysInsert:=False, I get exactly the same result as the default True. If there are 3 empty rows below the Excel table followed by a 4th row populated with data, after the .Add, there are STILL 3 empty rows.

Searching the web, I see a few other folks with a similar complaint, but no meaningful responses. Has anyone verified that AlwaysInsert:=False works as explained in MS documentation?


Comment by: Jan Karel Pieterse (29-3-2021 14:32:00) deeplink to this comment

Hi Paul,

Thanks for the comment!

The behaviour with AlwaysInsert in all versions appears to be: If Position is not specified AlwaysInsert works as anticipated, True by default or False if specified as False. However if Position is specified AlwaysInsert works as True even if specified as False. The documentation would seem to be incomplete in this respect.


Comment by: Tom (27-5-2021 01:36:00) deeplink to this comment

Thanks for the useful information on this page, it helped us to improve our Excel macro's performance a lot.


Comment by: Jan Karel Pieterse (27-5-2021 10:23:00) deeplink to this comment

Hi Tom,

You're welcome!


Comment by: Michel Saulnier (23-7-2021 13:35:00) deeplink to this comment

Thank you for this VBA tables info, not a lot is available. Here is include my code for adding a row by positioning yourself anywhere in the table even if table is not necessarily starting a the first row:


Sub Add_Copy_Paste_Row_Table()
'
' ADD COPY PASTE ROW above select cell IN TABLE EXCEL
' When table start at any row
' Found a way to link row(cell) in sheet to row(cell) in table
'
    Dim Row_ID As Integer
    Dim Table_Row As Integer
    Dim Table_ID As String
    Dim Real_Row As Integer
    Dim Row_Add As Integer

    ' EXCEL row number
    Row_ID = ActiveCell.Row
    Table_ID = ActiveCell.ListObject.Name
    ' count number of row in able including header
    Table_Row = ActiveSheet.ListObjects(Table_ID).Range.Rows.Count
    ' Select last row
    ActiveSheet.ListObjects(Table_ID).ListRows(Table_Row - 1).Range.Select
    ' EXCEL last table row number
    Real_Row = ActiveCell.Row
    ' How to make the magic happen
    Row_Add = Real_Row - Table_Row
    ' Turning off filter in order to avoid any error
    ActiveSheet.ListObjects(Table_ID).Range.AutoFilter
    ' the code :
    ' Add row above selected row
    Selection.ListObject.ListRows.Add (Row_ID - Row_Add - 1)
    ActiveSheet.ListObjects(Table_ID).ListRows(Row_ID - Row_Add).Range.Select
    Selection.Copy
    ActiveSheet.ListObjects(Table_ID).ListRows(Row_ID - Row_Add - 1).Range.Select
    ActiveSheet.Paste
' Turning back on filter
    ActiveSheet.ListObjects(Table_ID).Range.AutoFilter
End Sub

Hope this can help someone or if you have a more efficient way to do it

Michel S


Comment by: Jan Karel Pieterse (23-7-2021 13:43:00) deeplink to this comment

Hi Michael,

Thanks for posting!
Indeed that can be shortened, for example:

With ActiveCell.ListObject
    .ListRows.Add ActiveCell.Row - .Range.Cells(1, 1).Row
End With

I omitted the turning on and off of the filter though.


Have a question, comment or suggestion? Then please use this form.

If your question is not directly related to this web page, but rather a more general "How do I do this" Excel question, then I advise you to ask your question here: www.eileenslounge.com.




To post VBA code in your comment, use [VB] tags, like this: [VB]Code goes here[/VB].