I have a Form, and I wnat to rename the sheet. Actually, program does it correctly, but when I want to use .Activate function I get an error : Type mismatch
Worksheets.Add().name = UserForm1.txtNameSur
Worksheets(UserForm1.txtNameSur).Activate
I've also tried
Worksheets("&UserForm1.txtNameSur&").Activate
Still the same.
Thanks!
GD Stefan,
The .Activate method of Worksheets is expecting either an index (integer or long) or a string, identifying the page to activate. You are passing an object in the form of an TextBox to the .Activate method. Try passing the value, rather than the whole object, as VBA gets confused as to what to do with that TextBox where it is expecting only a String or an Integer or Long variable.
i.e.:
Worksheets.Add().Name = UserForm1.txtNameSur.Value
Worksheets(UserForm1.txtNameSur.Value).Activate
That should work !
Related
I'm starting with VBA and I'm very upset to discover I have fallen at the first hurdle in this book I am following.
I should type the following
Function Hello() As String
Hello = "Greetings"
End Function
This all works fine but next I am supposed to change the text and see the function change on the Excel spreadsheet. Unfortunately I can't get this to work. Does anyone know why?
I've saved the document as an Excel Macro-enabled workbook and tried opening and closing.
You seem to have this function in the code-file for thisWorkbook, but it should be in a module. Add a module to your project, place your code there and make the function Public. See also: How to Call VBA Function from Excel Cells?
Normally a Function should be in a Module, not in ThisWorkbook. You store Event handlers in the ThisWorkbook or a Sheet module.
It doesn't recalculate because it doesn't have a Range input, since the function doesn’t have any arguments, it is treated constant output and hence updating the function doesn’t update the cell value.
But if you modify it to accept a Range input, and if there are any changes to the input range, it will recalculate.
declare your function right
Function Hello(str As String) As String
then use something like
cells(1,1).value=str
function does the job for you but first you must call the function by parameter like this
cells(1,1).value=Hello("How are you")
the result will be that in cell 1,1 will "How are you" be written.
but from this on I am not sure what are you trying to accomplish. If you need funct. to write in specific cell all the time you should use something like
Funtion Hello(row as integer, column as integer) as string
cells(row,column).value=inputbox("give me the input")
end function
then to use this you write into code
result=Hello(1,2)
this example works
Function Area(row As Integer, column As Integer) As String
Cells(row, column).Value = InputBox("give something here")
End Function
Sub my()
result = Area(2, 2)
End Sub
run my()
Ok I worked it out, I had written the function into the file "This Workbook" instead of into the Module I created...
I knew it would be something simple! All working now.
Function Hello() As String
Application.Volatile
Hello = "Greetings"
End Function
I have a problem that I don't understand at all:
i = 150
If ActiveWorkbook.Worksheets("foobar").Cells(i, 3).Value Like "*:*" Then
MsgBox "I found a colon!"
End If
As you might guess, the sheet foobar has at position (150, 3) a cell containing a colon, thus the message box is shown.
Now I want to do this:
i = 150
Cell = ActiveWorkbook.Worksheets("foobar").Cells(i, 3).Value 'fails right here
If Cell Like "*:*" Then
MsgBox "I found a colon!"
End If
Here it gives me an error saying "Object variable or With block variable not set. In fact saying:
Sheet = ActiveWorkbook.Worksheets("foobar")
gives a similar message. Why? What am I doing wrong? I just want a reference of that object, or at least a refence.
Bottomline:
When handling objects, use Set.
When handling primitive data (integers, longs, strings*, variants*) you do not use it.
Sheet, Workbook, Range, are objects. Therefore, you need to use Set when assigning them to variables.
A Range.Value returns a Variant (that can be a long, a string, etc.) So, you cannot use Set.
==========================
Now, about your error message... I'd say then that maybe before in your code, Cell is being declared as object. Try use another variable name, or check the Cell variable type.
To check this, right click on it and then click in 'Definition'. Have it declared as Variant might fix the problem (be aware of side effects it might cause, though).
==========================
*I know these types aren't 'primitive'; I used as an example here for the sake of explanation's cleanliness.
As your assigning an object the line
Sheet = ActiveWorkbook.Worksheets("foobar")
should read
Set Sheet = ActiveWorkbook.Worksheets("foobar")
In your line
Cell = ActiveWorkbook.Worksheets("foobar").Cells(i, 3).Value
you are trying to assign the value of cell (probably a string) to a cell. Take the .Value off and the assignment should work better. The Excel message you encountered is not the best: sometimes you get it when you assign variables of the wrong type.
However, the If Cell Like might not work. (Hint: the .Value has to get moved, not deleted.)
I just tried the following code and it works probably in your code make sure the ActiveWorkBook is actually the Active Work book you want(if you are working with multiple work books)
Sub test()
i = 3
If ActiveWorkbook.Worksheets("Sheet1").Cells(i, 2).Value Like ":" Then
MsgBox "I found a colon!"
Else
MsgBox "didn't find a colon!"
End If
Cell = ActiveWorkbook.Worksheets("Sheet1").Cells(i, 2).Value
'fails right here
If Cell Like ":" Then
MsgBox "I found a colon!"
End If
End Sub
Use Dim MyCell as Variant (or as string) since Cell is an existing object
Eventually, I want to move the cell to the location where the last error occured. Edit: Forgot to say that I'm using Excel 2003.
As requested in comments...
Look up the 'Caller' property of the 'Application' object in the Excel VBA help. When you use it from a VBA routine, it will tell you where the call to the routine came from - what Range, Chart, etc.
An important thing to be aware of when using 'Application.Caller' is that it isn't always a Range object. Look at the help, but the property returns a Variant value that can be a Range, String, or Error. (It is a Range object in the case you're interested in, but you'll need to be aware of this.)
Because of the above, and the vagaries of VBA syntax when it comes to objects vs. values, it can be tricky to use 'Application.Caller'. Putting a line like:
Debug.Print Application.Caller.Address
in your code will fail when the caller isn't a Range. Doing something like:
Dim v
v = Application.Caller
will "compile", but will create circular references when the caller is a Range because you're trying to access the value of the calling Range.
This all means that it's probably best to write a little utility function for yourself:
Public Function currentCaller() As String
If TypeOf Application.Caller Is Range Then
Dim rng As Range
Set rng = Application.Caller
currentCaller = rng.Address(External:=True)
Else
currentCaller = CStr(Application.Caller)
End If
End Function
and then call it from your error handlers where you want to know where the call came from.
One more thing - obviously this can only tell you the caller once a VBA routine has actually been called. If you have errors in your calling formulas, Excel will return error values to your cells without ever calling your VBA routines.
Wrap your VBA function in another function that stores the cell location and value as variants. Keep this 'wrapper' function as basic as possible so it won't cause any additional errors.
If you're trying to debug app-crashing errors, the wrapper function could even store those values in a comma-delimited text file. Once stored, Excel can crash all it wants and you'll still know what the cell location and value were since you stored them outside of Excel beforehand.
Could this be done with an error handler?
An example of what I mean below:
sub code1()
on error goto cell A1
end sub
I've created a program that creates and populates a custom document property in an Excel 2007 workbook file. However I haven't been able to show the value of this property in a worksheet cell. In Word 2007 you can just select "Insert -> Quick Parts -> Field..." and use the DocProperty field to show the value of the custom field in a document. However I haven't found a similar function in Excel 2007.
Does anybody know how to display the value of a custom document property in an Excel worksheet cell? I would prefer a solution similar to the Word 2007 solution mentioned above. I rather not use a macro/custom code for this.
Unfortunately I believe you need to use an user defined function. Add a new VBA module to your workbook and add this function:
Function DocumentProperty(Property As String)
Application.Volatile
On Error GoTo NoDocumentPropertyDefined
DocumentProperty = ActiveWorkbook.BuiltinDocumentProperties(Property)
Exit Function
NoDocumentPropertyDefined:
DocumentProperty = CVErr(xlErrValue)
End Function
The call to Application.Volatile forces the cell to be updated on each recalculation ensuring that it will pick up changes in the document properties.
The equivalent in Excel would be via formula and I don't think it's possible to extract a document property without code. There are no native functions to pick out document properties. (An alternative could be to store information in workbook/worksheet Names, which ARE accessible via formula)
In VBA you'd have to create a function something like:
Public Function CustomProperty(ByVal prop As String)
CustomProperty = ActiveWorkbook.CustomDocumentProperties(prop)
End Function
and then call it in a formula with =CustomProperties("PropertyName").
There is another subtle point. Formula dependencies only relate to other cells; this formula depends on a custom property. If you update the custom property a pre-existing formula involving CustomProperty will not be updated automatically. The cell will have to be re-evaluated manually or the entire workbook forced through a recalc. Your best chance would be to make the function volatile, which means the formula would be recalc'd on every cell change -- but this still means you only get an update if a cell has been changed.
Select the cell you want to extract
Rename the cell to some useful. From "B1" to "Project_Number".
Open "Advance Properties" click the "Custom" tab. Enter a name for the new property. click "Link to content" the select the cell name from the "Value" pull down list.
I wish i could take cerdit but I found the answer online:
http://pdmadmin.com/2012/03/displaying-custom-property-values-in-excel-using-a-named-range/
You can link a named range to a custom property, but then the custom property reflects the value of the [first cell in the] range. It's effectively read-only; you can change the content of the cell to update the property, but not the other way around.
I know you want to avoid it, but if you want to use the property value in a formula, you'll have to create a custom worksheet function to do so.
I have experienced the same issues other people have. So I will try to comprehensively cover how I addressed it.
First of all, you have no other option than writing a function meant to get whatever you put in a custom or built-in property and make the "problem" cell to point at it this way:
=yourPropertyGettingFunctionName(PropertyName)
PropertyName being a string referring to the name of the custom/built-in property whose value you want to be shown in the cell.
The function could be written (as formerly suggested) as:
Public Function StdProp(ByVal sPropName As String) As String
Application.Volatile
StdProp = ActiveWorkbook.BuiltinDocumentProperties(sPropName).Value
End Function
for a built-in property, or as:
Public Function UsrProp(ByVal sPropName As String) As String
Application.Volatile
On Error GoTo UndefinedProp
UsrProp = ActiveWorkbook.CustomDocumentProperties(sPropName)
GoTo Exit
UndefinedProp:
UsrProp = "n/a"
Exit:
End Function
As already mentioned, including Application.Volatile will allow for a semi-automatic cell contents update.
However, this poses a problem on its own: whenever you open your Excel file, all the cells using such a relationship will get updated and, by the time you exit the file, Excel will ask you for your permission to update it, no matter if you did introduce any change on it or not, because Excel itself did.
In my development group, we use SubVersion as a version control system. In case you inadvertently hit "update" on exit, SVN will notice it and next time you want to commit your changes, the excel file will be included in the pack.
So I decided to use everything at hand to do whatever I needed and avoid, at the same time, this self-update effect I didn't want.
That means using named ranges in combination with property accessing function/s.
Given the fact I can't expect old files to have provision for my new needs, I wrote this function:
Private Function RangeAssign(sRange As String, sValue As String) As Integer
Dim rDest As Range
If RangeCheck(sRange) Then
Set rDest = Range(sRange)
Else
Set rDest = Application.InputBox(sMsg + vbCrLf + vbCrLf + _
"Please, select a cell to get" + vbCrLf + _
"the name " + sRange + " assigned", sCopyRight, Type:=8)
rDest.Name = sRange
End If
rDest.Cells(1, 1).NumberFormat = "#"
rDest.Cells(1, 1).Value = sValue
RangeAssign = True
End Function
It allows for a proper selection of the destination cell. When assigning values to a property (let's say "Author", which happens to be a built-in one), I also update the value stored in the named range, and can write in a cell:
=Author
if I happen to have defined a range named "Author" and filled its "A1" cell with the value for built-in property "Author", which I need to have updated for our own external tracking purposes.
This all didn't happen overnight. I hope it can be of some help.
I used this for extracting the SharePoint properties (based on Martin's answer):
Public Function DocumentProperty(Property As String)
Application.Volatile
On Error GoTo NoDocumentPropertyDefined
DocumentProperty = ActiveWorkbook.ContentTypeProperties(Property).Value
Exit Function
NoDocumentPropertyDefined:
DocumentProperty = CVErr(xlErrValue)
End Function
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.