2

I have a Sub that I Call in as part of another sub. When this is ran by itself it works fine but when it is called in from another sub it doesn't quite work exactly right. The main sub saves the default values and then:

Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False

Call FillInCoding

Here is the sub that is not working and I think it might be this coding is copy and paste special-ing:

Sub FillInCoding()

'clears the area to be pasted into
Worksheets(1).Range("A11:G98567").ClearContents


Dim JE As Worksheet
Dim Sheet1 As Worksheet
Dim lastRow As Long

Set JE = ThisWorkbook.Worksheets(1)
Set Sheet1 = ThisWorkbook.Worksheets(2)


lastRow = Sheet1.Range("R65536").End(xlUp).Row

'This part is copying the values of the ranges from worksheet #2 into the worksheet #1 destination
 Worksheets(2).Range("AM2:AM" & lastRow).Copy
 Worksheets(1).Range("A11:A" & lastRow).PasteSpecial xlPasteValues
 Worksheets(2).Range("AN2:AN" & lastRow).Copy
 Worksheets(1).Range("B11:B" & lastRow).PasteSpecial xlPasteValues
 Worksheets(2).Range("R2:R" & lastRow).Copy
 Worksheets(1).Range("E11:E" & lastRow).PasteSpecial xlPasteValues
 Worksheets(2).Range("AO2:AO" & lastRow).Copy
 Worksheets(1).Range("G11:G" & lastRow).PasteSpecial xlPasteValues


End Sub

I tried setting the ranges but it was pasting the cell formulas instead of the values, so this was the only easy way I could get it to do what I needed it to do. At the end of the day I need what you see on sheet 2 in a varying range (each time it's ran it could be a different length) to be copied as a value into sheet 1.

If someone knows how to easily set ranges on different sheets as equal to varying values when the end row changes and they are in different spots on the different sheets that would be ideal.

For example on sheet two it might be

A1="XYZ"
B1="100000"
C1="52.00"
D1="office supplies"

A2="YZA"
B2="150000"
C2="-52.00"
D2="office supplies"

But in sheet one we need these values pasted in starting at A11:12, B11:12, C11:12, D11:12, etc (the next day though maybe there are 40 lines instead of 2 lines).

Community
  • 1
  • 1
MadChadders
  • 127
  • 1
  • 11
  • 1
    How does the data on sheet1 change? Is it automatically updated, or is someone actually in Excel changing the data? – BruceWayne Aug 18 '15 at 21:14
  • @BruceWayne It is done by other formulas (basically there is a report dump, then macros that format it to the state it needs to be on sheet 2, and then I want that copied over to a different sheet as values). – MadChadders Aug 18 '15 at 21:44

4 Answers4

2

It is probably not a good idea to be using a worksheet's reserved .CodeName property as the name of a variable; particlarly so as it isn't even that worksheet's codename.

Sub FillInCoding()
    Dim lr As Long, JE As Worksheet, ws2 As Worksheet

    Set JE = ThisWorkbook.Worksheets(1)
    Set ws2 = ThisWorkbook.Worksheets(2)

    lr = ws2.Range("R65536").End(xlUp).Row

    With JE
        .Range("A11:G98567").ClearContents

        .Range("A11:B11").Resize(lr - 1, 2) = ws2.Range("AM2:AN" & lr).Value
        .Range("E11").Resize(lr - 1, 1) = ws2.Range("R2:R" & lr).Value
        .Range("G11").Resize(lr - 1, 1) = ws2.Range("AO2:AO" & lr).Value
    End With

End Sub

I've opted for direct value transfer using some modified worksheet references. The first two columns AM & AN to A and B could be doubled up. See my comment about setting the last row.

See How to avoid using Select in Excel VBA macros for more methods on getting away from relying on select and activate to accomplish your goals.

Community
  • 1
  • 1
  • Much more succinct than my answer. (Sorry, off topic, but should I delete my answer completely, or leave it since it, even if tangentially, answers part of the question?) – BruceWayne Aug 18 '15 at 21:17
  • 1
    @BruceWayne - I've got to edit my question to account for the shift. The OP can benefit for all answers. –  Aug 18 '15 at 21:20
  • Ah okay. Would you recommend adding that to a `Worksheet Change` event for Sheet1? It looks like OP wants to automatically copy info if changes are made in Sheet1...or is there a better way? (I was thinking that if the user themselves are changing the code, to just have them run the macro each time...) – BruceWayne Aug 18 '15 at 21:23
  • 1
    @BruceWayne - This doesn't seem to be something that I would put into a Worksheet_Change. Too much going on and all the user would have to do is change or remove a value to launch it. Probably best to leave it as a run-as-you-require macro sub. –  Aug 18 '15 at 21:26
2

There are a couple different problems that can occur due to the way your code is written.

First, you're copying a range that is larger than the area you are attempting to paste it in. In your exacmple:

 Worksheets(2).Range("AM2:AM" & lastRow).Copy
 Worksheets(1).Range("A11:A" & lastRow).PasteSpecial xlPasteValues

If your lastRow variable has 100 entries, then you just copied 99 cells (2-100). Yet you're trying to paste them in a space that's only 90 cells (11-100). One bad, yet really simple, unsophisticated fix is to simply add the difference to the lastrow variable:

 Worksheets(2).Range("AM2:AM" & lastRow).Copy
 Worksheets(1).Range("A11:A" & lastRow **+ 9**).PasteSpecial xlPasteValues

But better yet, you don't need to explicitly state the range you'll be pasting into. You can just give the target cell and it'll paste there regardless of the content length:

Worksheets(2).Range("AM2:AM" & lastRow).Copy
Worksheets(1).Range("A11").PasteSpecial xlPasteValues

Also, there's a few other things you're doing that may cause you some issues later on. you're setting your lastRow range to the length of column R on your first spreadsheet. When based on how you are copying and pasting, I'm guessing you want your lastRow length to be the length of the column you're actively copying. You can get that by using something like:

lastRow = Sheets("Sheet2").Range("A" & Rows.Count).End(xlUp).Row

and change "A" to whatever your target column is. Then reset the last row variable each time you're copying a new row. An Example would be:

 lastRow = Sheets("Sheet2").Range("AM" & Rows.Count).End(xlUp).Row
 Sheets("Sheet2").Range("AM2:AM" & lastRow).Copy
 Sheets("Sheet1").Range("A11").PasteSpecial xlPasteValues

 lastRow = Sheets("Sheet2").Range("AN" & Rows.Count).End(xlUp).Row
 Sheets("Sheet2").Range("AN2:AN" & lastRow).Copy
 Sheets("Sheet1").Range("B11").PasteSpecial xlPasteValues

Lastly, i changed Worksheets(1) to calling the sheet by name, because I find this method is more intuitive and allows you to identify which sheets you're copying and pasting from, even after you or a user has shifted the page order around.

So your final code will look like the below. There are definitely cleaner ways to write all this (including turning a lot of the repeated code, like sheets into variables), but I think this is the logical next step from the code you're currently writing:

Dim LastRow as long

     lastRow = Sheets("Sheet2").Range("AM" & Rows.Count).End(xlUp).Row
     Sheets("Sheet2").Range("AM2:AM" & lastRow).Copy
     Sheets("Sheet1").Range("A11").PasteSpecial xlPasteValues

     lastRow = Sheets("Sheet2").Range("AN" & Rows.Count).End(xlUp).Row
     Sheets("Sheet2").Range("AN2:AN" & lastRow).Copy
     Sheets("Sheet1").Range("B11").PasteSpecial xlPasteValues

     lastRow = Sheets("Sheet2").Range("R" & Rows.Count).End(xlUp).Row
     Sheets("Sheet2").Range("R2:R" & lastRow).Copy
     Sheets("Sheet1").Range("E11").PasteSpecial xlPasteValues

     lastRow = Sheets("Sheet2").Range("A0" & Rows.Count).End(xlUp).Row
     Sheets("Sheet2").Range("A02:A0" & lastRow).Copy
     Sheets("Sheet1").Range("G11").PasteSpecial xlPasteValues
Rookz
  • 81
  • 4
  • Just read the asnwers of the folks who beat me to the punch :). Looks like most are advocating for the value to value transfer. This method can be faster if you're just moving values across, however, you will need to make certain the ranges are identical (i.e. you can't just paste in a single cell like I did.) So if you are coping from range 2-lastrow and pasting to range 11-Last row, you will have to add the difference in cells to the last row variable. :Sheets("Sheet1").Range("A11:A" & lastRow + 9).value = Sheets("Sheet2").Range("AM2:AM"& lastRow).value. – Rookz Aug 18 '15 at 21:55
  • One user did use the resize method (that I'm not very familiar with) so that might be an effective way to do this. Also, make sure your lastRow is the length you want it to be. I.E. if you want it to capture all the data in column AM, you need to reset the variable to the last cell in that column before copying, not just set it once at the top of the script. – Rookz Aug 18 '15 at 22:00
1

One thing to note is that you can skip the copy/paste part, and just set two range values equal to eachother:

Worksheets(2).Range("AM2:AM" & lastRow).Copy
 Worksheets(1).Range("A11:A" & lastRow).PasteSpecial xlPasteValues

is the same as

Worksheets(1).Range("A11:A" & lastRow).Value = Worksheets(2).Range("AM2:AM" & lastRow).Value

As for working with different sheets, I highly recommend creating two variables to hold these, and work directly (and explicitly) with each sheet:

Sub test()
Dim ws1 As Worksheet, ws2 As Worksheet

' Let's set "Sheet1" to be ws1, and "Sheet2" to be ws2
Set ws1 = Worksheets("Sheet1")
Set ws2 = Worksheets("Sheet2")

'Now, use WITH to work in a specific sheet.
With ws1
    .Range("A1").Value = "Cell A1" ' Note the . at the beginning of Range().  This makes sure that the range you're using is
    ' on sheet1, not any other sheet

    'The next line is the SAME as using Range("A1"), only using Cells(row,Column). NOTE the . before Range() AND Cells()
    .Range(.Cells(1, 1), .Cells(1, 1)).Value = "Cell A1"
End With

With ws2
    .Range("A1").Value = "Sheet2, Cell A1"
End With

'Let's now say you want sheet1, A1 to be put in Sheet2, A1:

' [copy TO range] = [where you want to copy FROM]
ws2.Range("A1").Value = ws1.Range("A1").Value

'Or, of course, we can use ranges
ws2.Range("A1:B100").Value = ws1.Range("A1:B100").Value
' is same as
ws2.Range(ws2.Cells(1, 1), ws2.Cells(100, 2)).Value = ws1.Range(ws1.Cells(1, 1), ws1.Cells(100, 2)).Value
' is same as
With ws2
    .Range(.Cells(1, 1), .Cells(100, 2)).Value = ws1.Range(ws1.Cells(1, 1), ws1.Cells(100, 2)).Value
End With

End Sub

Hopefully the above is clear, and you can see how to edit your macro to make sure to pull data and paste data to the right pages.

Edit: Andddd I just noticed you did set some worksheets already - so just use the above with those. After re-reading, I think I explained your question (or at least part of it)...

Edit2: Wow, okay so I completely misunderstood your question. Sorry about that! I figure I will leave this here though as it does explain setting values, no copy/paste. Sorry mate, I'll have another look at your question. (If someone more familiar with StackOverflow can let me know, should I keep this answer here, or just delete completely? What's the forum guideline?)

BruceWayne
  • 22,923
  • 15
  • 65
  • 110
0

I appreciate the answers given, I learned something from all of them. What I ended up needing to do to get the macro to run and copy the data over exactly right was:

calcState = Application.Calculation
'when this was turned off before the next coding it didn't copy over the company name properly (column A)
Application.Calculation = calcState

'Puts the coding into the JE Sheet
Call FillInCoding

'turning it back off to speed up the final bits of coding
Application.Calculation = xlCalculationManual

I'm not really sure why having Calculation mode as Manual instead of Automatic makes it so that sometimes the columns weren't copying over correctly in the example I listed in column A instead of copying (from the second sheet to the first) over something like

A1 "XYZ"
A2 "YZA"
A3 "GHI"
A4 "XYZ"

it was putting:

A1 "XYZ"
A2 "XYZ"
A3 "XYZ"
A4 "XYZ"

Sorry I think I wasn't the clearest that is what was happening, my coding was working overall by itself but @Jeeped's is clearly cleaner, simpler, and just better. I'm still fairly new to coding generally and this site.

I got the idea on turning those options off of automatic to manual from https://blogs.office.com/2009/03/12/excel-vba-performance-coding-best-practices/ It does make the parent macro significantly faster to do as is suggested there, but I guess it can cause issues.

halfer
  • 19,824
  • 17
  • 99
  • 186
MadChadders
  • 127
  • 1
  • 11
  • Did you remember to turn calculation back on at the end? `Application.Calculation = xlCalculationAutomatic`? If you're copying Values, I'm not sure why they're not copying correctly - however, if it's formulas, the formulas won't update themselves (calculate) until you tell Excel to (via `xlCalculationAutomatic`). Also, you can avoid Copy/Pasting altogether if you instead set the ranges equal. Can you post your code as you have it now - with the calculation turned off, and the copying, so I can try to recreate your error? – BruceWayne Aug 19 '15 at 18:01
  • @BruceWayne to recreate it just take the sub that Jeeped used above, have it be called by another sub like my note mentioned 'Sub MasterSub() calcState = Application.Calculation Application.Calculation = xlCalculationManual Call FillInCoding Application.Calculation = calcState End Sub' When done that way it does not copy over the company codes (Column A, they are all 3 letter codes) properly into the second sheet. It copies one and puts the same thing in all the area it is pasting to.But if it stays on Auto calculation (the excel default) then it does work. – MadChadders Aug 19 '15 at 18:36
  • The coding mentioned above by @Jeeped works like a charm by itself too, it's something odd about Calculation=Manual vs Calcuation=Auto (that's the default). – MadChadders Aug 19 '15 at 18:39