I've been working all week to prepare a VBA application, which I'll be using in a meeting today. Unfortunately the code that has been running all week last week without a hitch, has decided to break over the weekend.
I constantly get Object variable or With block variable not set Run-time error '91' from this statement:
With Sheet5
Set adjrng = .Range(.Cells(.Range("G43:G60").Find(.Range("H39").Value).Row, 10), .Cells(.Range("G43:G60").Find(.Range("H39").Value).Row, 21))
End With
idea is to set a range in the row of the Range G43:G60 where the Value of H39 matches from Column 10 to Column 21.
Anybody spot the issue? My brainz are to nervous and sleepy this morning...
Thanks a bunch
Ben
EDIT:
After playing a bit with find and replace, the issue seems to be that excel has not yet properly calculated the "lookin" and "lookup" Ranges G43:G60 and H39. A simple recalculation didn't make excel rediscover the contents but when I used one of my input toggles to display a different value in those cells, and the went back to the original it did manage to find it.
Maybe using find for this is bad style, the find formula has these kind of hicups usually or any other comments on this? For now everything works fine again, but I'm afraid of running into these issues again. Any tips would thus still be appreciated.
EDIT: (from comment below)
We have a dynamic range (G43:J60) where unique identifiers are listed in column G and data is to the right. if something is changed in the data part of the range AND the lines uniqued identifier in column G matches the one in cell H39 a sub() is triggered via worksheet_ change intersect(target, adjrng) Defining that adjrng is the part that throws errors when find returns null.
I believe you are simply trying to set a range hoping that there will be two matches to the value in H39 within the G43:G60 range. While I avoid on Error Resume Next (never could adjust to the logic of breaking something in the hope to accomplish something), I always check that the values will be there when I look for them.
Dim rwUNIQ as long
Set adjrng = nothing
With Sheet5
if cbool(application.countif(.Range("G43:G60"), .Range("H39").Value)) then
rwUNIQ = application.match(.Range("H39").Value, .Range("G43:G60"), 0)
Set adjrng = .Cells(42 + rwUNIQ, 10).resize(1, 11)
end if
if not adjrng is nothing then
'do something with adjrng
end if
Set adjrng = nothing
End With
That checks to make sure that there are at least two H39 values in G43:G60 before proceeding. There is no further error control because we've counted at least two of them. You might want to compensate with an Else for when there isn't. If a single H39 value was found, you also might want to select a single row.
Remember that the .Find uses many parameters that were retained from the last time Find was used, whether in VBA or with a user on the worksheet. You have a real lack of parameters that specify the options that Find should use to proceed. e.g. xlPart or xlWhole, After:=what?, look in formulas or values, etc.
EDIT:* Modified the code to look for a single instance of the value in H39 and .Resize to expand the width (as per OP's comments).
Related
For some weird reason when cell B3 has a value "48987" and it is formatted as general (number, not string because it is right aligned) the find function works but when it is formatted as a comma separated number, the find function is unable to find it. The code given below is what I'm trying.
Set Loc = ActiveSheet.Range("B2:B10").Find("48987", LookIn:=xlValues)
If Not (Loc Is Nothing) Then
Debug.Print Loc.Address
End If
Note: I've tried with/without double quotes and the lookin options
But the moment I change the code to the one given below it works!!
Set Loc = ActiveSheet.Range("B2:B10").Find("48,987", LookIn:=xlValues)
I've restarted my system and done a lot of search but I'm unable to find a solution. This happens with dates because they are stored as an integer in excel but I don't know why this would happen with a number. Plus the same cell does get detected when I change the condition
Since you asked ...
When using the Find method and its LookIn parameter, xlFormulas ignores formatting (so if comparing with a range is more comparable to the Value property) whereas xlValues does not (so is actually more comparable to the Text property). Therefore, in most cases xlFormulas is preferable as it searches contents and formulas.
It's also good practice to specify other Find parameters as they can be inadvertently changed (via the worksheet) and may not be set to the values you expect.
So I have a large suite of code that creates an archive of data in sheets used by employees. Part of what makes this functional is named ranges on each sheet of usable data. In order for the data integrity to remain, I need to copy the named range objects from the archive sheet to its copy. The named ranges are built programatically and function as expected on the sheets. The problem I'm having is when I go to archive the sheet. Here is the code I'm using to handle the named range object:
For Each n In OldSht.Names
NamedRangeRefersTo = n.RefersTo
NamedRange = n.Name
TrimmedName = Right(n.Name, Len(n.Name) - InStr(1, n.Name, "!", vbTextCompare))
OldSht.Names(n.Name).Delete
OldSht.Names.Add Name:=ArchiveNamedRange, RefersTo:=NamedRangeRefersTo
Next n
The strings that grab data from n are used to add the same name object to the new sheet.
The problem I'm having is when a named range is referencing too large of a range when it hits the line Oldsht.Names.Add, it returns error 1004. I figured out it was the size of the referenced range by messing around with it. I haven't found the exact triggering cause, but this code works as-is when I use it on most of the named ranges. On large data sets with a joined data type that results in a very large named range (it would take a long time to explain how the ranges are built in text. It's a group of 8 sub functions with over 2000 lines of code), this results in the 1004 error.
What I'm confused by is why I can build the named range, use the named range, and copy the named range without issues (if I comment out the offending line, it executes perfectly but I lose data integrity). But when I take the referenced range into a code value, delete the old name reference, then add a new name(with a different name) and assign it the same refersto value of the old name, it can have this problem. I don't understand how it would be different doing this rather than just copying/renaming the name object. Unfortunately, I haven't found a workaround as of yet, nor have I found a clear cause of this error other than the fact that when I remove data or use smaller sets of data in test scenarios, I never have the problem. Does anyone have any ideas of what I can do? Does anyone have any ideas how a named range could be referring to a small enough range that it can be created, but using its refersto value to create a new named range could cause errors only when that is referencing a large range?
I wish I could provide some more concrete examples but unfortunately it would be very difficult to scrub enough sensitive info to provide the full code that would be necessary to reproduce my exact scenarios. Any ideas would be much appreciated.
As requested here's where ArchiveNamedRange gets set:
If Len(OldSht.Name) > 21 Then
ArchiveShtName = Left(OldSht.Name, 21) & DatePart("m", Date) & DatePart("d", Date) & DatePart("yyyy", Date)
Else
ArchiveShtName = OldSht.Name & DatePart("m", Date) & DatePart("d", Date) & DatePart("yyyy", Date)
End If
ArchiveNamedRange = ArchiveShtName & NameObjectName & "Test"
NameObjectName is just the name of the type of object and is passed in from another function. I'm not having an issue with the name just fyi. In the most extreme example the ArchiveNamedRange value at debug run time is = "OutageSystemProcedureMMDDYYYYSecurityRedactionTest" so the name might reach 50 and if things get crazier it might run upwards of 60 characters but it won't ever go beyond that or come anywhere near the 255 character limit. Ultimately, I haven't seen ArchiveNamedRange have an invalid value. It's just a string and it always has a value.
Edit-
Through my troubleshooting I've found that my code works when NamedRangeRefersTo has a length of 2075, but does not work when it has a length of 2091. So somewhere between 2075 characters and 2091 characters is a breaking point for assigning a string to RefersTo: in a named range.
So let's just assume there is a character limit for some reason of 2080 (or whatever it actually is between 2075 and 2091). When I initially find and create these named ranges, they are being given a range object. When I am copying the ranges I am copying as a string. Somehow when I pass a ranged object into RefersTo: it accepts characters beyond 2080 but when I pass in a string it does not. Given that this is my only breaking point of a large suite of code I'd rather find a workaround for this than have to re-factor the entire concept of my archive system. If I use a range object for copying the named ranges, their references follow the old Sheet. That means that when I copy the name over it can be "CriticalSystemsTest1" and referto: "CriticalSystemsTest1!$A$2,..." but once I copy that over and rename the archive worksheet (now CriticalSystems562015) the references adjust to be "CriticalSystems562015Test1!$A$2,..."
So I had to copy as a string to avoid that problem (it breaks data on the new sheet). All I really need is a creative way to overcome this character limit issue on my string. Rebuilding the named range from scratch on the new sheet is also not going to work. So I guess if anyone has ideas for how to work around this string size issue or a way of trimming the string while maintaining functionality of the named range, that would be amazing.
Each of these names has a worksheet level scope, so maybe if there's a way of using just the cell address($A$2) in RefersTo: so it doesn't also contain the worksheet reference (SheetName!), that would be a potential solution but I haven't figured out if that's even possible.
The reason the range definitions as strings are so long is that there are many areas within them. So one workaround would be to build up a new Range object area by area. You can use the string address of each area without running into any limits as each area only has a short reference. Using Range.Address gets the cell reference without the sheet reference, so you can create a new Range on a different sheet but with the same cells. Then use Union() to join all the areas and create the new name using the newly built Range instead of a string:
Dim i As Long, oldRange As Range, newRange As Range
Set oldRange = n.RefersToRange
Set newRange = oldSht.Range(oldRange.Areas(1).Address(External:=False))
For i = 2 To oldRange.Areas.Count
Set newRange = Union(newRange, oldSht.Range(oldRange.Areas(i).Address(External:=False)))
Next i
oldSht.Names.Add Name:="ArchiveNamedRange", RefersTo:=newRange
A couple of notes:
For ranges with many areas this is slow. If you can reliably tie down the threshold where you have problems, it would probably be worth testing for this first and only using this workaround where it was needed.
When testing I also ran into problems with using Worksheet.Range("some very long string range reference"), so this limitation isn't confined to named ranges.
I am writing an Excel sheet with some buttons that run VBA.
each button essentially picks a cell at the beginning of the range that it will eventually use, a length(number of cells that will be used), and a couple of other parameters entered by the user, and then sends the information to a sub, which resizes the Range that currently contains one cell to the number of cells entered in the length
When I use
Sub GetFromCTLGX(RangeToFill As Range, Name, TagName, TagLength)
RangeToFill = RangeToFill.Resize(1, TagLength)
my Range, "RangeToFill" is not changed at all.
If I use
Sub GetFromCTLGX(RangeToFill As Range, Name, TagName, TagLength)
RangeToFill.Resize(1, TagLength)
the code faults and doesn't compile.
has anyone else run into this problem?
am I missing something?
I found the mistake in my code,
it should have been
Set RangeToFill= RangeToFill.Resize(1, TagLength)
it's odd though.... it still doesn't explain to me why just
RangeToFill.Resize(1, TagLength)
wasn't working, that's what is used in a majority of the examples I saw.
I'm surprised that there wasn't an error for not having the Set keyword there.
This example resizes the RangeToFill to extend it by one row and one column.
http://msdn.microsoft.com/en-us/library/office/ff193274%28v=office.15%29.aspx
numRows = RangeToFill.Rows.Count
numColumns = RangeToFill.Columns.Count
Set RangeToFill = RangeToFill.Resize(numRows + 1, numColumns + 1)
Easily adaptable to your code
Basically what I'm trying to accomplish is to search the document for blank rows and delete them, if any. This works great if there are blank rows to delete; however, if there are no blank rows, the macro ends with an error. I'd be eternally grateful if someone could advise me how to make this into an "if blank rows then this, if none then that"
Sheets ("xml") .Select
Cells.Select
Selection.SpecialCells(x1CellTypeBlanks).Select
Selection.EntireRow.Delete
Enter my second macro (this part works fine)
Regards
Let me point you to the canonical:
How to avoid using Select/Activate in Excel VBA macros
So you can start to understand why your current code fails or performs undesired operation. What happens when there are no blank cells in your selection? You'll get an error. Why?
Because in that circumstance, Selection.SpecialCells(xlCellTypeBlanks) evaluates to Nothing. (You can verify this using some debug statements) And because Nothing does not have any properties or methods, you'll get an error, because you're really saying:
Nothing.Select
Which is a null program, does not grok, does not compute, etc.
So, you need to test for nothingness with something like this:
Sheets("xml").Select
Cells.Select
If Not Selection.SpecialCells(x1CellTypeBlanks) Is Nothing Then
Selection.SpecialCells(x1CellTypeBlanks).EntireRow.Delete
End If
I still suggest avoiding Select at all costs (it is superfluous about 99% of the time and makes for sloppy code which is difficult to debug and maintain).
So you could do something more complete following that line of thought:
Dim blankCells as Range '## Use a range variable.
'## Assign to your variable:
Set blankCells = Sheets("xml").Cells.SpecialCells(xlCellTypeBlanks)
'## check for nothingness, delete if needed:
If Not blankCells Is Nothing then blankCells.EntireRow.Delete
Follow-up from comments
So in VBA we are able to declare variables which represent objects or data/values, much like a maths variable in an equation.
A Range is a type of object part of the Excel object model, which consists of the Workbook/Worksheets/Cells/Ranges/etc. (far more than I could hope to convey to you, here)
http://msdn.microsoft.com/en-us/library/office/ff846392(v=office.14).aspx
A good example of why to use variables might be here if you scroll down to the "Why Use Variables" section.
http://www.ozgrid.com/VBA/variables.htm
This is of course very simple... but the reader's digest version is that variables allow us to repeatedly refer to the same object (or value for sipmle data types) without explicitly referring to it each time.
THen there is the handy side-effect that the code bcomes more easy to read, maintain and debug, when we use variables instead of absolute references:
Dim rng as Range
Set rng = Sheets(1).Range("A1:Q543").Resize(Application.WorksheetFunction.CountA(Sheets(1).Range("A:A"),))
Imagine that fairly (but not ridiculously) complicated range construct. If you needed to refer to that range more than once in your code, it would be silly not to assign it to a variable, if for no other reason than to save your own sanity from typing (and possibly mistyping a part of it). It is also easy to maintain, since you need only modify the one assignment statement and all subsequent references to rng would reflect that change.
I have a table of data with the top row being filters, I have a loop that changes which filter needs to be used inside the loop is the variable filterColumn that is being assigned a new value every time the loop runs through.
when i try to use filterColumn to determine which filter will be 'switched on' i get an error
Autofilter method of Range Class Failed
ActiveSheet.Range("$U$83:$CV$1217").AutoFilter Field:=filterColumn, Criteria1:="<>"
What is the correct syntax in order to use a variable to determine which field the filter is in?
Problem Solved I found the solution. I was referencing the filters columns position in terms of the whole worksheet when in fact I should have been referencing what number it was in the group of filters. For example the filter I wanted to change was in 'CF' which is the 84th column but my the filter I wanted to change is the 64th in the group.
Dim filterColumn As Integer
filterColumn = 2
ActiveSheet.Range("$U$83:$CV$1217").AutoFilter Field:=filterColumn, _
Criteria1:="<>"
EDIT: I tried #HeadofCatering's solution and initially it failed. However I filled in values in the referenced columns and it worked (my solution also failed under reverse conditions - make the column headers blank and it fails).
However this doesn't quite mesh with what I've (and probably you've) seen - you can definitely add filters to columns with blank headers. However one thing was consistent in the failures I saw - the filterColumn referenced a column that was outside of Application.UsedRange. You may want to try verifying that the column you are referencing is actually within Application.UsedRange (easy way: run Application.UsedRange.Select in the Immediate Window and see if your column is selected). Since you are referencing a decent amount of columns, it is possible that there are no values past a certain point (including column headers), and when you specify the column to filter, you are actually specifying something outside of your UsedRange.
An interesting (this is new to me as well) thing to test is taking a blank sheet, filling in values in cells A1 and B1, selecting columns A:G and manually adding AutoFilters - this will only add filters to columns A and B (a related situation can be found if you try to add filters to a completely blank sheet).
Sorry for the babble - chances are this isn't even your problem :)
Old solution (doesn't work when conditions described above are used)
I may be overkilling it, but try setting the sheet values as well (note I used a sample range here):
Sub SOTest()
Dim ws As Worksheet
Dim filterColumn As Integer
' Set the sheet object and declare your variable
Set ws = ActiveSheet
filterColumn = 2
' Now try the filter
ws.Range("$A$1:$E$10").AutoFilter Field:=filterColumn, Criteria1:="<>"
End Sub