Table Reference when copying a worksheet - vba

Second part of a question which has been answered on here
First Part Of Question
I have a worksheet that contains a table that has some code to add columns as and when needed. This works perfectly (thanks to answers on this site) but within the code it references the table name (in this case it is a table called 'Labour'). The worksheet is also a blank template that again has some code that copies the entire worksheet and pastes it to the end of the workbook for the next new task.
The problem I have now is that as I have a table on the template worksheet, when the worksheet is copied to a new worksheet the table name changes (ie if it was table1 it is then named table2 on the new sheet). This in turn stops the code working for adding a new column into the new table. Is there anyway to have the code 'know' what table is on the sheet and use that?
I can get the table name to appear in a cell, would there be a way to use that?
The code for adding a column to the table is:
Sub AddNewColumn()
Application.ScreenUpdating = False
Dim oSh As Worksheet
Dim oList As ListObject
Dim str As String
Set oSh = ActiveSheet
Set oList = oSh.ListObjects("Labour")
With oList
.ListColumns.Add
str = .ListColumns(1).Name
Range("Labour[[#All],[Column16]]").Select
Selection.Copy
.ListColumns(.ListColumns.Count).DataBodyRange.PasteSpecial xlPasteAll
Application.ScreenUpdating = True
End With
End Sub
As you can see the table name "Labour" is within the code
Set oList = oSh.ListObjects("Labour")
So when adding a new column on the new worksheet I get an error (because the table Labour doesn't exist on the new sheet).

Scrap this question I think I have figured it out. I got VBA to use the cell with the table name in so it could use a variable. Code as follows:
Sub AddNewColumn()
Application.ScreenUpdating = False
Dim oSh As Worksheet
Dim oList As ListObject
Dim tblName As String
Dim str As String
tblName = Range("B18").Text
Set oSh = ActiveSheet
Set oList = oSh.ListObjects(tblName)
With oList
.ListColumns.Add
str = .ListColumns(1).Name
Range(tblName & "[[#All],[Column16]]").Select
Selection.Copy
.ListColumns(.ListColumns.Count).DataBodyRange.PasteSpecial xlPasteAll
Application.ScreenUpdating = True
End With
End Sub
I've left this just in case it could help anyone else.

Related

Active VBA Macro that performs a vlookup to a separate sheet in the workbook

I was asked to do this specifically not in the sheet itself within the cell.
I need a constantly running Macro so that when I put an ID number in cell D9 in sheet 1, various other cells in Sheet 1 get populated by data points in a table in Sheet 2.
I have the following:
Also, Excel is crashing constantly doing this, but my instruction is specifically to use VBA and not use normal lookups in the cell.
Tried setting it to general and other things. very new to VBA sorry
Private Sub Worksheet_Change(byVal Target As Range)
Dim ID As String
Dim LookupRange As Range
Set LookupRange = Sheet3.Range("A13:AN200")
Dim DataValue As String
If Sheets("Template").Range("D9").Value <> "" Then
ID = Sheets("Template").Range("D9")
DataValue = Application.WorksheetFunction.Vlookup(ID, LookupRange, 3, False)
Range("D11").Value = DataValue
End if
End
I reviewed your code and made some changes that should allow it to work. I have commented most of what I did. If you have questions please let me know.
Disclaimer: This is untested. So you will want to verify it before actually using it.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim wb As Workbook
Dim ws As Worksheet
Dim ws3 As Worksheet
Dim wsName As String
Dim IDRange As String
Dim ResultRange As String
Dim vLookUpRange As String
Dim ID As String
Dim LookupRange As Range
Dim DataValue As String
wsName = "Template"
IDRange = "D9"
ResultRange = "D11"
vLookUpRange = "A13:AN200"
'This is just a habbit of mine, I always set sheets to their own variables.
'It is just easier for me to work with
Set wb = ActiveWorkbook
Set ws = wb.Worksheets(wsName)
Set ws3 = wb.Worksheets(3)
'This line (moved from below Dim) was not writen correctly. it is not Sheet3 but sheets(3) As you can see I moved
'the sheet definition to above. (Again a habbit of mine)
Set LookupRange = ws3.Range(vLookUpRange)
'This is not needed but I add it when I am working with changes to sheets so that I only run the code I want
'when it is within the rang I am looking for. You could add logic to make sure that you only run the code if
'you are only modifying that spesific cell. But for your goal, I don't think it is needed.
If Not Intersect(Target, ws.Range(IDRange)) Is Nothing Then
'You can use .Value but .Value2 is slightly faster with very few consequences.
'eg if you ever need to read or write large amounts of data it will save you some time.
If ws.Range(IDRange).Value2 <> "" Then
ID = ws.Range(IDRange)
DataValue = Application.WorksheetFunction.VLookup(ID, LookupRange, 3, False)
'You also need to specify a sheet for this. Since this is located in the sheet you are entering
'data I assumed the sheet "template"
ws.Range(ResultRange).Value = DataValue
End If
End If
End Sub

Pasting into last column of table

I've been creating a VBA code to help me with a worksheet I use but I'm stuck at a certain point.
The code looks at the table on the current worksheet, adds a new column to the end of the table and then I get it to copy the first column in the worksheet (as this has the formats and some calculated cells). This is where my coding finishes. Ideally I would then like it to take the copied cells and paste them into the new end column of the table.
This is what I have so far:
Sub AddNewColumn()
Application.ScreenUpdating = False
Dim oSh As Worksheet
Set oSh = ActiveSheet
With oSh.ListObjects("Labour")
.ListColumns.Add
Range("Labour[[#All],[Column16]]").Select
Selection.Copy
End With
Application.ScreenUpdating = True
End Sub
(Labour being the name of the current table).
If I can get this to work fantastic but then I think I will encounter another issue. The table is on a template worksheet and contained on this I have a command button to create a copy of the template (for different tasks). This would then change the name of the table (Labour1 then Labour2 etc as new worksheets are created). How would I get the code to work on new worksheets as the code I have at the minute would simply want to link back to the original table (Labour).
You don't need actually copy values from the first column to the newly created, just use formula. I have modified your code:
Sub AddNewColumn()
Application.ScreenUpdating = False
Dim oSh As Worksheet
Dim oList As ListObject
Dim str As String
Set oSh = ActiveSheet
Set oList = oSh.ListObjects("Labour")
With oList
.ListColumns.Add
str = .ListColumns(1).Name
.ListColumns(.ListColumns.Count).DataBodyRange.FormulaR1C1 = "=[#[" & str & "]]"
End With
End Sub
If you need actual values, not formulas, you may copy and paste special the last column. Before end with add:
With .ListColumns(.ListColumns.Count).DataBodyRange
.Copy
.PasteSpecial xlPasteValues
End With
This is answer to your first question. Unfortunately, I am not able to understand the second. Besides, I think you should ask it separately.
OK I have tweaked your code #MarcinSzaleniec and it appears to be working.
Sub AddNewColumn()
Application.ScreenUpdating = False
Dim oSh As Worksheet
Dim oList As ListObject
Dim str As String
Set oSh = ActiveSheet
Set oList = oSh.ListObjects("Labour")
With oList
.ListColumns.Add
str = .ListColumns(1).Name
Range("Labour[[#All],[Column16]]").Select
Selection.Copy
.ListColumns(.ListColumns.Count).DataBodyRange.PasteSpecial xlPasteAll
Application.ScreenUpdating = True
End With
End Sub
The reason I need:
Range("Labour[[#All],[Column16]]").Select
Selection.Copy
Is due to it being a column hidden out the way and has the blank bits blank and the formula bits as formulas.
Many thanks for everybody's help. Now to ask the second part of my question on here.

Splitting Sheets into Separate Workbooks

I have a workbook with a master sheet for school report cards. I have a macro applied to a button for exporting information from the master sheet to separate, newly-generated sheets in the same workbook. A1:C71 is the template and goes to every new sheet, and the following columns of info, from D1:71 to Q1:71, each appear in separate sheets (always in D1:71).
Here's the screenshot (http://imgur.com/a/ZDOVb), and here's the code:
`Option Explicit
Sub parse_data()
Dim studsSht As Worksheet
Dim cell As Range
Dim stud As Variant
Set studsSht = Worksheets("Input")
With CreateObject("Scripting.Dictionary")
For Each cell In studsSht.Range("D7:Q7").SpecialCells(xlCellTypeConstants, xlTextValues)
.Item(cell.Value) = .Item(cell.Value) & cell.EntireColumn.Address(False, False) & ","
Next
For Each stud In .keys
Intersect(studsSht.UsedRange, studsSht.Range(Left(.Item(stud), Len(.Item(stud)) - 1))).Copy Destination:=GetSheet(CStr(stud)).Range("D1")
Next
End With
studsSht.Activate
End Sub
Function GetSheet(shtName As String) As Worksheet
On Error Resume Next
Set GetSheet = Worksheets(shtName)
If GetSheet Is Nothing Then
Set GetSheet = Sheets.Add(after:=Worksheets(Worksheets.Count))
GetSheet.Name = shtName
Sheets("Input").Range("A1:C71").Copy
GetSheet.Range("A1:D71").PasteSpecial xlAll
GetSheet.Range("A1:B71").EntireColumn.ColumnWidth = 17.57
GetSheet.Range("C1:C71").EntireColumn.ColumnWidth = 54.14
GetSheet.Range("D1:D71").EntireColumn.ColumnWidth = 22
End If
End Function`
I would now like to create a separate button to split the sheets into separate workbooks so that the master sheet can be kept for record keeping and the individual workbooks can be shared with parents online (without divulging the info of any kid to parents other than their own). I would like the workbooks to be saved with the existing name of the sheet, and wonder if there's a way to have the new workbooks automatically saved in the same folder as the original workbook without having to input a path name? (It does not share the same filename as any of the sheets).
I tried finding other code and modifying it, but I just get single blank workbooks and I need as many as have been generated (preferably full of data!), which varies depending on the class size. Here's the pathetic attempt:
`Sub split_Reports()
Dim splitPath As String
Dim w As Workbook
Dim ws As Worksheet
Dim i As Long, j As Long
Dim lastr As Long
Dim wbkName As String
Dim wksName As String
Set wsh = ThisWorkbook.Worksheets(1)
splitPath = "G:\splitWb\"
Set w = Workbooks.Add
For i = 1 To lastr
wbkName = ws
w.Worksheets.Add(After:=w.Worksheets(Worksheets.Count)).Name = ws
w.SaveAs splitPath
w.Close
Set w = Workbooks.Add
Next i
End Sub`
I have learned so much, and yet I know so little.
Maybe this will start you off, just some simple code to save each sheet as a new workbook. You would probably need some check that the sheet name is a valid file name.
Sub x()
Dim ws As Worksheet
For Each ws In ThisWorkbook.Sheets
ws.Copy
ActiveWorkbook.Close SaveChanges:=True, Filename:=ws.Name & ".xlsx"
Next ws
End Sub

Copy sheet without creating new instances of named ranges?

I'm using the following code to copy a sheet. I also have a few named ranges that are scoped to the Workbook. The problem is, when I do the copy, it creates duplicates of all the named ranges with a scope of the new sheet. Everything works of course but I could potentially have 20+ sheets. I don't need 80 named ranges that are mostly duplicates. How can I avoid this?
Sub btnCopyTemplate()
Dim template As Worksheet
Dim newSheet As Worksheet
Set template = ActiveWorkbook.Sheets("Template")
template.Copy After:=Sheets(Sheets.Count)
Set newSheet = ActiveSheet
newSheet.Name = "NewCopy"
End Sub
And the Name Manager after a copy:
Here is my answer:
Sub btnCopyTemplate()
Dim template As Worksheet
Dim newSheet As Worksheet
Set template = ActiveWorkbook.Sheets("Template")
template.Copy After:=Sheets(Sheets.Count)
Set newSheet = ActiveSheet
newSheet.Name = "NewCopy"
deleteNames 'Check the sub
End Sub
Sub deleteNames()
Dim theName As Name
For Each theName In Names
If TypeOf theName.Parent Is Worksheet Then
theName.Delete
End If
Next
End Sub
This way you will delete all the names with the scope "worksheet" and keep the "workbook" names
Edit#2
After read the comments here is the update passing the sheet to loop only the "newSheet"
Sub btnCopyTemplate()
Dim template As Worksheet
Dim newSheet As Worksheet
Set template = ActiveWorkbook.Sheets("Template")
template.Copy After:=Sheets(Sheets.Count)
Set newSheet = ActiveSheet
newSheet.Name = "NewCopy"
deleteNames newSheet
End Sub
Sub deleteNames(sht As Worksheet)
Dim theName As Name
For Each theName In Names
If (TypeOf theName.Parent Is Worksheet) And (sht.Name = theName.Parent.Name) Then
theName.Delete
End If
Next
End Sub

Excel copy/paste data based on tab names in multiple files

I have a (hopefully) easy situation. I'm seeking to automate this process with a VBA macro.
I have an Excel spreadsheet (let's call this data.xls) that has multiple tabs with the following names (this is just an example):
Sucralose
Cellulose
Dextrose
Each tab simply has a column of data in it.
I want to know if there is a simple way to copy all the tabs of data to another spreadsheet with specific formatting for further operations (let's call this reduction.xls) based on the tab naming.
For example:
I want to copy Column A of tab Sucrose, Dextrose, Cellulose FROM data.xls TO Column F of the same named tabs (already existing) in reduction.xls [Sucrose, Dextrose, Cellulose].
I'm looking for a "true/false" type statement where the column from each tab in data.xls will be pasted into reduction.xls assuming the same exact named tab exists, without any need for interaction from the user.
Code posted below has the following features:
It is prepared for easily handling an arbitrary number of tabs. You have to modify only 3 lines, as indicated: 1) The list of tab names, 2) the name of the source workbook, 3) the name of the target workbook.
It is "protected" against missing tabs in the target workbook.
The structure is likely self-explanatory (although this might be a subjective statement).
.
Sub copy_tab(ByVal wsName As String)
Dim wbnamesrc As String
Dim wbnametrg As String
wbnamesrc = "source.xlsm" ' Change this line
wbnametrg = "Book8" ' Change this line
Dim wbsrc As Workbook
Dim wbtrg As Workbook
Set wbsrc = Workbooks(wbnamesrc)
Set wbtrg = Workbooks(wbnametrg)
If (WorksheetExists(wsName, wbnametrg)) Then
Dim rngsrc As Range
Dim rngtrg As Range
Application.CutCopyMode = False
wbsrc.Worksheets(wsName).Range("A:A").Copy
wbtrg.Worksheets(wsName).Range("A:A").PasteSpecial
End If
End Sub
Sub copy_tabs()
Dim wslist As String
Dim sep As String
wslist = "Sucralose|Cellulose|Dextrose|Sheet1" ' Change this line
sep = "|"
Dim wsnames() As String
wsnames = Split(wslist, sep, -1, vbBinaryCompare)
Dim wsName As String
Dim wsnamev As Variant
For Each wsnamev In wsnames
wsName = CStr(wsnamev)
Call copy_tab(wsName)
Next wsnamev
End Sub
Public Function str_split(str, sep, n) As String
' From http://superuser.com/questions/483419/how-to-split-a-string-based-on-in-ms-excel
' splits on your choice of character and returns the nth element of the split list.
Dim V() As String
V = Split(str, sep)
str_split = V(n - 1)
End Function
' From http://stackoverflow.com/a/11414255/2707864
Public Function WorksheetExists(ByVal wsName As String, ByVal wbName As String) As Boolean
Dim ws As Worksheet
Dim ret As Boolean
ret = False
wsName = UCase(wsName)
For Each ws In Workbooks(wbName).Worksheets
If UCase(ws.Name) = wsName Then
ret = True
Exit For
End If
Next
WorksheetExists = ret
End Function
Personally I would create the VBA in a separate workbook that you can open and execute separately from the other 2 interacting workbooks.
Thus I defined three dimension. wbk = workbook with code in it. wbk1 = the source workbook where you will copy from. wbk2 - the destination workbook where you will paste to.
You will have to edit the file locations as well as the Ranges. Say if you only wanted A1:A100, provided it is the same number of rows each time. If not I suggest increasing the rows far past what you anticipate the row count will be so you make sure you don't miss any.
Go to a new workbook
Hold Alt and press F11 key
Click Insert -> Module
Paste the below code in the window and update file locations and copy/paste range as needed
Press Run Macro (green play button) or hit F5 with your cursor in the code
Sub DataTransfer()
Dim wbk, wbk1, wbk2 As Workbook
'Workbook with VBA in it.
Set wbk = ActiveWorkbook
'Define destination workbook
Set wbk1 = Workbooks.Open("C:\data.xls")
'Define Source workbook
Set wbk2 = Workbooks.Open("C:\reduction.xls")
Call wbk1.Worksheets("Sucralose").Range("A1:A100000").Copy
Call wbk2.Worksheets("Sucralose").Range("F1:F100000").PasteSpecial(xlPasteValues)
Application.CutCopyMode = False
Call wbk1.Worksheets("Cellulose").Range("A1:A100000").Copy
Call wbk2.Worksheets("Cellulose").Range("F1:F100000").PasteSpecial(xlPasteValues)
Application.CutCopyMode = False
Call wbk1.Worksheets("Dextrose").Range("A1:A100000").Copy
Call wbk2.Worksheets("Dextrose").Range("F1:F100000").PasteSpecial(xlPasteValues)
Application.CutCopyMode = False
End Sub