How to pause macro, then do my stuff and continue/resume from where I left? - vba

I got data in one sheet form B2:ZY191, and I want to copy each row (B2:ZY2,B3:ZY3, and so on till B191:ZY191) to another workbook worksheet for analysis. Now while doing so I sometimes need to stop and mark my results in between and then continue from where I left. For example, I started the macro and it copied from B2:ZY2 to B52:ZY52 then I pause the macro & mark my results. Now I want to continue from B52:ZY52 onwards then again if I want to stop after copying data till B95:ZY95 I should be able to pause the macro, mark my result and continue from B95:ZY95 thereon. I should be able to do this as many times as I want.
If provided with buttons like start, pause and resume would be very helpful.

you could adopt the following workaround:
choose the "sets" you want to virtually divide your data range into
let's say:
set#1 = rows 1 to 20
set#2 = rows 21 to 30
... and so on
mark with any character in column "A" the final rows of all chosen sets
so you'd put a "1" (or any other character other than "|I|" or "|E|" - see below) in the following cells of column "A" (i.e. the one preceding your data range):
A21
A31
..., and so on
(since your data starts at row 2 then its ith row is in worksheet row I+1)
then you put the following code in any module of your data range workbook:
Option Explicit
Sub DoThings()
Dim dataRng As Range, rngToCopy As Range
'assuming Analysis.xlsx is already open
Set dataRng = Worksheets("BZ").Range("B2:ZY191") '<--| this is the whole data range. you can change it (both worksheet name and range address) but be sure to have a free column preceeding it
Set rngToCopy = GetCurrentRange(dataRng) '<--| try and set the next "set" range to copy
If rngToCopy Is Nothing Then '<--| if no "set" range has been found...inform the user and exit sub!
MsgBox "There's an '|E|' at cell " _
& vbCrLf & vbCrLf & vbTab & dataRng(dataRng.Rows.Count, 1).Offset(, -1).Address _
& vbCrLf & vbCrLf & " marking data has already been entirely copied" _
& vbCrLf & vbCrLf & vbCrLf & "Remove it if you want to start anew", vbInformation
Exit Sub
End If
With rngToCopy
Workbooks("Analysis").Worksheets("Sheet1").Range(.Address).value = .value
End With
End Sub
Function GetCurrentRange(dataRng As Range) As Range
Dim f As Range
Dim iniRow As Long, endRow As Long
With dataRng
With .Offset(, -1)
Set f = .Resize(, 1).Find(what:="|E|", lookat:=xlWhole, LookIn:=xlValues) '<--| look for the "all copied" mark ("|E|")
If Not f Is Nothing Then Exit Function '<--| if "all copied" mark was there then exit function
Set f = .Resize(, 1).Find(what:="|I|", lookat:=xlWhole, LookIn:=xlValues) '<--| look for any "initial" mark put by a preceeding sub run
If f Is Nothing Then '<--|if there was no "initial" mark ...
iniRow = 1 '<--| ...then assume first row as initial one
Else
iniRow = f.row - .Cells(1).row + 1 '<--| ... otherwise assume "marked" row as initial one
f.ClearContents '<--| and clear it not to found it the next time
End If
endRow = .Cells(iniRow, 1).End(xlDown).row - .Cells(1).row + 1 '<--| set the last row as the next one with any making in column "A"
If endRow >= .Rows.Count Then '<--| if no mark has been found...
endRow = .Rows.Count '<--| ...set the last row as data last row...
.Cells(endRow, 1).value = "|E|" '<--|... and put the "all copied" mark in it
Else
.Cells(endRow, 1).ClearContents '<--| ...otherwise clear it...
.Cells(endRow + 1, 1).value = "|I|" '<--| ... and mark the next one as initial for a subsequent run
End If
End With
Set GetCurrentRange = .Rows(iniRow).Resize(endRow - iniRow + 1) '<--| finally, set the range to be copied
End With
End Function
and make it run as many times as you need: after each time it ends and you can mark your result and then make it run again and it'll restart form where it left

you can use Stop and Debug.Print to achieve the desired results when placed within your code. For example if you're looping through a range, add the statement of choice with an if statement:
for a = 1 to 150
if a = 20 or a = 40 then
debug.Print "The value of a is: " & a.value 'or whatever you want to see
end if
next
This will print to the immediates window, or use stop to pause your code in a strategic place in the same manner.
I dont understand what you mean by buttons? They surely aren't a good idea as the code will run too fast?

Related

Replacing hard value cells with subtotal formula - VBA

Essentially, our system runs off an expenditure listing of cost headings, with a subtotal on each. The issue being we adjust the data, so need to go through and manually turn the hard value subtotals into subtotal formula in each heading; which over hundreds of different headings, with variable numbers of costs, can be tedious and time consuming.
I've built a basic test example whereby for every instance of A (Heading), where the associated B has a value (an element of data from the system for a line of expenditure), the costs (C) will be subtotalled (109,...), replacing the hard copied value.
Sub insertsubtotal()
Dim cell As Range
Dim sumrange As Range
Set cell = Cells(Cells.Rows.Count, "A")
Do
Set cell = cell.End(xlUp)
Set sumrange = cell.Offset(1, 1).CurrentRegion.Offset(1, 2).Resize(cell.Offset(1, 1).CurrentRegion.Rows.Count - 1, columnsize:=1)
If sumrange.Cells.Count > 1 Then
sumrange.End(xlDown).Offset(2, 0).Formula = "=SUBTOTAL(109," & sumrange.Address & ")"
Else
sumrange.Offset(2, 0).Formula = "=SUBTOTAL(109," & sumrange.Address & ")"
End If
Loop Until cell.Row = 1
End Sub
This works whereby the first heading is in A1, and the cost data in column C as below...
However, where I'm struggling is, I need to amend the process to have the first 5 rows ignored (first heading being on 6), and the cost data and subtotal that needs replacing being in column M.
Any help would be appreciated.
Using SpecialCells to divide the UsedRange in Columns("C") into blocks of contant values, will allow you to easily identify and subtotal your data blocks.
Sub insertsubtotal()
Dim Source As Range, rArea As Range
With Worksheets("Sheet1")
On Error Resume Next
Set Source = Intersect(.UsedRange, .Columns("C")).SpecialCells(xlCellTypeConstants)
On Error GoTo 0
If Source Is Nothing Then
MsgBox "No data found", vbInformation, "Action Cancelled"
Exit Sub
End If
For Each rArea In Source.Areas
rArea.Offset(rArea.Rows.Count).Cells(2).Formula = "=SUBTOTAL(109," & rArea.Address & ")"
Next
End With
End Sub

Deletion loop requires multiple runs to delete all rows meeting criteria

I have a msgbox which gets the user to input a value. My workbook then looks up the value in another sheet called 'Values'. In most cases, there are multiple instances of this value in that sheet.
I then take another value from that row (ID) and look for it in a third sheet called 'Req Raw' using the format "[InputValue] [ID]" where ID is a numeric string in the format of "0000".
The workbook then deletes that row in both 'Req Raw' and 'Values' and repeats; continuing to look for the input value in 'Values'.
'SecDelete = Input Value
'VSect = Range in sheet 'Values'
'RReq = Range in sheet 'Req Raw'
With ThisWorkBook.Worksheets("Values")
For Each VSecT In .Range(.Cells(.Cells(Rows.count, 2).End(xlUp).Row, 2), .Cells(15, 2))
If LCase(VSecT.Value) = LCase(SecDelete) Then
'Identify ID
IDF = CStr(VSecT.Offset(columnOffset:=3).Value)
IDF = Format(IDF, "0000")
'Find offset ID in 'Req Raw'
For Each RReq In ThisWorkbook.Worksheets("Req Raw").Range(ThisWorkbook.Worksheets("Req Raw").Cells(ThisWorkbook.Worksheets("Req Raw").Cells(Rows.count, 1).End(xlUp).Row, 1), _
ThisWorkbook.Worksheets("Req Raw").Cells(2, 1))
If RReq.Value = VSecT.Value & " " & IDF Then
'Delete from 'Req Raw'
RReq.EntireRow.Delete
Exit For
End If
Next RReq
'Delete from 'Values'
VSecT.EntireRow.Delete
End If
Next VSecT
End With
I have found that for some reason, a random number of rows are removed rather than all with the input value.
For example, if my input value was "Test 1.0" and there were ten instances in the 'Values' sheet where "Test 1.0" was present, with corresponding IDs 0000, 0001, 0002, ... 0010, only some of the rows would be deleted each time I run the sub. I have to run the sub 7-8 times before all rows with "Test 1.0" are deleted.
Note that I am looping backwards in both For Each statements.
Here is a portion of your code rewritten to accommodate walking backwards through the rows. Note that I have adjusted your string concatenation as well.
Dim rw1 As Long, rw2 As Long
With ThisWorkbook.Worksheets("Values")
For rw1 = .Cells(Rows.Count, 2).End(xlUp).Row To 15 Step -1
If LCase(VSecT.Value) = LCase(SecDelete) Then
'Identify ID
IDF = .Cells(rw1, 2).Value & Format(.Cells(rw1, 2).Offset(columnOffset:=3).Value, " 0000")
'Find offset ID in 'Req Raw'
With ThisWorkbook.Worksheets("Req Raw")
For rw2 = .Cells(Rows.Count, 1).End(xlUp).Row To 2 Step -1
If .Cells(rw2, 1).Value = IDF Then
'Delete from 'Req Raw'
.Rows(rw2).EntireRow.Delete
Exit For
End If
Next RReq
End With
'Delete from 'Values'
.Rows(rw1).EntireRow.Delete
End If
Next rw1
End With
Simply put, you can define a range as .Range("A10:A1") but if you use a For Each/Next to crawl through the cells you will still be progressing through A1, A2, A3.... A10. The numerical row number with Step -1 is the best (only...?) way to work backwards through your data set.
Reason behind this is cell addressing i believe. If you loop through range e.g. a1 to a10 like this
range("a1:a10").select
for each cell in selection
if cell.value = something then
cell.entirerow.delete
end if
next cell
it is analogical to your code, so what happens when row 2 is deleted? all cells shift upwards so macro on next run skips address A2(which in fact was A3 before deleting) because the loop already passed it. There is the hole in the algorythm, what you need is to go back by one row every time macro deletes a row, so you need to rebuild the macro like this(for my example):
for i = 1 to 10
if range("a" & i ).value = something
range("a" & i).entirerow.delete
i = i - 1
next i ' so that each time something is deleted loop steps backward to catch shifted value
hope this helps, cheers
When I have to delete rows from a set range, what I do while mixing the If statements to search for the rows that will be deleted is, for example, change the value of a the cell in a given column (which i know always has values) to blank. then using the specialcells blanks i select all the new blanks cells and then delete the entire row.
I don't like deleting in loops, This code will build a range reference (Rng) and delete all rows in one go at the end.
Sub DeleteBlanks()
Dim Rng As Range, X As Long
For X = 1 To Range("A" & Rows.Count).End(xlUp).Row
If Range("A" & X).Text = "" Then
If Rng Is Nothing Then 'This has to be in here because you can't union a range if it is currently nothing
Set Rng = Range("A" & X)
Else
Set Rng = Union(Rng, Range("A" & X))
End If
End If
Next
If Not Rng Is Nothing Then Rng.Rows.Delete 'If no blanks are found, this will stop it erroring. Only delete when there is something in Rng
End Sub

Need to determine LastRow over the Whole Row

I am not a programmer but have managed to cobble together great amounts of code that work on 4 pretty large projects (Yay for me!) I have tried numerous ways to find the Last Row. Some work for me some don't. I can find a few that give me the "actual" last row regardless of blanks in Column A (this is what I need). Yet I CANNOT for my life figure how to integrate that code with the way I am passing values from my array from one workbook to another. All of the code works "As Is" but I need to find a better way of searching the whole row (currently columns A:O) for the Last Row and then copying the data over. Column A maybe empty at times and to avoid the code from being overwritten, that "Last Row" needs to check the whole row. I am currently forcing a hidden cell (A7) with a "." as a forced placeholder. Any advice would be awesome.
Option Explicit
Public Sub SaveToLog15()
Dim rng As Range, aCell As Range
Dim MyAr() As Variant
Dim n As Long, i As Long
Dim LastRow As Long
Dim NextCell As Range
Dim Sheet2 As Worksheet
Set Sheet2 = ActiveSheet
Application.ScreenUpdating = False
With Sheet2
' rng are the cells you want to read into the array.
' Cell A7 (".") is a needed "Forced Place Holder" for last row _
determination
' A7 will go away once "better" LastRow can be added to this code
Set rng = Worksheets("Main").Range("A7,D22,D19,D20,J22:J24,E23,D21,J25:J27,D62,D63,G51")
' counts number of cells in MyAr
n = rng.Cells.Count
' Redimensions array for above range
ReDim MyAr(1 To n)
' Sets start cell at 1 or "A"
n = 1
' Loops through cells to add data to the array
For Each aCell In rng.Cells
MyAr(n) = aCell.Value
n = n + 1
Next aCell
End With
On Error Resume Next
' Opens "Test Log.xls"
Workbooks.Open FileName:= _
"S:\Test Folder\Test Log.xls"
' SUBROUTINE 1 "Disable Sheet Protection and Show All" REMOVED
' Finds last row on Tab "Tracking" based on Column "A"
' Last row determination DOES NOT go to next row if first _
Column is blank
' Use A7 "." to always force Data to Col A
'**********************************************************************
'THIS WORKS FINE BUT DOES NOT RECOGNIZE THE POSSIBLE BLANK IN COL A.
With Worksheets("Incoming Data")
Set NextCell = Worksheets("Incoming Data").Cells _
(Worksheets("Incoming Data").Rows.Count, "A").End(xlUp).Offset(1, 0)
End With
' I need this code replaced by the following code or integrated into
' this code snippet. I am lost on how to make that happen.
'***********************************************************************
'***********************************************************************
'THIS CODE FINDS THE "ACTUAL" LAST ROW AND THIS IS WHAT I'D LIKE TO USE
' I need to figure how to integrate this code block with the above
' Or maybe redo the whole thing.
LastRow = Cells.Find(What:="*", After:=[A1], _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious).Row
MsgBox ("The Last Row Is: " & LastRow)
' I am not using this code in the program. It's just there to show
' what I need to use because it works. I need to make this code work
'WITH the above block.
'***********************************************************************
' Sets the size of the new array and copies MyAr to it
NextCell.Resize(1, UBound(MyAr)).Value = (MyAr)
' SUBROUTINE 2 "Add borders to cells in range" REMOVED
' SUBROUTINE 3 "Re-enable Sheet Protection" REMOVED
ActiveWorkbook.Save
'ActiveWindow.Close
Application.ScreenUpdating = True
MsgBox "Your Data has been saved to the Log File: " & vbCrLf & vbCrLf _
& "'Test Log.xls'", vbInformation, "Log Save Confirmation"
End Sub
This is a common problem with "jagged" data like:
Clearly here column B has that last row. Here is one way to get that overall Last row by looping over the four candidate columns:
Sub RealLast()
Dim m As Long
m = 0
For i = 1 To 4
candidate = Cells(Rows.Count, i).End(xlUp).Row
If candidate > m Then m = candidate
Next i
MsgBox m
End Sub
:
Find works best for most situations, below is the function i use that takes sheet ref as input and returns row number as type Long
Dim lLastRow As Long
lLastRow = LastUsedRow(shName)
Private Function LastUsedRow(sh As Worksheet) As Long
LastUsedRow = sh.Cells.Find(What:="*", After:=sh.Cells.Cells(1), _
LookAt:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, MatchCase:=False).Row
End Function
The simplest thing might be to use the specialcells method, as in range.specialcells(xllastcell). This returns the cell whose row number is the last row used anywhere in the spreadsheet, and whose column is the last column used anywhere in the worksheet. (I don't think it matters what "range" you specify; the result is always the last cell on the worksheet.)
So if you have data in cells B30 and X5 and nowhere else, cells.specialcells(xllastcell) will point to cell X30 (and range("A1").specialcells(xlastcell) will also point to cell X30).
Instead of:
LastRow = Cells.Find(What:="*", After:=[A1], _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious).Row
MsgBox ("The Last Row Is: " & LastRow)
use this:
LastRow = cells.specialcells(xllastcell).row
MsgBox ("The Last Row Is: " & LastRow)
After 35 attempts this is the code that I was able to hack into my original:
' Used to determine LastRow, LastColumn, LastCell, NextCell
Dim LastRow As Long
Dim LastColumn As Integer
Dim LastCell As Range, NextCell As Range
With Worksheets("Tracking")
' Find LastRow. Works Best. 1st and last cells can be empty
If WorksheetFunction.CountA(Cells) > 0 Then
'Search for any entry, by searching backwards by Rows.
LastRow = Cells.Find(What:="*", After:=[A1], _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious).Row
'Search for any entry, by searching backwards by Columns.
LastColumn = Cells.Find(What:="*", After:=[A1], _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious).Column
'MsgBox "Last Cell" & vbCrLf & vbCrLf & Cells(LastRow, LastColumn).Address
'MsgBox "The Last Row is: " & vbCrLf & vbCrLf & LastRow
'MsgBox "The Last Column is: " & vbCrLf & vbCrLf & LastColumn
End If
' Number of columns based on actual size of log range NOT MyAr(n)
Set NextCell = Worksheets("Tracking").Cells(LastRow + 1, (LastColumn - 10))
End With
This finds the "Real" Last Row and column and ignores any empty cells in Column A or J which seem to affect some of the LastRow snippets. I needed to make it ROWS instead of ROW and HAD the add the Offset portion as well. (-10) puts me back to Column "A" for my sheet and now I have removed Column "A" {the forced Place Holder "."} and have "Real" data there now. YAY for the "Hacking Code Cobbler".
Glad they pay me at work to learn this stuff. :) Solved this a while back. Just now got to update this post.

Broken VBA Loop

I'm sure this is simple I just can't find anything on the Web.
I'm writing a Macro to format XL spreadsheets that i download from a 3rd party application. They come formatted all wacky so i'm trying to make it easier to get the data we need from them.
This is a simple VBA Do Loop that causes the cells in Column BL to update. The data in these cells contain line breaks which don't show up until you double click in the cell. The VBA below causes an update to the cells which achieves the same effect, just with less work. However it is currently crashing excel and I can't figure out why. It works in a single instance, but when I loop -- BOOM!!! -- frozen. Any help would be gently appreciated.
Sub updateCell()
Dim currentValue As String
ActiveSheet.Range("BL1").Select
Do Until ActiveCell.Value = ""
ActiveCell.Offset(1, 0).Select
currentValue = ActiveCell().Value
ActiveCell().Value = currentValue & ""
Loop
End Sub
Try something a bit more direct:
With ActiveSheet
lrow = .Range("BL" & .Rows.Count).End(xlUp).Row '~~> find last row on BL
With .Range("BL1:BL" & lrow) '~~> work on the target range
.Value = .Value '~~> assign its current value to it
End With
End With
Above code is like manually pressing F2 then pressing Enter.
Edit1: Explanation on getting the last row
ActiveSheet.Rows.Count '~~> Returns the number of rows in a sheet which is 1048576
MsgBox ActiveSheet.Rows.Count '~~> run this to confirm
So this line actually concatenates BL to 1048576.
.Range("BL" & .Rows.Count) '~~> Count is a property of the Rows Collection
Same as:
.Range("BL" & 1048576)
And same as:
.Range("BL1048576")
Then to get to the last row, we use Range Object End Method.
.Range("BL" & .Rows.Count).End(xlUp)
So basically, above code go to Cell BL1048576 then like manually pressing Ctrl+Arrow Up.
To return the actual row number of the range, we use the Range Object Row property.
lrow = .Range("BL" & .Rows.Count).End(xlUp).Row
See here more about With Statement.
It has the same effect (with your code) without the loop. HTH
But if what you want is to remove Line Breaks produced by Alt+Enter on a cell, try below:
Dim lrow As Long, c As Range
With ActiveSheet
lrow = .Range("BL" & .Rows.Count).End(xlUp).Row
For Each c In .Range("BL1:BL" & lrow)
c.Value = Replace(c.Value, Chr(10), "")
Next
End With
Where Chr(10) is the equivalent of Line Break replaced with "" using Replace Function.

Creating excel macro to take info from form and append it onto bottom of list

I am currently working on a spreadsheet to help track individuals who attend a weekly meeting conducted by my department. I am trying to automate the process of tracking by using a macro to copy values from a list/form that a member of my department will enter the attendees email and the date. The email and date will then be added together (=a&b) to generate a value and that value will be used to mark whether the individual is present or not at that particular meeting. View Image of form/table
A report is generated after the meeting to tell which individuals have attended and how long they were on the call for. Before I was taking this report and pasting it onto the bottom of the original list but this has become inefficient as the columns and table length have changed. What I would like to do is take the emails, dates, and value on spreadsheet from the calculate tab and have those values append onto the bottom of the list on the reports tab without altering any of the previous information. View Image of reports tab
After the values have been appended to the bottom of the report, I have another tab called meeting dates. This contains a formula that will determine whether the individual was present or not by marking it with either ā€œYā€ or ā€œNā€. Forgot to mention that every week it is the same 17 individuals that are attending these meetings. Eventually I would like to have it so that if the date entered on the calculate tab is not present on the meeting dates tab, add the date to the meeting dates tab.
I am still very new to Excel VB and macros however do have some programming experience. Just not in excel. If somebody could help me, that would be awesome!
This answer is an attempt to get your started.
If you search the internet for "Excel VBA Tutorial" you will get many hits. Try a few because they are all different and pick the one you like best. Work through that tutorial to get a general feel for Excel. I do not believe you will be successful finding bits of relevant code without that general feel.
Do not try to describe your entire problem because I doubt anyone will respond. Instead try to break your problem down into little steps and seek help with those steps.
For example, you will need to determine the number of rows in the post-meeting report so you can access that data. You then want to add that data to the bottom of the previous list. In both cases you need to determine the last used row in a worksheet. "Excel VBA: How to find last row of worksheet?" is a simple question and you will be able to find multiple answers. I give my response to that question below.
I assume the post-meeting report and the list you are creating are in different workbooks. Your macro could be in the same workbook as the list or it could be in a different workbook. Macros can access their own workbooks, any other workbook that happens to be open or they can open as many other workbooks as required. Again "Excel VBA: How do I work with several workbooks?" should result in plenty of hits.
I have not tried either of my questions. I find "Excel VBA:" helps but you may require several attempts before you find the just the right question to get the answer you seek. But if your question is small and precise you should always be able to find an answer.
Let's return to the first question. An irritating feature of Excel VBA is that they are almost always several ways of achieving a similar effect. Create a new workbook, create a module and copy the code below to it. Run the macro FindFinal().
This macro demonstrates several methods of finding the last row and column. Every method has its problems and I have tried to show how how each method can fail. There is a lot of worksheet access within this macro which I believe will repay study. It should help you decide which method is appropriate for each of your requirements.
Option Explicit
Sub FindFinal()
Dim Col As Long
Dim Rng As Range
Dim Row As Long
' Try the various techniques on an empty worksheet
Debug.Print "***** Empty worksheet"
Debug.Print ""
With Worksheets("Sheet1")
.Cells.EntireRow.Delete
Set Rng = .UsedRange
If Rng Is Nothing Then
Debug.Print "Used range is Nothing"
Else
Debug.Print "Top row of used range is: " & Rng.Row
Debug.Print "Left column row of used range is: " & Rng.Column
Debug.Print "Number of rows in used range is: " & Rng.Rows.Count
Debug.Print "Number of columns in used range is: " & Rng.Columns.Count
Debug.Print "!!! Notice that the worksheet is empty but the user range is not."
End If
Debug.Print ""
Set Rng = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByRows, xlPrevious)
If Rng Is Nothing Then
Debug.Print "According to Find the worksheet is empty"
Else
Debug.Print "According to Find the last row containing a value is: " & Rng.Row
End If
Debug.Print ""
Set Rng = .Cells.SpecialCells(xlCellTypeLastCell)
If Rng Is Nothing Then
Debug.Print "According to SpecialCells the worksheet is empty"
Else
Debug.Print "According to SpecialCells the last row is: " & Rng.Row
Debug.Print "According to SpecialCells the last column is: " & Rng.Column
End If
Debug.Print ""
Row = .Cells(1, 1).End(xlDown).Row
Debug.Print "Down from A1 goes to: A" & Row
Row = .Cells(Rows.Count, 1).End(xlUp).Row
Debug.Print "up from A" & Rows.Count & " goes to: A" & Row
Col = .Cells(1, 1).End(xlToRight).Column
Debug.Print "Right from A1 goes to: " & ColNumToCode(Col) & "1"
Col = .Cells(1, Columns.Count).End(xlToLeft).Column
Debug.Print "Left from " & Columns.Count & _
"1 goes to: " & ColNumToCode(Col) & "1"
' Add some values and formatting to worksheet
.Range("A1").Value = "A1"
.Range("A2").Value = "A2"
For Row = 5 To 7
.Cells(Row, "A").Value = "A" & Row
Next
For Row = 12 To 15
.Cells(Row, 1).Value = "A" & Row
Next
.Range("B1").Value = "B1"
.Range("C2").Value = "C2"
.Range("B16").Value = "B6"
.Range("C17").Value = "C17"
.Columns("F").ColumnWidth = 5
.Cells(18, 4).Interior.Color = RGB(128, 128, 255)
.Rows(19).RowHeight = 5
Debug.Print ""
Debug.Print "***** Non-empty worksheet"
Debug.Print ""
Set Rng = .UsedRange
If Rng Is Nothing Then
Debug.Print "Used range is Nothing"
Else
Debug.Print "Top row of used range is: " & Rng.Row
Debug.Print "Left column row of used range is: " & Rng.Column
Debug.Print "Number of rows in used range is: " & Rng.Rows.Count
Debug.Print "Number of columns in used range is: " & Rng.Columns.Count
Debug.Print "!!! Notice that row 19 which is empty but has had its height changed is ""used""."
Debug.Print "!!! Notice that column 5 which is empty but has had its width changed is not ""used""."
Debug.Print "!!! Notice that column 4 which is empty but contains a coloured cell is ""used""."
End If
Debug.Print ""
Set Rng = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByRows, xlPrevious)
If Rng Is Nothing Then
Debug.Print "According to Find the worksheet is empty"
Else
Debug.Print "According to Find the last row containing a formula is: " & Rng.Row
End If
' *** Note: search by columns not search by rows ***
Set Rng = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByColumns, xlPrevious)
If Rng Is Nothing Then
Debug.Print "According to Find the worksheet is empty"
Else
Debug.Print "According to Find the last column containing a formula is: " & Rng.Column
End If
' *** Note: Find returns a single cell and the nature of the search
' affects what it find. Compare SpecialCells below.
Debug.Print ""
Set Rng = .Cells.SpecialCells(xlCellTypeLastCell)
If Rng Is Nothing Then
Debug.Print "According to SpecialCells the worksheet is empty"
Else
Debug.Print "According to SpecialCells the last row is: " & Rng.Row
Debug.Print "According to SpecialCells the last column is: " & Rng.Column
End If
Debug.Print ""
Row = 1
Do While True
Debug.Print "Down from A" & Row & " goes to: ";
Row = .Cells(Row, 1).End(xlDown).Row
Debug.Print "A" & Row
If Row = Rows.Count Then Exit Do
Loop
End With
With Worksheets("Sheet2")
.Cells.EntireRow.Delete
.Range("B2").Value = "B2"
.Range("C3").Value = "C3"
.Range("B7").Value = "B7"
.Range("B7:B8").Merge
.Range("F3").Value = "F3"
.Range("F3:G3").Merge
Debug.Print ""
Debug.Print "***** Try with merged cells"
Set Rng = .UsedRange
If Rng Is Nothing Then
Debug.Print "Used range is Nothing"
Else
Debug.Print "Used range is: " & Replace(Rng.Address, "$", "")
End If
Debug.Print ""
Set Rng = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByRows, xlPrevious)
If Rng Is Nothing Then
Debug.Print "According to Find the worksheet is empty"
Else
Debug.Print "According to Find the last cell by row is: " & Replace(Rng.Address, "$", "")
End If
Set Rng = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByColumns, xlPrevious)
If Rng Is Nothing Then
Debug.Print "According to Find the worksheet is empty"
Else
Debug.Print "According to Find the last cell by column is: " & Replace(Rng.Address, "$", "")
End If
Debug.Print "!!! Notice that Find can ""see"" B7 but not F3."
Debug.Print ""
Set Rng = .Cells.SpecialCells(xlCellTypeLastCell)
If Rng Is Nothing Then
Debug.Print "According to SpecialCells the worksheet is empty"
Else
Debug.Print "According to SpecialCells the last row is: " & Rng.Row
Debug.Print "According to SpecialCells the last column is: " & Rng.Column
End If
End With
End Sub
Function ColNumToCode(ByVal ColNum As Long) As String
Dim Code As String
Dim PartNum As Long
' Last updated 3 Feb 12. Adapted to handle three character codes.
If ColNum = 0 Then
ColNumToCode = "0"
Else
Code = ""
Do While ColNum > 0
PartNum = (ColNum - 1) Mod 26
Code = Chr(65 + PartNum) & Code
ColNum = (ColNum - PartNum - 1) \ 26
Loop
End If
End Function
In the code above, I access worksheet cells directly with statements such as .Range("B2").Value = "B2". This can be slow particularly when you are moving data from one worksheet to another. An alternative approach is to use arrays.
Dim Rng As Range
Dim ShtValues as Variant
With Worksheets("Xxxx")
Set Rng = .Range(.Cells(Row1, Col1), .Cells(Row2, Col2))
End With
ShtValues = Rng.Value
A Variant is a variable that can hold anything including an array. ShtValues = Rng.Value converts ShtValues to a two-dimensional array hold all the values within Rng. Processing values within an array is much faster that accessing them in the worksheet.
.Range(.Cells(Row1, Col1), .Cells(Row2, Col2)) is perhaps the easiest way of creating a range specifying the worksheet area with Cells(Row1, Col1) as the top left cell and Cells(Row2, Col2) as the bottom right.
If I understand correctly, you want to move data from the post-meeting report to the list but the sequence of columns in the report and list are not the same. This suggests you need to move the data as columns. Using .Range(.Cells(Row1, Col1), .Cells(Row2, Col2)) and with Col1 = Col2, you can define a range that is a column.
Rng1.Copy Destination := Cell2
The above statement will copy the contents of Rng1 to the range starting at Cell2. A statement like this for each column of data in the report may be the easiest way of copying the data.
I hope the above gives you a start.