Copying the Merge-Formatting of a Table Using VBA - vba

I'm trying to create a macro that works on columns that are adjacent to an existing table. The purpose of this macro is to take any cell merges that exist in the table and copy them into the next two columns (It's a helper method for something with a larger purpose). My code is below but I'm getting an error that says, "PasteSpecial method of Range class failed" which occurs on the line:
Selection.PasteSpecial Paste:=xlPasteFormats, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
The line directly above was a test line to check if the code was working for the first iteration of the loop, which it is. Once the code tried to re-iterate and paste again, however, the code fails. I believe it's because the 'Selection.PasteSpecial' call is not referencing the correct object anymore but I'm not sure how to fix it.
Sub extendColumnMerges()
'
' Works on a column adjacent to a table by extending the column's merge-formatting to the selected column
' Active cell must begin as the first cell in the column immediatley adjacent the table on the right
'
Dim cols As Integer
cols = 2
'Selects the last column of the table and copies the selection into the new column, modifying the format of the new column
Range(ActiveCell.Offset(0, -1), ActiveCell.Offset(0, -1).End(xlDown)).Select
Selection.Copy
'Pastes the columns' merge-formatting into each specified column adjacent the table on the right
For c = 1 To cols
ActiveCell.Offset(0, 1).Range("A1").Select
ActiveCell.Value = "Yes"
Selection.PasteSpecial Paste:=xlPasteFormats, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
Application.CutCopyMode = False
'Removes the formatting from the cells in the new column
With Selection.Interior
.Pattern = xlNone
.TintAndShade = 0
.PatternTintAndShade = 0
End With
'Removes borders from the newly modified column
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.Select
Next c
End Sub
Also, if there are any suggestions as to how to code this in a more elegant manner they would be greatly appreciated. Copying the formatting and then removing the borders and fills seems bulky in code. Thanks.

This seemed to work for me:
Sub Tester()
'copy the merged areas of the current selection 4 columns to the right
ReplicateMergeAreas Selection, 4
End Sub
Sub ReplicateMergeAreas(sourceArea As Range, colOffset As Long)
Dim rw As Long, col As Long
Dim c As Range, c2 As Range
Dim rngTL As Range
'set the cell at the top-left of the "destination" range
' (actually offset -1,-1 to make it easier to set range 'c2')
Set rngTL = sourceArea.Cells(1).Offset(-1, colOffset - 1)
'loop through each cell in the "source" range
For rw = 1 To sourceArea.Rows.Count
For col = 1 To sourceArea.Columns.Count
'c=cell in source range
Set c = sourceArea.Cells(rw, col)
'is this cell merged?
If c.MergeArea.Cells.Count > 1 Then
'c2=corresponding cell in the destination range
Set c2 = rngTL.Offset(rw, col)
'Is the "destination" cell already merged?
' skip if yes
If c2.MergeArea.Cells.Count = 1 Then
'set merge area the same size as the "source" cell
c2.Resize(c.MergeArea.Rows.Count, _
c.MergeArea.Columns.Count).Merge
End If
End If
Next col
Next rw
End Sub
The range passed to the sub must be a single-area rectangular range which completely encloses all the merged areas (or at least their top-left corners) you want to replicate across. colOffset is how many columns over you want to copy the merging to.

Related

VBA to copy information down but on a loop

I tried to run this in a macro but somehow the loop didn’t work as it kept referencing the cells when I need this to just run down until it hits the end. I am looking to do the following:
If there is a value in column D and nothing in column B then the information need to be copied down. To copy it will look for column A to match but look for the top line of the match so there is values in column B. once it finds the top row the code should copy down rows B,E & H
the code i used is a recorded macro. this look for a value in B goes to the bottom (using Ctrl + Down) copies this value and uses Ctrl + Shift + down, the up one to find the end. pastes the value then moves across to the other columns. but i can only get this to run on the first section it needs to repeat until the end. the end is defined on row 10000:
Sub Sort_The_Fus_To_One_Line_2()
Application.Goto Reference:="R8C2"
Range("B8").Select
Selection.End(xlDown).Select
Selection.Copy
Range(Selection, Selection.End(xlDown)).Select
Range("B10:B43").Select
ActiveSheet.Paste
Range("E10").Select
Application.CutCopyMode = False
Selection.Copy
Range(Selection, Selection.End(xlDown)).Select
Range("E10:E43").Select
ActiveSheet.Paste
Range("H10").Select
Application.CutCopyMode = False
Selection.Copy
Range(Selection, Selection.End(xlDown)).Select
Range("H10:H43").Select
ActiveSheet.Paste
Range("B10").Select
Selection.End(xlDown).Select
End Sub
Can someone help!? Thanks!
I think this is something like what you're after:
Sub Test()
Dim rStart As Range, rEnd As Range
Dim FirstAdd As String
Dim lLastRow As Long
lLastRow = 10001
With Worksheets("Sheet1").Columns(2)
'Find the first non-blank cell in column B.
Set rStart = .Find(What:="*", _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchDirection:=xlNext)
'rStart will be Nothing if the column is empty.
If Not rStart Is Nothing Then
FirstAdd = rStart.Address 'Very first found address.
Do
'Find the next non-blank cell in column B.
Set rEnd = .FindNext(rStart)
If rEnd.Row < rStart.Row And rStart.Row < lLastRow Then
'The cell reference is relative to the column in the With command.
'making column 1 = sheet column 2.
Set rEnd = .Cells(lLastRow, 1)
End If
'If the second address isn't the same as the very first address and
'the second address isn't the row below the start address then copy the value down.
If rEnd.Address <> FirstAdd And rStart.Offset(1).Address <> rEnd.Address Then
'Places the value from the Start row into every cell between one below the
'start row to one cell above the end row.
Worksheets("Sheet1").Range(rStart.Offset(1), rEnd.Offset(-1)) = rStart.Value
Worksheets("Sheet1").Range(rStart.Offset(1), rEnd.Offset(-1)).Offset(, 3) = rStart.Offset(, 3).Value
Worksheets("Sheet1").Range(rStart.Offset(1), rEnd.Offset(-1)).Offset(, 6) = rStart.Offset(, 6).Value
End If
'Set the new start address as the previous end address.
Set rStart = rEnd
Loop While rStart.Row < lLastRow
End If
End With
End Sub

excel vba copy row and paste to new sheet as pastevalue

I want to copy data from sheet "summary" row A44 (fixed row with dynamic data with formula) to sheet18 (row A3), A1 and A2 are header; i have below vba code and manage to do so. I would like to copy and paste the data as value (like Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False), so that the data will convert to absolute number, anyone how to edit the code?
Sub COPY_SUMMARY2COPYDATA()
Set des = Sheet18.Range("a1")
With Worksheets("SUMMARY")
.Rows(Range("A44").Row).Copy
des.Range("A3").Insert Shift:=xlUp
End With
Application.CutCopyMode = False
End Sub
Please try this:
Sub COPY_SUMMARY2COPYDATA()
Dim LastRow As Long
LastRow = Sheet18.Cells(Rows.Count,1).End(XlUp).Row + 1
Sheets("SUMMARY").Rows("44").Copy
Sheet18.Rows(LastRow).PasteSpecial xlPasteValues
Application.CutCopyMode = False
End Sub
Hope this help
To find the first blank cell in column A look from the bottom up and offset down one row.
Use a direct value transfer with .Value2 to pass over 'the data will convert to absolute number'. This will discard regional currency and date conventions as well as formulas in favor of the raw underlying value.
with Worksheets("SUMMARY")
with intersect(.usedrange, .rows(44).cells)
Sheet18.cells(.rows.count, "A").end(xlup).offset(1, 0).resize(.rows.count, .columns.count) = .value2
end with
end with

Copy selected cells from one workbook and copy to another

I am trying to create a database which will copy a selected range of data from a main workbook and copy into a separate workbook.
The code causing the issue is below. The 2nd workbook opens based on the value of "W2". A new row should be inserted to the new Wb and formatted then the value of the selected cells pasted.
'Select data to be copied
ActiveCell.Resize(1, 4).Copy
'Open Lessons Learned Db
Location = Range("W2").Value
Set Lessons = Workbooks.Open(Location)
Set LL = Sheets("Lessons Learned")
Windows("Lessons Learned Database.XLSM").Activate
Sheets("Lessons Learned").Activate
'Insert New Row
Range("5:5").Activate
ActiveCell.Offset(1).EntireRow.Insert
'Enter Odd Or Even VALUE
Range("A7").Select
OE = ActiveCell.Value
If OE = 1 Then
Range("A6").Select
ActiveCell.FormulaR1C1 = 0
Else
Range("A6").Select
ActiveCell.FormulaR1C1 = 1
End If
'Hide Permanently Hidden Rows -LINE BELOW GIVES ERROR 1004
Rows("5:5").Select
Selection.EntireRow.Hidden = True
Columns("A").Select
Selection.EntireColumn.Hidden = True
'FORMAT ROW
Range("A6").Select
SC = ActiveCell.Value
If SC = 1 Then
Range("B6:N6").Select
With Selection.Interior
.ColorIndex = 15
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
End With
End If
Range("B5").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Any pointer on where I'm going wrong would be greatly appreciated.
Its advised not to use .Select or .Activate as there are other ways to accomplish this.
Because you were selecting and activating this could have caused the ERROR 1004.
Below i have "cleaned up" your code defining Lessons, LL and Location and included MainWB and defined your ranges.
By defining your Range Excel will always get the .Value from that Range there is then no need to use .Select or .Activate.
As far as tested the below code works:
Sub CopyMainWBtoNewWB()
Dim Lessons As Workbook
Dim LL As Worksheet
Dim MainWB As Workbook
Dim Location As String
Set MainWB = Workbooks("Name Here")
'Open Lessons Learned Db
Location = MainWB.Sheets("Sheet Name").Range("W2").Value
Set Lessons = Workbooks.Open(Location)
Set LL = Lessons.Sheets("Lessons Learned")
'Insert New Row
LL.Rows(5).Offset(1).EntireRow.Insert shift:=xlDown
'Enter Odd Or Even VALUE
If LL.Range("A7").Value = 1 Then
LL.Range("A6").Value = 0
Else
LL.Range("A6").Value = 1
End If
'Hide Permanently Hidden Rows -LINE BELOW GIVES ERROR 1004
LL.Rows(5).Hidden = True
LL.Columns(1).Hidden = True
'FORMAT ROW
If LL.Range("A6").Value = 1 Then
With LL.Range("B6:N6").Interior
.ColorIndex = 15
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
End With
End If
MainWB.Sheets("Sheet1").Range("A1:A4").Copy
LL.Range("B5").PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
End Sub
All you need to do is change the Workbook name of MainWB and the Sheet name its collecting the Valuefor Location.
I suspect your code has some other issues that Code Review may be able to help with but to answer your question:
The Rows("5:5").Select is being passed the wrong argument data type.
Worksheet.Rows() is expecting a number, either Integer or Long data type but you are giving it a string.
Change it to Rows(5) and it should work.
That can all be consolidated to:
ActiveCell.Resize(1, 4).Copy '// not sure what this is for
Set Lessons = Workbooks.Open([w2])
Set LL = Lessons.Sheets("Lessons Learned")
With LL
.Rows(6).EntireRow.Insert
.Range("A6").value = IIf(.Range("A7").value = 1, 0, 1)
.Rows(5).Hidden = True
.Columns("A").Hidden = True
If .Range("A6").value = 1 Then
With .Range("B6:N6").Interior
.ColorIndex = 15
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
End With
End If
.Range("B5").PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
End With
You will notice that this code refers directly to each object without activating or selecting it. Coding in this way means that every object is fully qualified and you know exactly which instance you are working with.
This should ensure that the row is correctly hidden without error because you are referring to the Rows collection which is a collection of Ranges with defined properties and methods. Selection can be a sheet, workbook, chart, range or pretty much anything else you can point and click at - so this can cause problems when trying to access properties or methods that belong to a particular object or class.

Run time error in excel vba. last used range rows selects row 65536 instead of actual last used range

I am trying to select columns E and K from sheet Input, process in Working sheet and paste in the Output sheet after the last used row. I have stored the last used row number in x and paste the values in x+1 cell. However excel selects last row of the sheet (x as 65536) and gives run time error 4004. Can someone please help me in assisting the code.
Dim x As Long, y As String
Sheets("Input").Activate
Range("E:E,K:K").Select
Range("K1").Activate
Selection.Copy
Sheets("Working").Select
Cells(1, 1).Select
ActiveSheet.Paste
Cells.Select
Application.CutCopyMode = False
Selection.AutoFilter
Range("B5").Select
ActiveSheet.Range("$A$1:$H$30").AutoFilter Field:=1, Criteria1:="="
Cells.Select
Selection.Delete Shift:=xlUp
Columns("B:B").Select
Selection.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Range("B2").Select
ActiveCell.FormulaR1C1 = _
"=IF(RC[-1]="""","""",VLOOKUP(RC[-1],Repository!C[-1]:C[1],3,0))"
Range("B2").Select
Selection.Copy
Range("B3:B30").Select
ActiveSheet.Paste
Cells.Select
Application.CutCopyMode = False
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Range("B2:C2").Select
Range(Selection, Selection.End(xlDown)).Select
Application.CutCopyMode = False
Selection.Copy
Sheets("Output").Select
Range("A1").Select
x = Worksheets("Output").UsedRange.Rows.Count
y = "a" & Trim(x + 1)
ActiveSheet("Output").Range(y).Select
ActiveSheet.Paste
Your UsedRange is still thinking that the last row is 65536. Add this subroutine, then call it right before you set x.
Sub CorrectUsedRange()
Dim values
Dim usedRangeAddress As String
Dim r As Range
'Get UsedRange Address prior to deleting Range
usedRangeAddress = ActiveSheet.UsedRange.Address
'Store values of cells to array.
values = ActiveSheet.UsedRange
'Delete all cells in the sheet
ActiveSheet.Cells.Delete
'Restore values to their initial locations
Range(usedRangeAddress) = values
End Sub
Near the bottom of your code replace:
Sheets("Output").Select
with:
Sheets("Output").Select
ActiveSheet.UsedRange
this should "re-set" UsedRange
Sometimes the Used Range gets generically large and won't reset on it's own. When this happens, the only way that I've found to force it to reset itself correctly is to Save the Workbook that the subject Worksheet is in. This works for me, on Excel 2010 anyway. Since you're using .Select and Active<obj> (which I don't recommend), it would simply be this:
ActiveWorkbook.Save
I would use a Find loop to populate an array and then output the array when the macro has completed. There is no need for a "Working" sheet. This also uses Cells(Rows.Count, "A").End(xlUp) in order to find the last populated row instead of UsedRange.Rows.Count which can be unreliable.
Sub tgr()
Dim rngFound As Range
Dim rngLookup As Range
Dim arrResults() As Variant
Dim ResultIndex As Long
Dim strFirst As String
With Sheets("Input").Columns("E")
Set rngFound = .Find("*", .Cells(.Cells.Count), xlValues, xlWhole)
If Not rngFound Is Nothing Then
strFirst = rngFound.Address
ReDim arrResults(1 To WorksheetFunction.CountA(.Cells), 1 To 2)
Do
If rngFound.Row > 1 Then
ResultIndex = ResultIndex + 1
On Error Resume Next 'Just in case the VLookup can't find the value on the 'Repository' sheet
arrResults(ResultIndex, 1) = Evaluate("VLOOKUP(""" & rngFound.Value & """,Repository!A:C,3,FALSE)")
arrResults(ResultIndex, 2) = .Parent.Cells(rngFound.Row, "K").Value
On Error GoTo 0 'Remove the On Error Resume Next condition
End If
Set rngFound = .Find("*", rngFound, xlValues, xlWhole)
Loop While rngFound.Address <> strFirst
End If
End With
If ResultIndex > 0 Then Sheets("Output").Cells(Rows.Count, "A").End(xlUp).Offset(1).Resize(ResultIndex, UBound(arrResults, 2)).Value = arrResults
Set rngFound = Nothing
Erase arrResults
End Sub
instead of used range check how many rows already are filled with this code:
X = WorksheetFunction.CountA(Columns(1))
Of course this only works ok if you have no rows that are empty in Column A, as those rows would be ignored!

how to capture cell address as a variable and use in VB code?

Need a code snippet; if some kind guru could help, please. I need to express the following cursor movement sequence in XL VBA.
After entering a formula in cell A1 (Col-A is otherwise empty), I need to copy the formula to all cells in the range A1:AN, where N is the last row of the table.
I recorded a macro to do the following (code below):
1) enter the formula (in Cell A1)
2) copy the formula
3) go Right to B1
4) go to the last populated cell in Col-B [using Ctrl+Down] (easiest way to find the last row)
5) go Left to Col-A
6) select all cells from current to A1
7) paste the formula to the selection
The part I need help with is a way to capture the cell address in step 5 as a variable so that I can use this macro on a series of files having a variable number of rows.
Here is the recorded macro. In this example, the last row in the table is 7952.
Sub test()
ActiveCell.FormulaR1C1 = "=LEFT(RC[1],3)"
ActiveCell.Select
Selection.Copy
ActiveCell.Offset(0, 1).Range("A1").Select
Selection.End(xlDown).Select
ActiveCell.Offset(0, -1).Range("A1").Select
Range(Selection, Selection.End(xlUp)).Select
ActiveCell.Offset(-7951, 0).Range("A1:A7951").Select
ActiveCell.Activate
ActiveSheet.Paste
End Sub
Kindly copy the below code to the worksheet.
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
If Target.Address = "$A$1" And Target.Count = 1 And Target.HasFormula Then
Dim lastRow As Long
lastRow = Range("A65000").End(xlUp).Row
Dim rng As Range
Set rng = Range("A2:A" & lastRow)
' Target.Copy
' rng.PasteSpecial xlPasteFormulas
'OR
' rng.Formula = Target.Formula
' OR
rng.FormulaR1C1 = Target.FormulaR1C1
End If
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
End Sub
I'm not sure if your end cell is always going to be the same, meaning you may want to "un" hard code the rows, but you could try this.
Sub test()
Range(Cells(1, 1), Cells(7951, 1)) = "=LEFT(RC[1],3)"
End Sub
If you are always going to put equations in column A based on the number of rows used in column B you could try this.
Sub test()
' dimension the variable type
Dim lastRow As Long
' select cell "B1"
Cells(1, 2).Select
' jump to the last consecutive row in column B
Selection.End(xlDown).Select
' collect the row number into a variable
lastRow = ActiveCell.Row
' paste the equation into the variable length range
Range(Cells(1, 1), Cells(lastRow, 1)) = "=LEFT(RC[1],3)"
End Sub
Thanks Todd and user2063626,
I decided on a simpler approach. I only needed to obtain the last row in order to set my selection area; the number of the last row is not used in the actual values to be written. The files to be manipulated are flat ascii exports; the column layout is constant, only the number of rows is variable.
After writing the formula to A1, I move down column B and test for a value one cell at a time; if TRUE, copy the formula to the left adjacent cell; if FALSE, end process.
Sub FillClientCodes()
Range("A1").Select
ActiveCell.FormulaR1C1 = "=LEFT(RC[1],3)"
ActiveCell.Select
Selection.Copy
ActiveCell.Offset(0, 1).Select
ActiveCell.Offset(1, 0).Select
CheckCell:
ActiveCell.Activate
If ActiveCell.Value <> 0 Then
ActiveCell.Offset(0, -1).Select
ActiveCell.Activate
ActiveSheet.Paste
ActiveCell.Offset(0, 1).Select
ActiveCell.Offset(1, 0).Select
GoTo CheckCell
Else: GoTo EndOfData
End If
EndOfData:
End Sub
It's not elegant - it runs slower than a single select and paste - but it works, and it will work on all the files I need to process. Thanks again.