Getting a value from a named range in VBA - vba

I want to retrieve a value from named range. Imagine a named range that has X columns and Y rows. I want to return a value, e.g., from column 2, row 3. The issue I experience is that if I write the code and run it, Excel throws an error. If I write the code into the watch window, it returns fine. See below
...
Dim NamedRange As Variant: NamedRange = Range(NamedRangeName)
...
Dim ReturnValue As Object
Set ReturnValue = NamedRange(RowIndex, ColumnToRetrieveIndex) 'Throws Run-time error 424. Object required
If I write
NamedRange(RowIndex, ColumnToRetrieveIndex)
into the watch window, I can see the correct value of the cell.
I don't know VB much so I guess it's just some kind of syntax error how I want to pass it into the ReturnValue but I just can't figure it out.

Use this
ThisWorkbook.Names("myNamedRange").RefersToRange(1,1)
To get the value from the first cell in the named range "myNamedRange"
With ThisWorkbook.Names you can access all named ranges of all the sheets within the current workbook.
With RefersToRange you get a reference to the actual range.

This looks like a good place to put a handy note (as it's a top search result):
If you name a cell "TopLeft", then Sheets(1).Range("TopLeft").Value gets the contents, and Sheets(1).Range("TopLeft").Offset(2,3).Value gets the value from 2 down, 3 across from there.

Related

Unable to assign value from record set to vba variable (error -2147217887 (80040e21))

I have an excel workbook, for many reasons I am using sql into excel to loop through fields, the workbook, contains values about employees.
I created a record set from the sheet that I am working on with this request :
Set rst = OpenRecordset("SELECT * FROM [Feuil1$]")
this will load my sheet into a record set.
everything were making well till I passed my excel to a friend and he copy pasted data from another sheet into my excel and launched my macro and here I discovered a bug.
When I reach the column "G" Client the line number 13, and try to assign it to a variable, i get an error
I have a variable called Client in which I fill the value of the field client
Dim rst As Object
Dim Client As Variant
Set rst = OpenRecordset("SELECT * FROM [Feuil1$]")
While Not rst.EOF
...
Client = rst.Fields("Client").Value
..
rst.movenext
Wend
Set rst = Nothing
the line of code causing the error is :
Client = rst.Fields("Client").Value
I get an error telling -2147217887 (80040e21)
"you cannot record changes because a value you entered violates the settings defined for this table or list (for example, a value is less than the minimum or greater than the maximum). correct the error and try again"
Sorry the message in the screen capture is in french cuz the workbook that i am working on it is french but here is the translation to english
This made me trying to understand what has specific the line 13 in the column "G"
First of all I can see that this problem appears at the first line filled with data (ATS) in that colum which is the line 13 .. the previous cells in previous lines were empty and the code was working fine till line 13.
Client = rst.Fields("Client").Value
At my own original sheet, there is no kind of error, the error came just after my friend copy/pasted his own values into the sheet, and after looking deeply I noticed that after he copy/pasted the data, the format of the cells in the colum G changed to numbers (double) as seen in this picture
But my variable is variant, so it should accept any kind of data (empty or not, double or not) that's why I can't understand why it's giving this problem ?
I said maybe Excel is not loading into the record set the value ATS, maybe it's loading inside the Rs a kind of special error or something like that, so that the variant variable can't contains it and give this error, I really don't know that why I am asking if you have any clue about this error and how to solve it by code because I simply can solve it manually by changing back to what was the format of my cells before that my friend pasted his data, so if i change the format to Normal instead of decimal in excel cells the problem is solved, but what I want to know is to understand why this error, and how to solve it by CODE inside vba not excel sheet and why a variant variable won't accept a string value (ATS) even if the format in excel is decimal ?
One last thing, i tried to do something like the following but still getting the same error
If Not IsError(rst.Fields("Client").Value) Then Client = rst.Fields("Client").Value Else Client= vbNullString
Also, If I add a spy to the line causing the problem, I find inside the value, the same error message shown in my vba msgbox, here is a printscreen
: "
"you cannot record changes because a value you entered violates the settings defined for this table or list (for example, a value is
thanks in advance to anyone who will have a constructive and usefull answer.

excel listObject issue

I have inherited a spreadsheet full of VBA I know next to nothing about.
When the data is refreshed, the spreadsheet re-formats itself via the VBA. I had to change some tab names and table names and now when the auto reformat kicks in it gives an error as it is looking for the old table name.
Sorry if this s a bad explanation/terminology, I really never get involved with VBA usually!
So the error I get is
Run-time error '450': Wrong number of arguments or invalid property assignment
debug takes me to this line
For Each rptTable In sID.ListObjects
rptTable.TableStyle = teamName <--- this is the highlighted line in debug
oldSize = rptTable.ListRows.Count
If oldSize < teamSize Then
teamName has the new value, rptTable.TableStyle has the old value.
the variables are defined as follow i believe:
Sub FormatSheet(sID As Worksheet)
'Clear debugWindow
Call clearDebugConsole
Application.StatusBar = "Formatting Teamsheet ... "
'Change Active Sheet to the Sheet being Formatted
Worksheets(sID.Name).Activate
Debug.Print "* Entered FormatSheet Sub"
' Set Variables
....
Dim rptTable As ListObject
I basically trying to work out how the ListObject is being populated and where the old name is being picked up from? a global search in the excel spreadsheet doesn't pick it up
Well, it seems that teamname has a value that does not correspond to anything in TableStyles.
stop the code on that line and see what value variable teamname holds. There needs to be value that exists in your table styles. My guess is there is empty string. You need to assign it something like "TableStyleMedium2" etc.. You can find these names in the design ribbon -> table styles. Use the value without spaces.

How do I use RefersToRange?

Can anyone tell me how to use RefersToRange in vba? and what and when is the need of it.
Please provide with simple example first.
Thanks in advance.
In Excel, there is the concept of a named range, that is a range of cells which has a name attached to it. This is represented by the Name object.
The RefersToRange method:
Returns the Range object referred to by a Name object.
For example, let's say I want to read the values only in the print area of the current worksheet. I need the Name object in order to access the print area, but I can't do anything useful with it; if I want to do something useful I have to access the range of cells referred to by that name:
Dim n As Name
Set n = ActiveWorkbook.Names("Print_Area")
Dim rng As Range
Set rng = n.RefersToRange
Dim values() As Variant
values = rng 'I can't read values with a Name object, but I can with a Range object
One thing I have discovered is that sometimes referring to a named range in this fashion:
ThisWorkbook.Range("DownloadPictures").Value = "yes"
results in:
Run-time error '1004':
Method 'Range' of object '_Worksheet' failed
But referring to the named range in this fashion:
ThisWorkbook.Names("DownloadPictures").RefersToRange = "yes"
works fine. My particular circumstance is that the named range ("DownloadPictures") is scoped to the entire workbook, which has several worksheets. I am referring to the name in one of the worksheet's Worksheet_Calculate subroutines. I don't understand why the first method causes an error and the second does not but apparently this is one reason to use the RefersToRange property.
RefersToRange is, one of many ways, to 'point' a range. For example,
ThisWorkbook.Names("DownloadPictures").RefersToRange = "yes"
the above code, points to the range, which can be a cell, named "DownloadPictures" and sets it value to "yes". As a matter of fact, I'd rather use,
ThisWorkbook.range("DownloadPictures").Value = "yes"
The 'Name' in MS Excel is similar to the Variant variable type, but unlike the variables you 'Dim' in the VB code, the 'Name' only be used (or you can consider it is only be used) in the Workbook. The 'Name' and variables are not interfered with each other.
If you open the 'Name Manager', the concept becomes easier to understand. The Name can be created to refer to a cell, a range of cell (a range), some value (so called constant), or a formula.
Each Name object represents a defined name for a range of cells. Names can be either built-in names—such as Database, Print_Area, and Auto_Open—or custom names. ---MSDN
As a Name can refer to range, constant or a formula .RefersToRange specifically refer to a range. And return false if not so.

troubles passing a discontinuous named range into a custom function

I've been looking around stack overflow for an answer to this for longer than I care to admit now.
Here's what I have: In a worksheet I have a bunch of discontinuous cells which I need to check for the existence of specific text. I've created a simple function to do this and can do this easily when I define that range manually (in code).
However, when I procedurally create a named range (while doing other stuff) and then try passing in the named range, the function never executes.
I know that the named range is being properly created because I have auto-formatting on it and also I can reference the range with excel formula which accept discontinuous ranges (SUM and whatnot).
Here's the pertinent portions of my code:
Function customProcess1(NamedRange As Range) As Long
For Each c in NamedRange.Cells
...
Next c
End Function
In Excel when I type the formula as "=customProcess1(A1:A2)" I get my number back after the function runs. When I type in "=customProcess1(NamedRange)" my function never even executes.
Again, I'm using the named range as defined already in the document. I can observe the name in the name manager, it references the appropriate cells, i can use the range in formula which accept non-continuous ranges, etc. I can't figure out how to get my working named range into my function.
When I put the formula as "=customProcess1("NamedRange")" the function executes, but since the named range is not ""NamedRange"" but is "NamedRange" it fails to set the object as Range (the object is not found). I've tried taking the named range as a string, but again, if I don't put the quotes around the name, it won't even run the function. So then I've tried passing in a string with the quotes and taking the quotes off inside the function, but this isn't exactly working well either.
In short, I just want to get my non-continuous named range in my custom function. Once I do that, everything is golden.
Anyone have any ideas? I'm not sure why this has been such a chore.
I'm not sure why what you're trying doesn't work and don't really have time to research that part of it, but you could do the following:
Function customProcess1(NamedRange As String) As Long
Dim TheRange As Range
Set TheRange = Range(NamedRange)
For Each c in TheRange.Cells
...
Next c
End Function
Hope this helps.
Adapting your UDF(), I coded:
Function customProcess1(NamedRange As Range) As Long
For Each c In NamedRange.Cells
customProcess1 = customProcess1 + c.Value
Next c
End Function
I then assigned the name Mike to the cells B6,C8,D10 and placed values in these cells. I then placed the formula:
=customProcess1(Mike)
in a cell and got the following:
NOTE:
I did not use =customProcess("Mike")

How to get/set unique id for cell in Excel via VBA

I want to have/define a unique id for each data row in my Excel data sheet - such that I can use it when passing the data onwards and it stays the same when rows are added/deleted above it.
My thoughts are to use the ID attribute of Range (msdn link)
So, I have a user defined function (UDF) which I place in each row that gets/sets the ID as follows:
Dim gNextUniqueId As Integer
Public Function rbGetId(ticker As String)
On Error GoTo rbGetId_Error
Dim currCell As Range
'tried using Application.Caller direct, but gives same error
Set currCell = Range(Application.Caller.Address)
If currCell.id = "" Then
gNextUniqueId = gNextUniqueId + 1
'this line fails no matter what value I set it to.
currCell.id = Str(gNextUniqueId)
End If
rbGetId = ticker & currCell.id
Exit Function
rbGetId_Error:
rbGetId = "!ERROR:" & Err.Description
End Function
But this fails at the line mentioned with
"Application-defined or object-defined error"
I thought perhaps its one of those limitations of UDFs, but I also get the same error if I try it from code triggered from a ribbon button...
Any other suggestions on how to keep consistent ids - perhaps I should populate the cells via my ribbon button, finding cells without IDs and generating/setting the cell value of those...
EDIT:
As Ant thought, I have the sheet protected, but even in an unlocked cell it still fails. Unprotecting the sheet fixes the problem.... but I have used "Protect UserInterFaceOnly:=True" which should allow me to do this. If I manually allow "Edit Objects" when I protect the sheet it also works, but I don't see a programmatic option for that - and I need to call the Protect function in AutoOpen to enable the UserInterfaceOnly feature...
I guess I need to turn off/on protect around my ID setting - assuming that can be done in a UDF... which it seems it cannot, as that does not work - neither ActiveSheet.unprotect nor ActiveWorkbook.unprotect :(
Thanks in advance.
Chris
Okay...
It does appear that if the sheet is locked, macros do not have write access to low-level information such as ID.
However, I do not think it is possible to unprotect the sheet within a UDF. By design, UDFs are heavily restricted; I think having a cell formula control the sheet protection would break the formula paradigm that a cell formula affects a cell only.
See this page on the Microsoft website for more details.
I think this limits your options. You must either:
give up sheet protection
give up the UDF, use a Worksheet_Change event to capture cell changes and write to ID there
use a UDF that writes the ID into the cell value, rather than save to ID
The UDF approach is fraught with problems as you are trying to use something designed for calculation of a cell to make a permanent mark on the sheet.
Nonetheless, here's an example of a UDF you can use to stamp a "permanent" value onto a cell, which works on unlocked cells of a protected sheet. This one only works for single cells (although it could be adapted for an array formula).
Public Function CellMark()
Dim currCell As Range
Set currCell = Range(Application.Caller.Address)
Dim myId As String
' must be text; using .value will cause the formula to be called again
' and create a circular reference
myId = currCell.Text
If (Trim(myId) = "" Or Trim(myId) = "0") Then
myId = "ID-" & Format(CStr(gNextUniqueId), "00000")
gNextUniqueId = gNextUniqueId + 1
End If
CellMark = myId
End Function
This is quite flawed though. Using copy or the fillbox will, however, retain the previous copied value. Only by explicitly setting cells to be a new formula will it work. But if you enter in the formula into the cell again (just click it, hit ENTER) a new value is calculated - which is standard cell behaviour.
I think the Worksheet_Change event is the way to go, which has much more latitude. Here's a simple example that updates the ID of any cell changes. It could be tailored to your particular scenario. This function would need to be added to every Worksheet the ID setting behaviour is required on.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim currCell As Range
Set currCell = Target.Cells(1, 1)
Dim currId As String
currId = currCell.ID
If Trim(currCell.ID) = "" Then
Target.Parent.Unprotect
currCell.ID = CStr(gNextUniqueId)
Target.Parent.Protect
gNextUniqueId = gNextUniqueId + 1
End If
End Sub
Last note; in all cases, your ID counter will be reset if you re-open the worksheet (at least under the limited details presented in your example).
Hope this helps.
Concur with Ant - your code works fine here on Excel 2003 SP3.
I've also been able to use:
Set currCell = Application.Caller
If Application.Caller.ID = "" Then
gNextUniqueId = gNextUniqueId + 1
'this line fails no matter what value I set it to.
currCell.ID = Str(gNextUniqueId)
End If
Aha! I think I have it.
I think you're calling this from an array formula, and it only gets called ONCE with the full range. You can't obtain an ID for a range - only a single cell. This explains why Application.Caller.ID fails for you, because Range("A1:B9").ID generates an Application-defined or object-defined error.
When you use Range(Application.Caller.Address) to get the "cell" you just defer this error down to the currCell.ID line.
I think we may have a few issues going on here, but I think they are testing issues, not problems with the code itself. First, if you call the function from anything other than a Cell, like the immediate window, other code, etc. Application.Caller will not be set. This is what is generating your object not found errors. Second, if you copy/paste the cell that has the function, they you will by copy/pasting the ID too. So wherever you paste it to, the output will stay the same. But if you just copy the text (instead of the cell), and then paste then this will work fine. (Including your original use of Application.Caller.)
The problem is with Application.Caller.
Since you are calling it from a user defined function it is going to pass you an error description. Here is the remark in the Help file.
Remarks
This property returns information about how Visual Basic was called, as shown in the following table.
Caller - Return value
A custom function entered in a single cell - A Range object specifying that cell
A custom function that is part of an array formula in a range of cells - A Range object specifying that range of cells
An Auto_Open, Auto_Close, Auto_Activate, or Auto_Deactivate macro - The name of the document as text
A macro set by either the OnDoubleClick or OnEntry property - The name of the chart object identifier or cell reference (if applicable) to which the macro applies
The Macro dialog box (Tools menu), or any caller not described above - The #REF! error value
Since you are calling it from a user defined function, what is happening is Application.Caller is returning a String of an error code to your range variable curCell. It is NOT causing an error which your error handler would pick up. What happens after that is you reference curCell, it's not actually a range anymore. On my machine it tries setting curCell = Range("Error 2023"). Whatever that object is, it might not have an ID attribute anymore and when you try to set it, it's throwing you that object error.
Here's what I would try...
Try removing your error handler and see if VBA throws up any exceptions on Range(Application.Caller.Address). This won't fix it, but it could point you in the right direction.
Either through logic or Application.ActiveCell or however you want to do it, reference the cell directly. For example Range("A1") or Cells(1,1). Application.Caller.Address just doesn't seem like a good option to use.
Try using Option Explicit. This might make the line where you set curCell throw up an error since Range(Application.Caller.Address) doesn't look like it's passing a range back, which is curCell's datatype.
I have found that if I protect the sheet with "Protect DrawingObjects:=False", the UDF can set the Id. Strange.
Thanks for all the help with this.