How to reassign the address of a named range in Open Office VBA? - vba

In Excel VBA, I redefine the address of a named range with:
Ranges("MyRange").Cells(1).CurrentRegion.Name = "MyRange"
in the Worksheet_Deactivate event.
This way, after updating the spreadsheet, MyDataRange always references to the entire data.
Ok with Excel, but when I tried to migrate this to Open Office VBA (OpenOffice.org 3.1.1 /
OOO310m19 Build:9420) the ".Name" property of the Range isn't defined.
Then, I tried with:
ThisWorkbook.Names("MyDataRange").RefersTo = Range("MyDataRange").Cells(1).CurrentRegion
with no luck ("the property is Read Only" error)
Again, I tried with:
ThisWorkbook.Names.Add ("MyDataRange", Range("MyDataRange").Cells(1).CurrentRegion)
This works, but after that all the cells with validations that references to the named range loses the original reference and shows "#NAME#" in the popup.
Is there any way to reassign the address of a named range without losing the references to it?
TIA,
Pablo

I wonder if this technique would work?

Related

Error 91 when using Cells.Find with window hidden

Please read this fully and understand that this program was working fine until I changed the way I was hiding the workbook.
I have a program that worked great while I was using Application.Visible = False and only showing the user form. I came to realize that this would hide all Excel windows and not just the one I was using. This is going to be distributed throughout the department and hiding all Excel windows was unacceptable.
I started using ActiveWindow.Visible = False, but I am now getting Error 91 anytime I search a worksheet for a value (Cells.Find).
Modifying the worksheet is not an option and the value for which I'm searching can move around the sheet depending on what has been added or removed.
Cells.Find worked out great for this reason. I need to either find another way to search the page, or find another way to hide the worksheet. Please help
When the window is not visible, the Cells reference is not qualified to a worksheet object (unless qualified, Cells refers to ActiveSheet.Cells and there is no ActiveSheet), so you can do like:
Sheets("sheetname").Cells.Find ' modifying "sheetname" as needed
This may also fail (with the same error), or it could also yield incorrect results if there are other open workbooks, so it's best to qualify to a workbook fully, e.g.:
Workbooks("workbookname").Sheets("sheetname").Cells.Find(...
It is still a good idea to test the result of Find before performing additional method/property calls against an object which could be Nothing, as per this answer:
Find command giving error: "Run-time Error '91': Object variable or With block variable not set"

Copy Method of Worksheet class fails with .xlam file

CONTEXT: I have created an Excel Add-In in which there is a worksheet named "Mother". When the user clicks a button from any workbook in which the Add-In is installed, I would like the worksheet "Mother" to be copied inside its same workbook and being renamed. In code:
ThisWorkbook.Sheets("Mother").Copy After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count)
WHAT I EXPECT: being the line of code above placed into the .xlam code, I know that ThisWorkbook refers to the Add-In and this is observable through a watcher as well:
So, I expect that a copy of "Mother" will be added to the workbook, at the end.
WHAT HAPPENS: the method simply fails, returning the error Copy Method of Worksheet class failed.
WHAT I HAVE TRIED: By simply looking for the name of the error online I have figured out that, apparently, I cannot copy a hidden worksheet and that's why the method fails. So I have tried:
1) To change the visibility with ThisWorkbook.Sheets("Mother").Visible = True... No success;
2) To activate the workbook before/after to change the visibility, so ThisWorkbook.Activate... No success.
I'm not being able to find much documentation online about this, so I'm almost wondering if it's actually possible to edit/show the workbook.xlam, something that I would really need to do for my project.
A BIT MORE OF CONTEXT: The reason why I need to both add the worksheet and show it to the user is that the Add-In, which basically consists in a custom function =myFunction(), needs to be fed by user-inputs for make its calculus.
In particular, neither I can write the data in the sheet before to distribute the Add-In (because I don't know the user inputs in advance), nor I can prepare a user-form where to insert the data and show it at running-time (because the user-inputs are usually a big amount of data that can be copied-pasted easily into an Excel spreadsheet, but not inserted manually into a form nor in the function parameters themselves). So:
MY QUESTION: Is it possible to copy (duplicate) a worksheet within a .xlma workbook and activate it after to allow the user data insertion? If yes, where am I being wrong on the above?
Looks like this link would help: http://forums.techguy.org/business-applications/1120165-add-extra-sheet-xlam-file.html.
The operation cannot be performed on add-in workbook, hence you just need to set it as "not add in":
ThisWorkbook.IsAddIn = False
'your copy operation
This Workbook.IsAddIn = True

Shapes not tied to worksheets?

ok so,
I was hoping to have some stuff in my excel app persist, as in hang around, and I want something a little more reliable than global variables as these are reset when code is edited or if say the app is halted. (which does often happen)
So I've been using shapes, and they work good, but they rely on at least one worksheet always being constant right? as shapes are tied to sheets, if the sheet with the shapes gets deleted the shapes go away. And the users often delete / add new sheets, theres no one sheet that is always a constant, and they wouldnt let me force that on them either.
so is there a way to make shapes tied to workbooks instead of sheets? so then if a sheet is deleted with shapes on it, the shapes wont disapear.
any help or other suggestions appeciated
Edit
Thanks again to: #David Zemens for the answer that got me through, just in case anyone ever looks at this down the road, the code to add a named range is: workbook.Names.Add Name:="Name", RefersTo:="value" - you NEED to add refersto or it will error. you can put in a temp value like "temp" and set the value later, but you have to have refers to when you add
You can use Named Ranges to save string data between sessions. They can be children of the Workbook or of specific Worksheets. You will want the former. From the Formula Ribbon, Names Manager, Define Name like so:
Then, in your VBA, you can retrieve and set this range's value like:
Public Const CSVFileName as String = "sFileName"
Sub YourSubroutine()
Dim nm As Name
Set nm = ActiveWorkbook.Names(CSVFileName)
'Get the value:
MsgBox Replace(Replace(nm.Value, "=", vbNullString), """", vbNullString)
'Set the value:
nm.Value = "C:\documents\filename.CSV"
End Sub
The value associated with the Named Range persists beyond runtime, it is basically a property of the workbook.
I will post this now for you to review. I will try to work up an example of the XML Customer Data, and will revise my answer with that, later.

Easy way to find cell reference in excel vba

I am working on a spreadsheet with lots of VBA code in it. Most cell references use numbers, e.g.
Worksheets("Sheet1").Cells(someRow, someColumn)
I am wondering if there is any easy way to find out exactly which cell is being referenced - is it possible to put a breakpoint on this line of code, and then see which cell it will refer to (using the traditional "BA10" style) or will I have to calculate it each and every time using divs and mods?
Not all the references are hard coded, and I would really like to be able to work out where data is being pulled from, as the cell is accessed (not necessarily changed).
(edit) Is it possible to do this without changing the original source line, e.g. in an event module or something?
Debug.Print Worksheets(1).Cells(10, 53).Address(False, False)
returns BA10
There is another option. If you are making changes to a sheet, you can catch the Change event on the Worksheet, and pump out the changed range like so:
Private Sub Worksheet_Change(ByVal Target As Range)
Debug.Print "CHANGED -> " & Target.Address(False, False)
End Sub
Each change to the sheet will be output to your Immediate window like thus:
CHANGED -> G10
CHANGED -> G11:G28
There is also the SelectionChange event as well, but that's unlikely to be too useful. There is no event for just "reading" cells.
You can use Address.
Debug.print Worksheets("Sheet1").Cells(10, 53).Address
Will print the ranges address for you in the Immediate Window.
Is that what you are looking for?
You can also change your reference style to R1C1 in:
tools\options\general\R1C1 reference style
this way you will be able to know what the code is refereeing to.
Another way to go would be to add a watch in the VBE (Visual Basic Editor). You can do this by going to View>Watch Window to make sure the watch window is visible. Then from Debug menu click Add Watch. If you do this in break mode the context will already be set for you so all you have to do is paste in the expression you want watched and it will be visible. This of course works best on range objects (ex: rngFoo.Address as the expression) but you can paste in things like Worksheets(1).Cells(10, 53).Address.
Use the following to know which cell is calling the function:
Application.Caller.Address
Example:
Function Addition(va As Double, vb As Double) As Double
Addition = va + vb
MsgBox "Cell calling function Addition is " & Application.Caller.Address
End Function
Whenever this function is calculated a message box indicates the cell address calling it.
Be aware that calling this function from another VBA function will send an error. Check http://www.cpearson.com/Excel/WritingFunctionsInVBA.aspx

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.