Here is the best way to move/shift references in a formula, and it uses the function ConvertFormula()
!
Function MoveFormulaReferences$(strFormula$, rngRelativeTo As Range, lngOffsetRows&, lngOffsetColumns)
MoveFormulaReferences = Application.ConvertFormula( _
Application.ConvertFormula( _
strFormula, _
XlReferenceStyle.xlA1, _
XlReferenceStyle.xlR1C1, _
, _
rngRelativeTo), _
XlReferenceStyle.xlR1C1, _
XlReferenceStyle.xlA1, _
, _
rngRelativeTo.Offset(lngOffsetRows, lngOffsetColumns))
End Function
Now some examples:
?MoveFormulaReferences("=C3*2+C3", Range("$D$4"), -2, -2)
returns =A1*2+A1
?MoveFormulaReferences("=$C3*2+C3", Range("$D$4"), -2, -2)
returns =$C1*2+A1
It is also possible to "force-move" all references, even the ones made absolute by a leading $
, using the ToAbsolute
parameter of the ConvertFormula()
function: pass XlReferenceType.xlRelative
as argument in the nested ConvertFormula()
call
Edit:
One could argue the use of the rngRelativeTo
parameter: it appears it can be confounding... At first I thought that the above behaves exactly as if a cell containing the formula was moved, but @GSerg, in its own way, made me realize that is not entirely correct, here an example:

If cell C1
is copied and pasted in B1
, the A1
reference in the formula being moved becomes #REF!
. I expected my function with ConvertFormula()
to have the same behavior than Excel's copy/paste but in fact, it "rotates the references from start to end" instead of "blocking" them when out of boundaries:
?MoveFormulaReferences("=A1*2", Range("C1"), 0, -2)
returns =XFC1*2
So one might think the rngRelativeTo
parameter is useless in that case since it does not prevent the "blocking", and that internally it could be replaced by Range("A1")
as a proxy, but it is incorrect: considering the same example: Range("A1").Offset(0, -2)
cannot be resolved. In the end, a user-provided relative range is required to simulate a real scenario.
Range("A1")
would work if both offsets are >= 0
Range("XFD1048576")
would work if both offsets are <= 0
No single Range
would work in any feasible case where one offset is > 0
and the other < 0
, so rngRelativeTo
is required to simulate a real copy/paste situation.
Edit 2:
Here is a function whose usage might be a little more intuitive for the op particular situation (still not using a copy/paste operation):
Function MoveCellFormulaReferences$(rngCell, lngOffsetRows&, lngOffsetColumns&)
MoveCellFormulaReferences$ = Application.ConvertFormula( _
Application.ConvertFormula( _
rngCell.Formula, _
XlReferenceStyle.xlA1, _
XlReferenceStyle.xlR1C1, _
, _
rngCell), _
XlReferenceStyle.xlR1C1, _
XlReferenceStyle.xlA1, _
, _
rngCell.Offset(lngOffsetRows, lngOffsetColumns))
End Function
EDIT 3:
and even, reduced to its simplest form:
Function MoveCellFormulaReferences2$(rngCell, lngOffsetRows&, lngOffsetColumns&)
MoveCellFormulaReferences2$ = Application.ConvertFormula( _
rngCell.FormulaR1C1, _
XlReferenceStyle.xlR1C1, _
XlReferenceStyle.xlA1, _
, _
rngCell.Offset(lngOffsetRows, lngOffsetColumns))
End Function
But the first version works with any String
A1
formula, it does not suppose you have a cell holding the R1C1
translation or that you want to use a temp cell to translate it...
I don't know how to quickly prevent the "rotation" though, other than analyzing the formula string, but that defeats the purpose of my answer...