Searching a range of columns defined by variables in VBA - vba

I'm trying to search a range of columns on a different worksheet, where the range is defined by two separate variables. I have successfully been able to use the same code to search a range of columns that I manually inputted, but using variables result in an error:
Run-time error '1004':
Application-defined or object-defined error
I am using the code to search a separate worksheet for the column number of the first instance of the month and then search a range beginning with that column number for the specific day.
An example of the worksheet I'm searching through:
http://i.imgur.com/ljmmGGi.png
Below is the code. Specifically, the MonthFind function has worked perfectly, but the subsequent DayFind function, which uses output from MonthFind is acting up.
Private Sub ComboBox21_Change()
Dim i As String
Dim j As String
i = "February"
j = 9
Dim MonthFind As Variant
With Sheets("Project Schedule").Range("A1:ZZ1")
Set MonthFind = .Find(i, LookAt:=xlWhole, MatchCase:=False)
End With
Dim Month1 As Integer
Dim Month2 As Integer
Month1 = MonthFind.Column
Month2 = MonthFind.Column + 12
Dim DayFind As Variant
With Sheets("Project Schedule").Range(Columns(Month1), Columns(Month2))
Set DayFind = .Find(j, LookAt:=xlWhole, MatchCase:=False)
End With
End Sub
Any help would be much appreciated, I've been trying so many different variations of this code to no avail!
Edit - Link to Excel file: https://www.dropbox.com/s/275fo0uucfeum3y/Project%20Scheduling%20SO.xlsm?dl=0

I almost gave up on this, but I found out what the problem was.
Your ComboBox_21 object has an input range (which will fill in the combobox with the selectable values) on the Inputs sheet that uses a bunch of formulas that reference the Project Schedule sheet. Whenever you do all those copy/paste functions against the range in Project Schedule that the combobox relies on, you are effectively changing the data for the dropdown box, and in turn, causing the _Change() event to fire off with every paste that effects that area.
This isn't always a problem (though in my opinion, it's causing a lot of unnecessary code execution), but there's this bit of code that is causing an issue in your AddJob1_Click() event:
Range(Sheet1.Cells(erow, 5), Sheet1.Cells(erow + 3, 10000)).ClearContents
Apparently you're not allowed to perform the Range.Find() method when the contents of the cells that the combobox rely on are being changed.
Here's some info about that, though it's not terribly helpful:
https://msdn.microsoft.com/en-us/library/office/aa221581(v=office.11).aspx
So that's the why, now how to fix it:
Defer automatic calculation until your UserForm code is finished doing whatever it needs to do.
This will ensure that you are allowed to do the Find's and referencing you need to do.
Private Sub AddJob1_Click()
' turn off automatic calculation
Application.Calculation = xlCalculationManual
erow = Sheet1.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
' ... other stuff
' turn calculation back on and perform a calculate, which will fire off the ComboBox21_Change() event
Application.Calculation = xlCalculationAutomatic
Application.Calculate
Unload Me
End Sub

Related

Copy a static range on one sheet, and paste in a dynamic range in another sheet based on a single value in a cell

I have three parts to this problem. I have a single cell with a Week number in Sheet1!A1. I have a static range in Sheet1!B1:F1 that needs to be copied. Then I need to paste the value in a dynamic range in Sheet2 offset by the week number for rows. This is part of a larger macro I am writing for a sheet I use regularly, but I seem to have those parts down. I may be either oversimplifying or oversimplifying but this is what I have currently.
Sub CopyPaste()
Sheets(1).Range("B1:F1").Copy
OffsetRange = Sheets(1).Cells(1,1).Value
Sheets(2).Cells(1+OffsetRange,1).Paste
End Sub
When I run this, it either gives me a Runtime Error 9 or Runtime Error 438.
Anyone know whats causing these errors? When I paste the range, does the cells object point towards the first cell of the copied range when I paste in at the location?
Try it as,
Option Explicit
Sub CopyPasteOffset()
Dim OffsetRange As Long
OffsetRange = Worksheets(1).Cells(1, 1).Value
Worksheets(1).Range("B1:F1").Copy _
Destination:=Worksheets(2).Cells(1 + OffsetRange, 1)
End Sub
The .Paste method is a member of Worksheet, not Range or Cells. You may have it confused with .PasteSpecial which is a member of the Range object. In any event, it is unnecessary as a destination can be applied directly to the copy command.

Excel file decided one day to format all of the cells that were General to Custom [$-409]ddd

I've been using this particular file for a number of years (expense tracking/checkbook). It has a few simple macros, but none of them have acted up after several years of fine-tuning to do anything like the effect I've encountered. I'm running Excel 2013, if that makes any difference to the scenario.
Several months ago I noticed that when entering data into an unused area on one of the worksheets, the result shown would be "Mon", "Tue", etc. Looking at the formatting drop-down list in the toolbar showed Custom, instead of General, as I would have expected (the specific formatting is [$-409]ddd). For a long time I just adjusted the formatting on the new work to whatever I needed it to be (General, Accounting, Percentage, etc) and carried on. It's become frustrating recently and I decided to investigate further.
It appears that ALL of the cells that were normally formatted as General, are really formatted as Custom. Most of the cells I didn't notice it on are simply text like Balance, Contribution, etc. so I didn't realize the formatting had changed. Only the cells that I specifically formatted as Accounting, Number, Percentage, etc. remain unaffected by the blanket "Custom-ization".
I don't have any code in my macros that do blanket changes to [$-409]ddd, only one section of code that applies "mmm dd" on one specific page, and it's hard coded to "mmm dd".
Does anyone have any clues on what may have happened? I'm open to suggestions on how to remedy the situation as well. I'm considering just a brute-force macro that walks through all of the cells in all of the worksheets, checks the formatting against [$-409]ddd and changes them to General.
This can happen if the Normal Style has been corrupted. Examine it (using right-click) and fix if necessary:
Gary's Student is most likely right as to the cause of this. Anyway, if you don't know how to fix it that way or the cause turns out to be something else, here is a brute-force way to remedy the situation in all worksheets in the workbook.
Some words of caution:
1) This will take a very long time to run, since it loops through all cells in the workbook.
2) Make sure you insert the name of the wrong number format exactly right, or it won't work.
3) Make a copy of the workbook in question before you try this to make sure you don't break anything unintentionally.
Sub resetNumberFormats()
Dim sht As Excel.Worksheet
Dim cll As Range
Dim wrongNumberformat As String
Application.ScreenUpdating = False
wrongNumberformat = "[$-409]ddd"
For Each sht In Worksheets
For Each cll In sht
If wrongNumberformat = cll.NumberFormat Then cll.NumberFormat = "General"
Next cll
Next sht
Application.ScreenUpdating = True
End Sub
Edit
The following code is much much faster and works in an instant by me. Try this instead:
Sub setNumberFormats()
Dim sht As Excel.Worksheet
Dim cll As Range
Dim wrongNumberformat As String
' insert VBA code for wrong number format below
wrongNumberformat = "[$-409]ddd"
With Application
.FindFormat.NumberFormat = wrongNumberformat
.ScreenUpdating = False
End With
On Error Resume Next
For Each sht In Worksheets
Do While Err.Number = 0
sht.Cells.Find(What:="*", SearchFormat:=True).NumberFormat = "General"
Loop
Err.Clear
Next sht
Application.ScreenUpdating = True
End Sub

Only some lines throwing "Object variable or with variable not set" error when using .Find method

(I know this sounds like a repeat question, but I've searched everywhere for this, and I couldn't find anything about this)
I'm trying to automate a tax filing process at work. Essentially, I'm reading off several worksheets and populating relevant fields into a master mapping sheet. However (and this is the strange part), when I first coded everything out, there were no errors and I managed to find everything. After saving and reopening the workbook though, I keep getting this error and only for certain lines (9 occurences out of the 57 times I call the .find method in total).
I know the error means that the .find method couldn't find what I was looking for, but I know the field exists since it worked perfectly well before I closed and reopened the workbook, and it works for the rest of the searches. Does anyone have any ideas?
I've checked my macro security settings (everything is enabled), and I know the .find method isn't retrieving anything (I put in "if ___ is nothing" statements to verify, and the affected lines all return nothing when called), but I know the fields I'm searching for exist in their respective sheets.
Here's one of the lines throwing the error:
Range("C7").Select
Set currentRowReference = Worksheets("Stat A").Range("B1:B999").Find("Current year tax losses", LookIn:=xlValues, lookat:=xlPart)
firstCalculation = Worksheets("Stat A").Cells(currentRowReference.Row - 2, "E")
Set currentRowReference = Worksheets("Stat A").Range("A1:A999").Find("Unabsorbed capital allowances c/f", LookIn:=xlValues, lookat:=xlPart)
secondCalculation = Worksheets("Stat A").Cells(currentRowReference.Row - 1, "E")
ActiveCell.FormulaR1C1 = firstCalculation - secondCalculation
Any help or ideas will be much appreciated!
Edit: I've added a larger portion of my code, and a screenshot of the sheet it's supposed to read from. In the code above, firstCalculation successfully computes, while secondCalculation throws the error.
Screenshot of source sheet. Specifically, it's supposed to read A31
since you have to deal with merged cells, you'd better use a function on your own like the following:
Function MyRowFind(rng As Range, textToFind As String) As Long
Dim cell As Range
MyRowFind = -1
For Each cell In rng.SpecialCells(xlCellTypeConstants, xlTextValues)
If InStr(cell.value, textToFind) > 0 Then
MyRowFind = cell.Row
Exit Function
End If
Next cell
End Function
to be used in your main sub as follows:
Sub main()
Dim currentRowReferenceRow As Long
currentRowReferenceRow = MyRowFind(Worksheets("Stat A").Range("A1:A999"), "Unabsorbed capital allowances c/f")
If currentRowReferenceRow > 0 Then
' code
End If
End Sub

Referring to Dynamic Named Ranges in VBA

I'm having troubling referring to a Dynamic Name Range in VBA.
My ranges are defined as
=OFFSET(Sheet!$B$2,0,0,COUNTA(Sheet!$B:$B)-1,1)
My code should search one range for all entries in another range, the intention being that any missing entries will be added. So far I have
Sub UpdateSummary()
Dim Cell As Range
Dim rngF As Range
Set rngF = Nothing
' Step through each cell in data range
For Each Cell In Worksheets("Aspect").Range("A_Date")
' search Summary range for current cell value
Set rngF = Worksheets("Summary").Range("Sum_Date").Find(Cell.Value) // Does not work
If rngF Is Nothing Then
' Add date to Summary
End If
Set rngF = Nothing
Next Cell
End Sub
The For loop seems to work ok. However, using the .Find method is giving me an error message.
Application-defined or object-defined error
It does work if I replace the named range with a specific range ($B$2:$B$5000), so it seems to be down to how the named range is being passed.
Any ideas would be appreciated.
Thanks.
The error is almost definitely because Excel can't find a named range Sum_Date that refers to a range on a worksheet named Summary. The most common causes are
Sum_Date refers to a sheet other than Summary. Check the RefersTo property of Sum_Date and make sure nothing is misspelled.
There is not a named range Sum_Date, that is, it's misspelled in the VBA code. Check the spelling of the named range in the Name Manager.
There is an error in the RefersTo formula of Sum_Date. It sounds like you already verified that this isn't the case.
I've had the a similar if not the same problem & here's how I solved it:
I first realized that the method I used to create my named range, using the Name Manager, my named range had a scope of Workbook. This is important because, it doesn't belong to the worksheet, & therefore will not be found there.
So, Worksheets("Summary").Range("Sum_Date") would not work for me.
Since my range belonged to the workbook, the way I was able to find is to use ActiveWorkbook.Names("Sum_Date")
For me I used it to remove the formula from named range that I am using in many places. The huge advantage is that named range is updated only once instead of the formula being called for every cell location that ranged is called. Huge time delay difference!
Public last_Selection As String
Private Sub Worksheet_Change(ByVal Target As Range)
'excel data change detection
If Range(last_Selection).Column = 2 Then
'Disable events, so this only executes once
Application.EnableEvents = False
'This can be done with a complex formula in a cell,
'but this is easily understood
Range("B1").End(xlDown).Select
ActiveWorkbook.Names("last_Entry").Value = ActiveCell.Row
'Re-enable so this routine will execute on the next change
Application.EnableEvents = True
End If
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
'constantly store the last cell to know which one was previously edited
last_Selection = Target.Address
End Sub
I know this is a very old thread, but I had the same issue today and I was looking for solution for quite a long time. So maybe this will help someone.
The named "range" defined by the =OFFSET(...) formula is actually a named FORMULA, so in VBA you have to evaluate it first to get the range. E.g.:
Set rgNamedRange = Worksheets("Summary").Evaluate("Sum_Date")
Credits to a guy named "shg" from mrexcel.com, who got me on right track. :)
I have been experimenting with this for a few days and eventually I came up with the following. It may not be the most efficient but it did work for me!
The named range of "OhDear" was set up in the normal way
Dim vItem As Variant
Set vItem = Names("OhDear")
Debug.Print vItem.Name
Worth a try don't you think!
This does not work if instead of using a variant you use something like: Dim Nm as Name: Set Nm = Names("OhDear"). Any variations using 'Nm' failed!!!

How do I count the number of non-zeros in excel?

I am trying to make a macro that will go through a whole workbook and count the number of days a employee worked. The sheets have the work broken out in days so all T have to find is the days that are not zero. I have tried to use COUNTIF(A11:A12,">0") and I get the error Expected : list separator or ). I am using a For Each loop to work through the sheets. I would like to put all the information on a new sheet at the end of the workbook with the name of the employee and the days worked. I am very new to visual basic but am quite good with c#.
I now have gotten this far
Option Explicit
Sub WorksheetLoop2()
' Declare Current as a worksheet object variable.
Dim Current As Worksheet
Dim LastColumn As Integer
If WorksheetFunction.CountA(Cells) > 0 Then
' Search for any entry, by searching backwards by Columns.
LastColumn = Cells.Find(What:="*", After:=[A1], _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious).Column
End If
' Loop through all of the worksheets in the active workbook.
For Each Current In Worksheets
Current.Range("A27") = Application.WorksheetFunction.CountIf(Current.Range(Cells(11, LastColumn), Cells(16, LastColumn)), ">0")
Current.Range("A28") = Application.WorksheetFunction.CountIf(Current.Range("Al17:Al22"), ">0")
Next
End Sub
When I run this I get an error saying method range of object'_worksheet' failed. I also haven't been able to find a way to get the information all on the summary sheet.
VBA Solution, in light of your last comment above.
Good VBA programming practice entails always using Option Explicit with your code, that way you know when you don't have variables declared correctly, or, sometimes, if code is bad! In this case you would have picked up that just writing A27 does not mean you are returning the value to cell A27, but rather just setting the value you get to variable A27. Or maybe you wouldn't know that exactly, but you would find out where your problem is real quick!
This code should fix it for you:
Option Explicit
Sub WorksheetLoop2()
'Declare Current as a worksheet object variable.
Dim Current As Worksheet
' Loop through all of the worksheets in the active workbook.
For Each Current In Worksheets
Current.Range("A27") = Application.WorksheetFunction.CountIf(Current.Range("A11:A12"), ">0")
Next
End Sub
In case it helps, Non-VBA solution:
Assuming you have a Summary sheet and each employee on a separate sheet, with days in column A and hours worked in column B, enter formula in formula bar in B1 of Summary and run down the list of names in column A.