ActiveCell VBA bugs out and crashes Excel completely - excel

I cannot figure out why this happens (it only happens intermittently, but always with the same function)...
I am inserting a few copied rows wherever the user happens to be, and checking if a page break is necessary between them.
I managed to recover Excel after it crashed one time, and found it stalling on a line with an ActiveCell reference. I apostrophe'd the line out, and the code continued successfully line by line to the next ActiveCell reference.
I reopened the template file with the code in it, and it worked just fine.
I know it's bad practice to use ActiveCell, but I don't know how to get around it in this case - I need to add the rows right where the user is.
Should I do something like this?
Dim R As Range
Set R = ActiveCell.Address
Will that keep the original ActiveCell address or will it dynamically update as the code runs and the ActiveCell changes?
Your help is much appreciated!
[Edit]: Code (please excuse untidiness it's in development):
Sub InsertArea()
'Dimension variables
Dim SR
Dim Rng2 As Range
Dim i, j, PB1 As Integer
Dim Crit() As String
Dim w As Worksheet
i = 2
j = 0
PB1 = 0
Set Rng = Nothing
'NEW PAGE
'Check for page height breach
'Assign PB1 to selection row
PB1 = Selection.Row
'Reset i to 1
i = 1
'Loop how many extra blank rows you want below the bottom spec on a page
Do Until i = 17
'If there's a page break above row i
If Rows(PB1).Offset(i, 0).EntireRow.PageBreak <> xlPageBreakNone Then
'Copy blank row
Range("A1000:A1006").EntireRow.Copy
Selection.EntireRow.Insert Shift:=xlDown
'Insert page break just above the new area
Rows(PB1).Offset(4, 0).PageBreak = xlPageBreakManual
Selection.Offset(7, 0).Select
i = 17
Else
'Increment i to prevent infinite loop
i = i + 1
End If
Loop
'INSERT NEW AREA
'Copy blank new area
ActiveWorkbook.Names("Temp_NewArea").RefersToRange.EntireRow.Copy
'Paste (insert) that line by shifting cell up, so target cell remains in the new blank row
BUGS HERE
ActiveCell.EntireRow.Insert Shift:=xlUp
'ASSIGN NEW AREA WITH A NEW NAME
SR = ActiveWorkbook.Names("Spec1").RefersToRange.Address
'Amend selection to Quoted Specifications
ActiveCell.Offset(2, 7).Resize(4, 1).Select
'ADD THE NEW AREA TO SPECIFIED_RANGES
'Add that specified range to string SR, comma separated
SR = SR & ":" & Range("Quote_End").Offset(-3, 1).Address
'Create/Overwrite (by default) Specified_Areas range using string SR
ActiveWorkbook.Names.Add "Specified_Ranges", "=" & SR
ActiveCell.Offset(4, -7).Activate
Application.EnableEvents = True
Application.ScreenUpdating = True
Application.CutCopyMode = False
End Sub

Instead of using ActiveCell throughout the code, you should immediately assign it to a variable and utilize that variable instead.
Sub InsertArea()
'Dimension variables
Dim SR
Dim Rng2 As Range
Dim i, j, PB1 As Integer
Dim Crit() As String
Dim w As Worksheet
'Then declare your cell in question
Dim myCell As Range
Set myCell = ActiveCell
This will prevent cases where the ActiveCell inadvertently changes while the code is running.
Also, within your declarations
Dim i, j, PB1 As Integer
i & j are declared as type Variant, while PB1 is declared as type Integer. While it is perfectly acceptable to declare more than one variable on a single line, these declarations must be done explicitly, such as:
Dim i As Integer, j As Integer, PB1 As Integer
- OR -
Dim i%, j%, PB1%
The % is the VB symbol for Integer, and may be used in declaring variables.
Posted as answer per OP request

Related

How do I loop through a data set and perform various actions depending on the contents?

I have a program that can generate a set of data that will end up formatted like this example.
Sample Data
I want to pull values from this data and generate text in a text file by using an Excel macro. I have figured out how to edit the text file in the manner I need to, but I am having a lot of trouble actually moving through the data.
I'm not sure my actual code will be that helpful because it's really not functional. Instead I'll try to annotate it with how I was thinking. I'm not experienced with VBA or programming in general and I think what I actually tried without any annotation would hurt more than it would help.
Sub Macro2()
'Declare variables
Dim DIRECTORY As String
Dim SCRPATH As String
Dim COORD As String
Dim PART as String
'Define where txt file is located (it will always be in same location as workbook and have the same name)
DIRECTORY = ActiveWorkbook.Path
SCRPATH = DIRECTORY & "\sample.txt"
'The data being read will always be on the same sheet
With Sheet1
For Each row In Range("A2:A500")
'For each row below the headings, loop through and do the same set of actions
'I have this set as A2:A500 because I know for sure there will never be more than 500 rows of data
'Would it be better to count the rows that have data and then repeat the loop that many times?
'There should never be blank rows breaking up the data
If Not IsEmpty(ActiveCell.Value) Then
'If there is data in that row, then continue. If there is no data, end the macro
For Each col In Range("F:J")
If Not IsEmpty(ActiveCell.Value) Then
'For every cell with data in the F to J columns, perform an action. When there is no more data, move to the 'next row
COORD = ActiveCell.Value
'Set COORD to the value of the cell so that it can be printed to the text file
Open SCRPATH For Append As #1
Print #1, COORD
Close #1
End If
Next
End If
Next
End With
End Sub
This ended up looping forever and causing my excel to crash. I believe that is because there is nothing that ever tells the active cell to move. I am unsure of the best way to go about moving the active cell, however. I would also like to print the value in the "part" cell but am unsure of the best place to put that function.
Sample Output
This is what I would like my output to look like, for all rows of data.
Using VBA, it's very rare you need to use active cell to get the value.
Sub Macro2()
Dim ws As Worksheet, fso, ts, n As Long
Dim lastrow As Long, lastcol As Long, i As Long, j As Long
Dim DIRECTORY As String, SCRPATH As String
DIRECTORY = ActiveWorkbook.Path
SCRPATH = DIRECTORY & "\sample.txt"
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.createTextFile(SCRPATH)
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = 2 To lastrow
ts.writeline .Cells(i, "B")
lastcol = .Cells(i, .Columns.Count).End(xlToLeft).Column
For j = 6 To lastcol
ts.writeline .Cells(i, j)
n = n + 1
Next
ts.writeline ""
n = n + 2
Next
End With
ts.Close
MsgBox n & " lines written to " & SCRPATH, vbInformation
End Sub

Set a range object with variables and offset from an activecell

Can someone pls point out my error.
I'm trying to set a range object to be comprised of a Range1 and an Offset from Range1. There is a table of values I will be passing from into; hence the variables in the range object variable.
Original Error:Run-time Error '1004' Method "Range" of object_global failed.
Essentially I'm looking for (example) result: Range("A2:B94") where A is known, 2-is unknown, B94 is offset n-columns and n-rows from A2.
Below is a revised sub using resize method, yet it still has an error.
Sub comptst()
Dim line0 As Long, nrow0 As Long, ncol0 as long, diff0 As Long
Dim k As Integer
Dim rng0 As Range
Application.DisplayAlerts = False 'turns off alert display for deleting files sub-routine
For k = 6 To Sheets.Count 'tst_1 is indexed at position 5.
ThisWorkbook.Sheets(k).Activate
Set fsobj = New Scripting.FileSystemObject
If Not fsobj.FileExists(Range("A1")) Then MsgBox "File is missing on sheet index-" & k
Cells(1, 1).Select 'find starting row number
Do
If ActiveCell.Value = "Latticed" Then
b0 = ActiveCell.row 'starting row position
Exit Do
Else
ActiveCell.Offset(1, 0).Select
End If
DoEvents '
Loop
Cells(4, 1).Select
Do Until ActiveCell.row = b0
line0 = ActiveCell.Value
nrow0 = ActiveCell.Offset(0, 1).Value
ncol0 = ActiveCell.Offset(0, 2).Value - 1
diff0 = b0 - 1
Set rng0 = ThisWorkbook.Sheets(k).Cells(line0 + diff0, 1).Resize(nrow0, ncol0)
diff0 = diff0 - 1
Debug.Print rng0.Address
ActiveCell.Offset(1, 0).Select
DoEvents
Loop
Next k
End Sub
You can use the Range.Resize-method. Also, it is often easier to use the Cells-property when coding as it takes numeric parameters for row and column.
Also, you should always specify on which sheet you are working, else VBA will use the ActiveSheet and that may or may not be the sheet you want to work with.
You have some issues with your variable declarations:
o Dim line0, nrow0, ncol0 As Double will declare line0 and nrow0 as Variant and only ncol0 as Double. You need to specify the type for every variable.
o You should declare all variables the deal with row or column numbers as Long. Integer may give you an overflow and Double makes not much sense as the numbers have never a fraction part.
If I understand your code correctly you could use
Dim line0 as Long, nrow0 as Long, ncol0 As Long, diff0 As Long
With ThisWorkbook.Sheets(1) ' <-- Replace with the book & sheet you want to work with
Set rng0 = .Cells(line0 + diff0, 1).resize(nrow0, ncol0)
End With

Updating a dynamic dropdown list in excel upon change in cell value

I am trying to create a form which hopefully updates the list of values for a particular dropdown list automatically (without VBA codes) upon user's input immediately.
Here is the form that the user will see:
Currently, both Columns F and H is based on a data-validation formula:
INDIRECT("VList!"&SUBSTITUTE(ADDRESS(1,MATCH($B11,VList!$1:$1,0),1),"1","")&"2:"&SUBSTITUTE(ADDRESS(1,MATCH($B11,VList!$1:$1,0),1),"1","")&COUNTA(INDIRECT("VList!"&ADDRESS(1,MATCH($B11,VList!$1:$1,0),4)&":"&ADDRESS(100,MATCH($B11,VList!$1:$1),4))))
... where VList refers to the sheet as shown below:
So my question here is, based on the Project Name in Column B, is there a way to update the list in sheet VList with the value "Cost Per Unit" [Cell E11], so that the dropdown list in F12 and H12 get automatically updated with the value "Cost Per Unit"?
Been researching a long time for this with no avail, so I'm hoping to seek some experts here to see if such a scenario is even possible without VBA. Thanks!
Edit: So I've been told that VBA codes can be triggered automatically upon changes in the cell value, so I am open to any solutions/help with VBA as well. Will be researching on that direction in the meantime!
Edit2: Added a simple illustration below that hopefully better depicts what I'm trying to achieve on excel:
*Edit3: I'm starting to explore the Worksheet_SelectionChange method, and this is what I've come out so far:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim projectName As String
Dim VariableList As Worksheet
Dim Form As Worksheet
Dim thisRow As Integer
Dim correctColumn As Integer
Dim lastRow As Integer
Set VariableList = ThisWorkbook.Sheets("VList")
Set Form = ThisWorkbook.Sheets("Form")
On Error GoTo EndingSub
If Target.Column = 5 Then
thisRow = Target.Row
projectName = Form.Cells(thisRow, 2)
correctColumn = Application.Match(projectName, VariableList.Range("1:1"), 0)
lastRow = VariableList.Columns(correctColumn).Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
VariableList.Cells(lastRow + 1, correctColumn).value = Form.Cells(5, thisRow).value
End If
EndingSub:
End Sub
Somehow the value of Form.Cells(5, thisRow).Value is always empty.
If I change it to Target.Value it still takes the previous value that was being input (e.g. I first put "ABC" as New Variable, it doesn't get updated. I changed New Variable to "DEF", it updates the list with "ABC" instead of "DEF"). It also takes ALL the values that are under Column E somehow.
Also, pressing Enter after I placed one input in E11 also causes both values of E11 and E12 to be updated when only E12 has been changed. However if I click away after E11 is being input, then only E11's value gets updated.
What exactly am I doing wrong here?
I was almost having fun with this one, if anyone can refine the screwed-up parts feel free to amend.
I furthermore recommend using tables. I do realise you can write lengthy formulae to refer to ranges but giving a name to your table gives an expanding list with a simple reference.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim NewVar As Range
On Error GoTo Err
Set NewVar = Range("C:C") 'data entered here, could be a referstorange kind of named range reference
If Application.WorksheetFunction.CountA(Intersect(Target, NewVar)) <> 0 Then Call ertdfgcvb(Target, NewVar) 'only run if there's an intersect, f*ed up but works anyway
Err:
End Sub
Sub ertdfgcvb(Target As Range, NewVar As Range)
Dim ws As Worksheet, Valid As Long, project As String, ListElmnt As String, Unlisted As Boolean, rng1 As Range, rng2 As Range
Set ws = Sheets("VList") 'the data that you refresh
Valid = 2 'projects in column B
HeaderRow = 1 'headers in Vlist are in row #1
uRow = Cells.Rows.Count 'f* yeah, compatibility considerations
For Each Cell In Intersect(Target, NewVar) 'will evaluate for each cell individually, in case you were to insert columns
ListElmnt = Cell.Value2 'stores the prospective list element
r = Cell.Row 'stores the list element's row to...
project = Cells(r, Valid).Value2 'identify the related project
HeaderRowRef = HeaderRow & ":" & HeaderRow
ColumnNum = ws.Range(HeaderRowRef).Find(What:=project, SearchDirection:=xlPrevious, SearchOrder:=xlByColumns, LookAt:=xlWhole).Column 'finds the project in VList
'MsgBox ws.Name
Set rng1 = ws.Cells(HeaderRow + 1, ColumnNum)
Set rng2 = ws.Cells(uRow, ColumnNum)
LastRow = ws.Range(ws.Cells(HeaderRow + 1, ColumnNum), ws.Cells(uRow, ColumnNum)).Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row 'finds the last row for the project in VList 'f*ed up but works
Unlisted = True 'assumes it's unlisted
For x = HeaderRow + 1 To LastRow
If ListElmnt = CStr(ws.Cells(x, ColumnNum).Value2) Then Unlisted = False 'unless proven otherwise
Next
If Unlisted Then ws.Cells(LastRow + 1, ColumnNum) = ListElmnt 'if it's unlisted it gets appended to the end of the list
Next
End Sub
EDIT:
How to purge the table, example:
Sub ert()
Dim rng As Range
Set rng = Range("Táblázat1") 'obviously the table name
Do While x < rng.Rows.Count 'for each row
If rng(x, 1).Value2 = "" Then 'if it's empty
rng(x, 1).Delete Shift:=xlUp 'then delete but retaining the table format
Else
x = x + 1 'else go to the next line (note: with deletion comes a shift up!)
End If
Loop
End Sub

Excel VBA or not to VBA, replace text if different between two cells

I have a quandary, and I don't know if it will work better using excel VBA or not. Thinking about it I believe VBA will work best, but I don't know how to make it work.
I have two pages in a workbook, one is the form, the other is the database, I want the pulldown menu from the form to populate the rest of the form. It does... what I want then is to be able to change the value of the form press submit, and the new data will overwrite the old data.
Is this possible?
Here is the link to the sheet I'm talking about.
http://dl.dropbox.com/u/3327208/Excel/Change.xlsx
Here is the script I am working with now...it takes the sheet, copies everything to a row takes that row, moves it to the NCMR Data tab and then clears the data on the new row from the original sheet.
This code technically could work, but what I need to do is make it use the same concept, but instead of creating a new row at the end of the sheet find the original line and replace the data from B to U in whatever row it was originally in.
I know it's possible, I just don't know how.
'Copy Ranges Variable
Dim c As Variant
'Paste Ranges Variable
Dim p As Range
'Setting Sheet
Set wsInt = Sheets("Form")
Set wsNDA = Sheets("Data")
Set p = wsInt.Range("A14")
With wsInt
c = Array(.Range("B11"))
End With
For i = LBound(c) To UBound(c)
p(i + 1).Value = c(i).Value
Next
With wsNDA
Dim Lastrow As Long
Lastrow = .Range("B" & Rows.Count).End(xlUp).Row + 1
wsInt.Rows("14").Copy
With .Rows(Lastrow)
.PasteSpecial Paste:=xlPasteFormats
.PasteSpecial Paste:=xlPasteValues
.Interior.Pattern = xlNone
End With
With .Range("A" & Lastrow)
If Lastrow = 3 Then
.Value = 1
Else
.Value = Val(wsNDA.Range("A" & Lastrow - 1).Value) + 1
End If
.NumberFormat = "0#######"
End With
End With
End Sub
I found this code:
Sub CopyTest()
Dim selrow As Range, rngToCopy As Range
With Worksheets("PD DB")
Set selrow = .Range("B:B").Find(.Range("BA1").Value)
'find the cell containing the value
Set rngToCopy = Union(selrow.Offset(0, 9), selrow.Offset(0, 12))
'use offset to define the ranges to be copied
rngToCopy.Copy Destination:=Worksheets("Edit Sheet").Range("B50")
'copy and paste (without Select)
End With
End Sub
As far as I can tell this will do what I want mostly, but I can't seem to figure out where to break it up to add it where I need to to make it work the way I want it to.
What I can tell is this, it will copy and paste, but I want to make sure it will paste the data into row it finds, and not overwrite the number of said row.
Can someone help make that possible with the two scripts I have here?
Not tested, but should get you started. I added a 3rd sheet (shtMap) to hold the mmapping between the cell addresses on your form and the column numbers on the "Data" sheet. Useful to name your sheets directly in the VB editor: select the sheet and set the name in the property grid.
*EDIT:*If you want to trigger the transfer on selecting a record id from a list in Range AG3 then place this code in the code module for that worksheet:
Private Sub Worksheet_Change(ByVal Target As Range)
Static bProcessing As Boolean
Dim rng As Range
If bProcessing Then Exit Sub
Set rng = Target.Cells(1)
If Not Application.Intersect(rng, Me.Range("AG3")) Is Nothing Then
bProcessing = True
'this is where you call your macro to transfer the record
bProcessing = False
End If
End Sub
You could use something like this for the transfer:
Public Enum XferDirection
ToForm = 1
ToDataSheet = 2
End Enum
Sub FetchRecord()
TransferData XferDirection.ToForm
End Sub
Sub SaveRecord()
TransferData XferDirection.ToDataSheet
End Sub
Sub TransferData(Direction As XferDirection)
Dim rngMap As Range, rw As Range, f As Range, dataCell As Range
Dim formCell As Range, dataCol As Long, dataRow As Long
Dim sId As String
sId = shtForm.Range("AG3").Value
Set f = shtData.Columns(1).Find(sId, LookIn:=xlValues, lookat:=xlWhole)
If Not f Is Nothing Then
dataRow = f.Row
Else
'what do you want to do here?
' record doesn't exist on data sheet
MsgBox "Record '" & sId & "' not found on '" & shtForm.Name & "' !"
Exit Sub
End If
Set rngMap = shtMap.Range("A2:B10")
For Each rw In rngMap.Rows
'the cell on the edit form
Set formCell = shtForm.Range(rw.Cells(1).Value)
'column # on datasheet
Set dataCell = shtData.Cells(dataRow, rw.Cells(2).Value)
If Direction = XferDirection.ToDataSheet Then
dataCell.Value = formCell.Value
Else
formCell.Value = dataCell.Value
End If
Next rw
End Sub
Matt, there are two approaches I would take. The first is use find(), which returns a range object, then append ".row" so that you'll be able to modify the row on Sheet2 (wsNDA, I think). You may want to test that find() doesn't return Nothing.
Dim foundRow as Long
Dim foundRng as Range
set foundRng = wsNDA.find(wsInt.Range("B11").Value, ...)
If Not foundRng is Nothing Then
foundRow = foundRng.row
End If
'method without check: foundRow = wsNDA.find(wsInt.Range("B11").Value, ...).Row
The other is to use a Dictionary object. I'm not sure what you'd want for the key, but the item could be the row on the data sheet. When you make the change to what's on the form, check against the key and grab its item (the corresponding row) to determine where you need to replace the values.

Deleting entire row whose column contains a 0, Excel 2007 VBA

UPDATE:
Alright, so i used the following code and it does what i need it to do, i.e check if the value is 0 and if its is, then delete the entire row. However i want to do this to multiple worksheets inside one workbook, one at a time. What the following code is doing is that it removes the zeros only from the current spreadsheet which is active by default when you open excel through the VBA script. here the working zero removal code:
Dim wsDCCTabA As Excel.Worksheet
Dim wsTempGtoS As Excel.Worksheet
Set wsDCCTabA = wbDCC.Worksheets("Login")
Set wsTempGtoS = wbCalc.Worksheets("All_TemporaryDifferences")
Dim LastRow As Long, n As Long
LastRow = wsTempGtoS.Range("E65536").End(xlUp).Row
For n = LastRow To 1 Step -1
If Cells(n, 5).Value = 0 Then
Cells(n, 5).EntireRow.Delete
End If
Next
What am i doing wrong? when i do the same thing for another worksheet inside the same workbook it doesnt do anything. I am using the following code to remove zeros from anohter worksheet:
Set wsPermGtoS = wbCalc.Worksheets("All_PermanentDifferences")
'delete rows with 0 description
Dim LastRow As Long, n As Long
LastRow = wsPermGtoS.Range("E65536").End(xlUp).Row
For n = LastRow To 1 Step -1
If Cells(n, 5).Value = 0 Then
Cells(n, 5).EntireRow.Delete
End If
Next
Any thoughts? or another way of doing the same thing?
ORIGINAL QUESTION:
I want to delete all the rows which have a zero in a particular column. I am using the following code but nothing seems to happen:
CurrRow = (Range("E65536").End(xlUp).Row)
For Count = StartRow To CurrRow
If wsDCCTabA.Range("E" & Count).Value = "0" Then
wsDCCTabA.Rows(Count).Delete
End If
Next
StartRow contains the starting row value
CurrRow contains the row value of the last used row
See if this helps:
Sub DelSomeRows()
Dim colNo As Long: colNo = 5 ' hardcoded to look in col 5
Dim ws As Worksheet: Set ws = ActiveSheet ' on the active sheet
Dim rgCol As Range
Set rgCol = ws.Columns(colNo) ' full col range (huge)
Set rgCol = Application.Intersect(ws.UsedRange, rgCol) ' shrink to nec size
Dim rgZeroCells As Range ' range to hold all the "0" cells (union of disjoint cells)
Dim rgCell As Range ' single cell to iterate
For Each rgCell In rgCol.Cells
If Not IsError(rgCell) Then
If rgCell.Value = "0" Then
If rgZeroCells Is Nothing Then
Set rgZeroCells = rgCell ' found 1st one, assign
Else
Set rgZeroCells = Union(rgZeroCells, rgCell) ' found another, append
End If
End If
End If
Next rgCell
If Not rgZeroCells Is Nothing Then
rgZeroCells.EntireRow.Delete ' deletes all the target rows at once
End If
End Sub
Once you delete a row, u need to minus the "Count" variable
CurrRow = (Range("E65536").End(xlUp).Row)
For Count = StartRow To CurrRow
If wsDCCTabA.Range("E" & Count).Value = "0" Then
wsDCCTabA.Rows(Count).Delete
' Add this line:
Count = Count - 1
End If
Next
I got it. For future reference, i used
ActiveWorkbook.Sheets("All_temporaryDifferences").Activate
and
ActiveWorkbook.Sheets("All_Permanentdifferences").Activate
You don't need to use ActiveWorkbook.Sheets("All_temporaryDifferences").Activate. In fact if the ActiveWorkbook is different from wbCalc you would get an error.
Your real problem is that you are using an unqualified reference to Cells(n, 5).Value. Unqualified means that you aren't specifying which sheet to use so it defaults to the active sheet. That may work sometimes but it is poor code. In your case it didn't work.
Instead you should always use qualified references. wsTempGtoS.Cells(n, 5).Value is a qualified reference. wsTempGtoS specifies which worksheet you want so VBA is not left guessing.
Dim LastRow As Long, n As Long
LastRow = wsTempGtoS.Range("E65536").End(xlUp).Row
For n = LastRow To 1 Step -1
If wsTempGtoS.Cells(n, 5).Value = 0 Then
wsTempGtoS.Cells(n, 5).EntireRow.Delete
End If
Next
This: CurrRow = (Range("E65536").End(xlUp).Row) is also an unqualified reference. Instead it should be CurrRow = wsDCCTabA.Range("E65536").End(xlUp).Row.

Resources