Related
I am trying to create a VBA macro to fill in cells that are empty in a range ("INV_Nums") without overwriting the cell if it contains data already. In order to accomplish this I am trying to use an if statement to check if the cell in question is blank...if it is not, then I want the loop to continue on to the next cell, however if it IS blank then I want to input the index(__,(match()) function into the cell.
I keep getting a "compile error: mismatch" on the True statement but I'm at a loss as to why my synatax would be wrong. Any help would be appreciated.
Here is my code:
Dim i As Integer
Dim Rng As Range
Dim ARwkb As Excel.Workbook
Dim ARwks As Excel.Worksheet
Dim Samwkb As Excel.Workbook
Dim Samwks As Excel.Worksheet
Set Samwkb = Excel.Workbooks("Samples - one sheet")
Set Samwks = Samwkb.Worksheets("samples shipment")
Set ARwkb = Excel.Workbooks("AR balance.xlsx")
Set ARwks = ARwkb.Worksheets("Total Trading")
Set Rng = Samwkb.Range("INV_Nums")
For i = 6 To Rng.Rows.Count + 6
If Range("AAi") = "" Is True Then
Range("AAi").Select
ActiveCell.FormulaR1C1 = _
"=INDEX('AR balance.xlsx'!AR_Invoice_Nums,MATCH(RC[-21],'AR
balance.xlsx'!AR_PL_Nums,0))"
End If
Next i
The problem is in how you are identifying the range and administering the criteria.
For i = 6 To Rng.Rows.Count + 6
If IsEmpty(Range("AA" & i)) Then
Range("AA" & i).FormulaR1C1 = _
"=INDEX('AR balance.xlsx'!AR_Invoice_Nums, MATCH(RC[-21],'AR balance.xlsx'!AR_PL_Nums, 0))"
End If
Next i
The .SpecialCells method can quickly determine the blank cells and an xlR1C1 formula can be used to insert all of the formulas at once..
...
with Samwkb.Range("INV_Nums")
.specialcells(xlcelltypeblanks).FormulaR1C1 = _
"=INDEX('AR balance.xlsx'!AR_Invoice_Nums, MATCH(RC[-21],'AR balance.xlsx'!AR_PL_Nums, 0))"
end with
...
I have done some searching and cannot figure out the syntax for what I would like to accomplish. I would like to have a specific, static cell on a Summary sheet automatically update to a dynamic cell on a different worksheet. I started with the following code:
Sheets("Summary").Activate
Range("B1").Select
ActiveCell.FormulaR1C1 = "='Current Billing'!R[46]C"
Range("B2").Select
ActiveCell.FormulaR1C1 = _
"='Current Billing'!R[45]C[3]+'Current Billing'!R[45]C[4]"
After getting some advice on a different topic for this same workbook, I now know .Select is a no-no and should be avoided. I also know that my last row on the "Current Billing" worksheet will change when I copy the code to another workbook for billing that project. This has led to me to modify my code to make it more fool-proof and versatile.
I have figured out how to get the the cell value to insert from my "Current Billing" sheet to my "Summary" sheet. But if something changes on the "Current Billing" sheet, the "Summary" sheet will not automatically update. This is the code I have that sort of works:
Dim ws5 As Worksheet
'ws5 is "Current Billing"
Dim ws6 As Worksheet
'ws6 is "Summary"
Dim LRowB2 As Long
Dim LRowB3 As String
LRowB2 = ws5.Cells(Rows.Count, "B").End(xlUp).Row
LRowB3 = ws5.Cells(LRowB2, "B")
ws6.Cells(1, "B").Formula = LRowB3
I tried these two code sequences, but kept getting errors:
Dim LRowB4 As String
Dim LRowE2 As Long
Dim LRowF2 As Long
LRowB4 = "= & ws5 & ! & LRowB3"
ws6.Cells(2, "B").Formula = "=sum(& LRowE2 &,& LRowF2 &)"
In short, is there a way to mimic the function of the first code procedure, but have the stability and fool-proofness of the second procedure? And, is there a way to integrate a =Sum() formula into the second procedure so I can add two cells from "Current Billing" on the "Summary" page? Thanks for the help!
The syntax of the formula is similar to =SUM('Current Billing'!C9, 'Current Billing'!D9)
This will check for errors, and is a bit more dynamic
Option Explicit
Public Sub UpdateSummary()
Dim wsCB As Worksheet, lrCB As Long, cbCellC As Range, cbCellD As Range
Dim wsSummary As Worksheet, sumCell As Range
Set wsCB = ThisWorkbook.Worksheets("Current Billing")
Set wsSummary = ThisWorkbook.Worksheets("Summary")
lrCB = wsCB.Cells(wsCB.Rows.Count, "C").End(xlUp).Row
'Verify that we have the same last row for col C and D on sheet "Current Billing"
If wsCB.Cells(wsCB.Rows.Count, "D").End(xlUp).Row = lrCB Then
Set cbCellC = wsCB.Cells(lrCB, "C") '"Current Billing".Cell(lr, C)
Set cbCellD = wsCB.Cells(lrCB, "D") '"Current Billing".Cell(lr, D)
Set sumCell = wsSummary.Cells(2, "B") '"Summary".Cell(2, B)
sumCell = "Invalid 'Current Billing' values" 'Default (in case of errors)
'Check "Current Billing" cells for errors (#N/A, #VALUE!, #REF!, #DIV/0!, etc)
If Not IsError(cbCellC) And Not IsError(cbCellD) Then
'Verify that "Current Billing" cells are numbers
If IsNumeric(cbCellC) And IsNumeric(cbCellD) Then
Dim cbC As String, cbD As String
cbC = "'" & wsCB.Name & "'!" & cbCellC.Address(0, 0) 'Current Billing'!C9
cbD = "'" & wsCB.Name & "'!" & cbCellD.Address(0, 0) 'Current Billing'!D9
'Final format: =SUM('Current Billing'!C9, 'Current Billing'!D9)
sumCell.Formula = "=SUM(" & cbC & ", " & cbD & ")" 'Update Summary cell
End If
End If
End If
End Sub
If the formula will contain a division, verify that the divisor is not 0
But if you are using VBA for this, you can simplify the syntax: sumCell = cbCellC + cbCellD
Note:
String variables become more complex when we have to use quotes inside quotes:
Str with ""double quotes"" can be built like this
str = "Str with """"double quotes""""", or
str = "Str with " & Chr(34) & """double quotes""" & Chr(34), or
str = "Str with " & Chr(34) & Chr(34) & "double quotes" & Chr(34) & Chr(34)
This string str = "Str with 'double quotes'" is Str with 'double quotes'
Use the Workbook_SheetChange event. It's an "event" action that is triggered when changes are made anywhere in your workbook. Within the event sub add code to update the value of your hard-coded/static cell with the value in your formula.
The Workbook_SheetChange sub must be in your workbook module (the default module name is "ThisWorkbook").
Here's an example of how you could use it. Just update the sheet names and range addresses for the variables myStaticCell and myFormulaCell.
In your ThisWorkbook Module:
Option Explicit
Public myVar As Variant ' stores last known value of static cell
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
' Define the locations of your formula cell and static cell.
Dim myStaticCell As Range
Dim myFormulaCell As Range
Set myStaticCell = Worksheets("Sheet2").Range("A2")
Set myFormulaCell = Worksheets("Sheet1").Range("A2")
' Ignore changes made directly to the static cell by comparing
' the sheet name and address where the workbook change
' occurred (variables Sh and Target which are built into the
' Workbook_SheetChange event) with the sheet name and address
' of your static cell.
If Sh.Name = myStaticCell.Parent.Name And _
Target.Address = myStaticCell.Address Then Exit Sub
' Save the value to a public variable so it can
' be checked the next time something changes.
myVar = myFormulaCell.Value
' If different, update the static cell with the
' value from your formula.
If myStaticCell <> myVar Then myStaticCell = myFormulaCell.Value
End Sub
How would I go about getting the relative position of a cell within a range? Finding the position of a cell in a worksheet is trivial, using the Row- and Column-properties, but I am unsure of how to do the same within a range.
I considered using the position of the top-left cell in the range I want to find the position of a cell in, and just deduct it (-1) from the position of the cell in the worksheet, but it gets a little bit cumbersome. Is there a more elegant way to go about this?
My best attempt, including a test, so far is this:
Option Explicit
Sub test()
Dim r As Range: Set r = Sheet1.Range("B2:E10")
Dim c As Range: Set c = Sheet1.Range("C2")
Debug.Print "Column in sheet: " & c.Column
Debug.Print "Row in sheet: " & c.Row
Debug.Print "Column in range: " & column_in_range(r, c)
Debug.Print "Row in range: " & row_in_range(r, c)
End Sub
Function column_in_range(r As Range, c As Range) As Long
column_in_range = c.Column - (r.Cells(1, 1).Column - 1)
End Function
Function row_in_range(r As Range, c As Range) As Long
row_in_range = c.Row - (r.Cells(1, 1).Row - 1)
End Function
This gives the desired output:
Column in sheet: 3
Row in sheet: 2
Column in range: 2
Row in range: 1
But I wonder if there are any native functions I can use instead?
updated using variant provided by lori_m
But I wonder if there are any native functions ...
use this
Sub test()
Dim r As Range, c As Range
With Sheet1
Set r = .[B2:E10]
Set c = .[C2]
End With
If Not Intersect(r, c) Is Nothing Then
Debug.Print "Column in sheet: " & c.Column
Debug.Print "Row in sheet: " & c.Row
Debug.Print "Column in range: " & Range(r(1), c).Columns.Count
Debug.Print "Row in range: " & Range(r(1), c).Rows.Count
End If
End Sub
output
Column in sheet: 3
Row in sheet: 2
Column in range: 2
Row in range: 1
There is no native way to do it. I also do what you have mentioned in the code above. However I put some extra checks.
Sub test1()
Dim r As Range: Set r = Sheet1.Range("B2:E10")
Dim c As Range: Set c = Sheet2.Range("C2") '<~~ Changed Sheet1 to sheet2
Dim rng As Range
On Error Resume Next
Set rng = Intersect(c, r)
On Error GoTo 0
'~~> Check if the range is in main range
If Not rng Is Nothing Then
'
'~~> Rest of your code
'
Else
MsgBox c.Address & " in " & c.Parent.Name & _
" is not a part of " & _
r.Address & " in " & r.Parent.Name
End If
End Sub
In my opinion there is almost native way to check it but result is a string required some additional manipulation. All you need to use is a proper construction of .Address property (according to MSDN). Some examples:
Dim r As Range: Set r = Sheet1.Range("B2:E10")
Dim c As Range: Set c = Sheet1.Range("c2")
Debug.Print c.Address(False, False, xlR1C1, , r.Cells(0, 0))
'>>result: R[1]C[2]
'-----------------------------------------------------
Set c = Sheet1.Range("e2")
Debug.Print c.Address(False, False, xlR1C1, , r.Cells(0, 0))
'>>result: R[1]C[4]
'-----------------------------------------------------
Set c = Sheet1.Range("e5")
Debug.Print c.Address(False, False, xlR1C1, , r.Cells(0, 0))
'>>result: R[4]C[4]
'-----------------------------------------------------
Take a look on MSDN to see more.
You can use something like :
MsgBox ActiveCell.Address(RowAbsolute:=True, _
ColumnAbsolute:=True, _
ReferenceStyle:=xlR1C1, _
External:=False, _
RelativeTo:=Range("B2"))
'Or shorter version :
MsgBox ActiveCell.Address(, , xlR1C1, False, Range("B2"))
But you'll have both information about row and column in the range, but not separately.
So you'll still need to extract these values from the answer (look like : R18C20) in two functions, so almost the same issue...
I'm not totally sure if this is what you are after.
But here it goes:
Sub ts2()
Dim test As Range
Set test = Range("B2:E10")
Dim topcorner As Range
Dim testcell As Range
Set topcorner = Cells(test.Row, test.Column)
Set testcell = Range("D7")
rel_row = testcell.Row - topcorner.Row
rel_col = testcell.Column - topcorner.Column
End Sub
By this, you will find the relative position.
But maybe you were looking for some built in function ?
If this was not the thing you were after, please edit your post...
I am trying to write a macro that modifies a string from
"evice & Services Help and Troubleshooting: Device Help and Troubleshooting: Device Unlock Code Request 205 613-2860"
to
"Device & Services Help and Troubleshooting Device Help and Troubleshooting Device Unlock Code Request"
This corrects the spelling mistake in the first word, removes the colons and phone number at the end.
I experimented with worksheet functions and the following functions work:
=SUBSTITUTE([#Description], ":", " ")
=SUBSTITUTE([#Description], "evice", "Device")
=LEFT([#Description], LEN([#[R-val]])-13)
I am having trouble with Error type 13 "Type Mismatch".
I am also assuming that because the original string is in a table that I will not have to do a loop.
My VBA code is
Sub rmvstuff()
Dim needsrd As Worksheet
Dim device As String
Dim rmvcln As String
Dim rmvtpnum As String
Dim desc As Range
Dim mycell As String
mycell = ActiveCell.Value
With needsrd
Set desc = Range("description")
End With
desc.Select
device = WorksheetFunction.Substitute([#Description], "evice", "Device")
rmvcln = WorksheetFunction.Substitute([#Description], ":", " ")
rmvtpnum = WorksheetFunction.Left([#Description], Len([#Description]) - 13)
End Sub
I really appreciate any help that I can get on this. Thank you.
You have a few errors such as needsrd is never declared so it's equal to Nothing. Also, #Description is not a valid identifier in VBA.
Here is another way to do what your trying:
Sub rmvstuff()
Dim needsrd As Worksheet
Dim desc As Range
Set needsrd = Sheets(1)
Set desc = needsrd.Range("description")
desc.Value = Replace(desc.Value, "evice", "Device")
desc.Value = Replace(cell.Value, "DD", "D")
desc.Value = Replace(desc.Value, ":", "")
desc.Value = Left(desc.Value, Len(desc.Value) - 13)
End Sub
Here is how to loop thorugh each cell assuming "description" is a named range of more than one cell:
Sub loopRMVstuff()
Dim needsrd As Worksheet
Dim desc As Range
Set needsrd = Sheets(1)
Set desc = needsrd.Range("description")
For Each cell In desc
cell.Value = Replace(cell.Value, "evice", "Device")
cell.Value = Replace(cell.Value, "DD", "D")
cell.Value = Replace(cell.Value, ":", "")
cell.Value = Left(cell.Value, Len(cell.Value) - 13)
Next cell
End Sub
Tested using named range for cells A1:A3
Before
After
If I have a Range object--for example, let's say it refers to cell A1 on a worksheet called Book1. So I know that calling Address() will get me a simple local reference: $A$1. I know it can also be called as Address(External:=True) to get a reference including the workbook name and worksheet name: [Book1]Sheet1!$A$1.
What I want is to get an address including the sheet name, but not the book name. I really don't want to call Address(External:=True) and try to strip out the workbook name myself with string functions. Is there any call I can make on the range to get Sheet1!$A$1?
Only way I can think of is to concatenate the worksheet name with the cell reference, as follows:
Dim cell As Range
Dim cellAddress As String
Set cell = ThisWorkbook.Worksheets(1).Cells(1, 1)
cellAddress = cell.Parent.Name & "!" & cell.Address(External:=False)
EDIT:
Modify last line to :
cellAddress = "'" & cell.Parent.Name & "'!" & cell.Address(External:=False)
if you want it to work even if there are spaces or other funny characters in the sheet name.
Split(cell.address(External:=True), "]")(1)
Ben is right. I also can't think of any way to do this. I'd suggest either the method Ben recommends, or the following to strip the Workbook name off.
Dim cell As Range
Dim address As String
Set cell = Worksheets(1).Cells.Range("A1")
address = cell.address(External:=True)
address = Right(address, Len(address) - InStr(1, address, "]"))
The Address() worksheet function does exactly that. As it's not available through Application.WorksheetFunction, I came up with a solution using the Evaluate() method.
This solution let Excel deals with spaces and other funny characters in the sheet name, which is a nice advantage over the previous answers.
Example:
Evaluate("ADDRESS(" & rng.Row & "," & rng.Column & ",1,1,""" & _
rng.Worksheet.Name & """)")
returns exactly "Sheet1!$A$1", with a Range object named rng referring the A1 cell in the Sheet1 worksheet.
This solution returns only the address of the first cell of a range, not the address of the whole range ("Sheet1!$A$1" vs "Sheet1!$A$1:$B$2"). So I use it in a custom function:
Public Function AddressEx(rng As Range) As String
Dim strTmp As String
strTmp = Evaluate("ADDRESS(" & rng.Row & "," & _
rng.Column & ",1,1,""" & rng.Worksheet.Name & """)")
If (rng.Count > 1) Then
strTmp = strTmp & ":" & rng.Cells(rng.Count) _
.Address(RowAbsolute:=True, ColumnAbsolute:=True)
End If
AddressEx = strTmp
End Function
The full documentation of the Address() worksheet function is available on the Office website: https://support.office.com/en-us/article/ADDRESS-function-D0C26C0D-3991-446B-8DE4-AB46431D4F89
I found the following worked for me in a user defined function I created. I concatenated the cell range reference and worksheet name as a string and then used in an Evaluate statement (I was using Evaluate on Sumproduct).
For example:
Function SumRange(RangeName as range)
Dim strCellRef, strSheetName, strRngName As String
strCellRef = RangeName.Address
strSheetName = RangeName.Worksheet.Name & "!"
strRngName = strSheetName & strCellRef
Then refer to strRngName in the rest of your code.
You may need to write code that handles a range with multiple areas, which this does:
Public Function GetAddressWithSheetname(Range As Range, Optional blnBuildAddressForNamedRangeValue As Boolean = False) As String
Const Seperator As String = ","
Dim WorksheetName As String
Dim TheAddress As String
Dim Areas As Areas
Dim Area As Range
WorksheetName = "'" & Range.Worksheet.Name & "'"
For Each Area In Range.Areas
' ='Sheet 1'!$H$8:$H$15,'Sheet 1'!$C$12:$J$12
TheAddress = TheAddress & WorksheetName & "!" & Area.Address(External:=False) & Seperator
Next Area
GetAddressWithSheetname = Left(TheAddress, Len(TheAddress) - Len(Seperator))
If blnBuildAddressForNamedRangeValue Then
GetAddressWithSheetname = "=" & GetAddressWithSheetname
End If
End Function
rngYourRange.Address(,,,TRUE)
Shows External Address, Full Address
The best way I found to do this is to use the following code:
Dim SelectedCell As String
'This message Box allows you to select any cell on any sheet and it will return it in the format of =worksheetname!$A$X" where X is any number.
SelectedCell = Application.InputBox("Select a Cell on ANY sheet in your workbook", "Bookmark", Type:=8).Address(External:=True)
SelectedCell = "=" & "'" & Right(SelectedCell, Len(SelectedCell) - Len("[" & ActiveWorkbook.Name & "]") - 1)
'Be sure to modify Sheet1.Cells(1,1) with the Sheet and cell you want to use as the destination. I'd recommend using the Sheets VBA name.
Sheet1.Cells(1, 1).Value = SelectedCell
How it works;
By Clicking on the desired cell when the message box appears. The string from "Address(External:=True)" (i.e ['[Code Sheet.xlsb]Settings'!$A$1) is then modified to remove the full name of the worksheet([Code Sheet.xlsb]).
Using the previous example it does this by taking the "Len" of the full length of;
[Code Sheet.xlsb]Settings'!$A$1 and subtracts it with the Len of ([Code Sheet.xlsb] -1). leaving you with Settings'!$A$1.
SelectedCell = "=" & "'" & Right(SelectedCell, Len(SelectedCell) - Len("[" & ActiveWorkbook.Name & "]") - 1)
The Code then its and "='" to insure that it will be seen as a Formula (='Settings'!$A$1).
Im not sure if it is only on Excel on IOS but for some reason you will get an Error Code if you add the "='" in any other way than "=" & "'" as seen bellow.
SelectedCell = "=" & "'" & Right....
From here all you need is to make the program in the Sheet and cell you want your new formula in.
Sheet1.Cells(1, 1).Value = SelectedCell
By Opening a new Workbook the full Code above will work as is.
This Code is Especially useful as changing the name of the workbook or the name of the sheet that you are selecting from in the message box will not result in bugs later on.
Thanks Everyone in the Forum before today I was not aware that External=True was a thing, it will make my coding a lot easier. Hope this can also help someone some day.
Why not just return the worksheet name with
address = cell.Worksheet.Name
then you can concatenate the address back on like this
address = cell.Worksheet.Name & "!" & cell.Address
Dim rg As Range
Set rg = Range("A1:E10")
Dim i As Integer
For i = 1 To rg.Rows.Count
For j = 1 To rg.Columns.Count
rg.Cells(i, j).Value = rg.Cells(i, j).Address(False, False)
Next
Next
For confused old me a range
.Address(False, False, , True)
seems to give in format TheSheet!B4:K9
If it does not why the criteria .. avoid Str functons
will probably only take less a millisecond and use 153 already used electrons
about 0.3 Microsec
RaAdd=mid(RaAdd,instr(raadd,"]") +1)
or
'about 1.7 microsec
RaAdd= split(radd,"]")(1)
[edit on 2009-04-21]
As Micah pointed out, this only works when you have named that
particular range (hence .Name anyone?) Yeah, oops!
[/edit]
A little late to the party, I know, but in case anyone else catches this in a google search (as I just did), you could also try the following:
Dim cell as Range
Dim address as String
Set cell = Sheet1.Range("A1")
address = cell.Name
This should return the full address, something like "=Sheet1!$A$1".
Assuming you don't want the equal sign, you can strip it off with a Replace function:
address = Replace(address, "=", "")