Do Until loop in VBA does not break on Exit Sub - vba

I wrote the following code in order to ask for an input.
validInput = False
Do
str = InputBox("Some text...")
If str = vbNullString Then
MsgBox ("Input canceled")
Exit Sub
Else
If IsNumeric(str) Then
exchange = CCur(str)
validInput = True
Else
MsgBox ("Input invalid.")
End If
End If
Loop Until validInput
However, if I cancel my input it keeps asking me for an input and the loop goes on even though I added the Exit Sub line.
I tried to add validInput = True before Exit Sub but that didn't work either.
What am I missing?
EDIT:
Here is the whole sub.
Public Sub CurrencyCheck()
Dim ws As Worksheet
Set ws = ActiveWorkbook.Worksheets("Datenbank")
Dim lastRow As Long
lastRow = ws.Cells(Rows.Count, 4).End(xlUp).Row
Dim i As Long
Dim j As Long
Dim curSymbol As String
Dim exchange As Currency
Dim str As String
Dim curArr() As String
Dim arrCnt As Integer
arrCnt = 1
Dim curInArr As Boolean
curInArr = False
Dim curIndex As Integer
Dim validInput As Boolean
ReDim curArr(1 To 2, 1 To arrCnt)
For i = 1 To lastRow
If ws.Cells(i, 4).Value <> "Price" And ws.Cells(i, 4).Value <> "" Then
curSymbol = Get_Currency(ws.Cells(i, 4).text) 'Function that returns currency symbol (€) or abbreviation (EUR)
If curSymbol <> "€" Then
For j = LBound(curArr, 2) To UBound(curArr, 2)
If curArr(1, j) = curSymbol Then
curInArr = True
curIndex = j
End If
Next j
If Not curInArr Then
If curSymbol = "EUR" Then
ReDim Preserve curArr(1 To 2, 1 To arrCnt)
curArr(1, arrCnt) = curSymbol
curArr(2, arrCnt) = 1
curIndex = arrCnt
arrCnt = arrCnt + 1
Else
validInput = False
Do Until validInput
str = InputBox("Some text...")
If str = vbNullString Then
MsgBox ("Input canceled.")
Exit Sub
Else
If IsNumeric(str) Then
exchange = CCur(str)
validInput = True
Else
MsgBox ("Input invalid.")
End If
End If
Loop
ReDim Preserve curArr(1 To 2, 1 To arrCnt)
curArr(1, arrCnt) = curSymbol
curArr(2, arrCnt) = exchange
curIndex = arrCnt
arrCnt = arrCnt + 1
End If
End If
ws.Cells(i, 4).Value = StringToCurrency(ws.Cells(i, 4).text)
ws.Cells(i, 4).Value = ws.Cells(i, 4).Value * curArr(2, curIndex)
ws.Cells(i, 4).NumberFormat = "#,##0.00 €"
End If
End If
Next i
End Sub
EDIT2: When I run the input loop as a subroutine by itself it works. The macro is run in another workbook and doing that it fails...
EDIT3: My bad. The problem is not related to the code but to the positioning of the subroutine. It was called of and over again because it was called in a loop. I have to apologize. Thanks to everyone.

This will loop until a numeric is entered:
Sub dural()
Dim validInput As Boolean
Dim strg As String, x As Variant
validInput = False
Do
strg = Application.InputBox(Prompt:="enter value", Type:=2)
If strg = "False" Then
ElseIf IsNumeric(strg) Then
x = CCur(strg)
validInput = True
End If
Loop Until validInput
End Sub
EDIT#1:
This version will quit the loop if the user touches the CANCEL button or the red x button:
Sub dural()
Dim validInput As Boolean
Dim strg As String, x As Variant
validInput = False
Do
strg = Application.InputBox(Prompt:="enter value", Type:=2)
If strg = "False" Or strg = "" Then
validInput = True
ElseIf IsNumeric(strg) Then
x = CCur(strg)
validInput = True
End If
Loop Until validInput
End Sub

I don't think is a null string. Try this
If str = vbNullString or str = "" Then

Related

VBA: Highlighting Specific Text (Case Sensitive to Case Insensitive)

The below VBA code is to highlight text (Case Sensitive) in cells. May I know how I can edit the VBA code below to become Case Insensitive?
Sub HighlightStrings_CaseSensitive_AllowForeignText_NotExactText()
Dim xHStr As String, xStrTmp As String
Dim xHStrLen As Long, xCount As Long, I As Long
Dim xCell As Range
Dim xArr
On Error Resume Next
xHStr = Application.InputBox("What are the words to highlight:", "Word Higlighter")
If TypeName(xHStr) <> "String" Then Exit Sub
Application.ScreenUpdating = False
For Each xCell In Selection
Dim varWord As Variant
For Each varWord In Split(xHStr, Space$(1))
xHStrLen = Len(varWord)
xArr = Split(xCell.Value, varWord)
xCount = UBound(xArr)
If xCount > 0 Then
xStrTmp = ""
For I = 0 To xCount - 1
xStrTmp = xStrTmp & xArr(I)
xCell.Characters(Len(xStrTmp) + 1, xHStrLen).Font.ColorIndex = 39
xCell.Characters(Len(xStrTmp) + 1, xHStrLen).Font.Bold = True
xCell.Characters(Len(xStrTmp) + 1, xHStrLen).Font.Italic = True
xStrTmp = xStrTmp & varWord
Next
End If
Next varWord
Next xCell
Application.ScreenUpdating = True
End Sub

Need to slightly tweak this code...need it to find exact match and I'm out of my league

Public Function FindCodes(keywords As Range, text As String)
'FindCodes = "TEST"
Dim codeRows As Collection
Dim codeString As String
Set codeRows = New Collection
'Find Codes
For Each Item In keywords
Dim keywordArr() As String
Dim i As Integer
i = 0
If Item.Row <> 1 Then 'Ignore first row
keywordArr() = Split(Item, ",")
'On Error Resume Next
On Error GoTo ErrHandler
For Each s In keywordArr()
If InStr(LCase(text), LCase(s)) <> 0 Then
codeRows.Add Item.Row, CStr(Item.Row)
End If
Next s
End If
Next Item
'Build Codes String
If codeRows.Count > 0 Then
Dim codeArr() As String
'Set codeArr = New Collection
'Dim i As Integer
'i = 0
ReDim codeArr(codeRows.Count)
For Each s In codeRows
'codeArr.Add s, CStr(Worksheets("Codes").Range("A" & s).Value)
codeArr(i) = Worksheets("Codes").Range("A" & s).Value
'Set i = Worksheets("Codes").Range("B" + s).Value
i = i + 1
Next s
End If
'FindCodes = Join(codeArr, ",")
If UBound(codeArr) > 1 Then
FindCodes = Join(codeArr, ",")
ElseIf UBound(codeArr) = 1 Then
FindCodes = codeArr(0)
Else
FindCodes = ""
End If
ErrHandler:
If Err.Number = 457 Or Err.Number = 0 Or Err.Number = 20 Then
'foo = someDefaultValue
Resume Next
Else
'Err.Raise Err.Number
FindCodes = CVErr(xlErrValue)
End If
End Function
Sub temp()
Dim r As Range
Set r = Worksheets("Codes").Range("B:B")
MsgBox FindCodes(r, ".")
End Sub
Your code seems over-complex, but maybe I'm misunderstanding what it's supposed to do.
Try this:
Public Function FindCodes(keywords As Range, text As String)
Dim c As Range, keywordArr, s, rv
'only look at used cells
Set keywords = Application.Intersect(keywords, keywords.Worksheet.UsedRange)
For Each c In keywords.Cells
If c.Row > 1 And Len(c.Value) > 0 Then 'Ignore first row and empty cells
keywordArr = Split(c.Value, ",")
For Each s In keywordArr
If LCase(Trim(s)) = LCase(Trim(text)) Then
'grab value from ColA and go to next cell
rv = rv & IIf(Len(rv) = 0, "", ",") & c.EntireRow.Cells(1).Value
Exit For
End If
Next s
End If
Next c
FindCodes = rv
End Function

dictionary.Exists(key) always False

I am trying to build a validation tool that consists of a header check, a dupe check, and a vLookup. In the DuplicateCheck subroutine, I am adding all unique values from a range to a dictionary using .Exists() = False; this check is failing consistantly and I am getting duplicate values added. Similar problems seemed to be fixed using lower() or upper(), but my testing has been with numbers such as "1", "2", "3", or values such as "k1", "k2", "k2".
Here is my code:
Option Explicit
Dim wbThis As ThisWorkbook
Dim wsOld, wsNew, wsValid As Worksheet
Dim lColOld, lColNew, lRowOld, lRowNew, iRow, iCol As Long
Dim cellTarget, cellKey As Variant
Dim cellValid, dataOld, dataNew As Range
Sub Execute()
Set wbThis = ThisWorkbook
Set wsOld = wbThis.Worksheets(1)
Set wsNew = wbThis.Worksheets(2)
Set wsValid = wbThis.Worksheets(3)
lColOld = wsOld.Cells(1, Columns.Count).End(xlToLeft).Column
lColNew = wsNew.Cells(1, Columns.Count).End(xlToLeft).Column
lRowOld = wsOld.Cells(Rows.Count, 1).End(xlUp).Row
lRowNew = wsNew.Cells(Rows.Count, 1).End(xlUp).Row
Set dataOld = wsOld.Range("A1").Resize(lRowOld, lColOld)
Set dataNew = wsNew.Range("A1").Resize(lRowNew, lColNew)
Call Validation.HeaderCheck
Call Validation.DuplicateCheck
Call Validation.vLookup
End Sub
Sub HeaderCheck()
Application.StatusBar = "Checking headers..."
Dim i As Long
With wsNew
For i = 1 To lColNew
If (wsNew.Cells(1, i) <> wsOld.Cells(1, i)) Then
MsgBox ("Column " & i & " on New Data is not the same as Old Data. This tool will not work with differences in headers. Please reorder your fields and run the tool again.")
Application.StatusBar = False
End
End If
Next i
End With
With wsOld
For i = 1 To lColOld
If (wsOld.Cells(1, i) <> wsNew.Cells(1, i)) Then
MsgBox ("Column " & i & " on Old Data is not the same as New Data. This tool will not work with differences in headers. Please reorder your fields and run the tool again.")
Application.StatusBar = False
End
End If
Next i
End With
Application.StatusBar = False
End Sub
Sub DuplicateCheck()
Dim iterator As Long
Dim dicKeys As New Scripting.Dictionary
Dim dicDupes As New Scripting.Dictionary
Dim key As Variant
Dim progPercent As Double
Dim keys As Range
Dim wsDupes As Worksheet
Set keys = wsNew.Range("A2").Resize(lRowNew, 1)
Application.ScreenUpdating = False
iterator = 1
For Each key In keys
If dicKeys.Exists(key) = False Then
dicKeys.Add key, iterator 'HERE IS THE BUG----------------------
Else
dicDupes.Add key, iterator
End If
progPercent = iterator / keys.Count
Application.StatusBar = "Identifying duplicates: " & Format(progPercent, "0%")
iterator = iterator + 1
Next key
If (dicDupes.Count <> 0) Then
Set wsDupes = ThisWorkbook.Worksheets.Add(, wsValid, 1)
wsDupes.Name = "Duplicates"
iterator = 1
For Each key In dicDupes
If (dicDupes(key) <> "") Then
wsDupes.Cells(iterator, 1).Value = dicDupes(key)
End If
progPercent = iterator / dicDupes.Count
Application.StatusBar = "Marking duplicates: " & Format(progPercent, "0%")
iterator = iterator + 1
Next key
End If
Set dicKeys = Nothing
Set dicDupes = Nothing
Application.ScreenUpdating = True
End Sub
Sub vLookup()
Application.ScreenUpdating = False
Dim progPercent As Double
For iRow = 2 To lRowNew
Set cellKey = wsNew.Cells(iRow, 1)
For iCol = 1 To lColNew
Set cellTarget = wsNew.Cells(iRow, iCol)
Set cellValid = wsValid.Cells(iRow, iCol)
On Error GoTo errhandler
If (IsError(Application.vLookup(cellKey.Value, dataOld, iCol, False)) = False) Then
If (cellTarget = Application.vLookup(cellKey.Value, dataOld, iCol, False)) Then
cellValid.Value = cellTarget
Else
cellValid.Value = "ERROR"
End If
Else
If (cellValid.Column = 1) Then
If (cellValid.Column = 1) Then
cellValid.Value = cellKey
cellValid.Interior.ColorIndex = 46
End If
Else
cellValid.Value = "ERROR"
End If
End If
Next iCol
progPercent = (iRow - 1) / (lRowNew - 1)
Application.StatusBar = "Progress: " & iRow - 1 & " of " & lRowNew - 1 & ": " & Format(progPercent, "0%")
Next iRow
Application.StatusBar = False
Application.ScreenUpdating = True
Exit Sub
errhandler:
MsgBox (Err.Description)
End Sub
The problem is probably here:
Dim key As Variant
Dim progPercent As Double
Dim keys As Range
Then when you make the check here:
For Each key In keys
If dicKeys.Exists(key) = False Then
dicKeys.Add key, iterator 'HERE IS THE BUG----------------------
Else
dicDupes.Add key, iterator
End If
Next
It compares the key as Range and not as value.
Try something like this:
If dicKeys.Exists(key.Value2) = False Then
dicKeys.Add key.Value2, iterator
Or find another way not to work with the object, but with its value.

extract column range from formula in excel using macro

Sub AddNameNewSheet1()
Dim wsToCopy As Worksheet, wsNew As Worksheet
Dim Newname As String
Newname = InputBox("Number for new worksheet?")
Set wsToCopy = ThisWorkbook.Sheets("Sheet1")
Set wsNew = ThisWorkbook.Sheets.Add
If Newname <> "" Then
wsNew.Name = Newname
End If
wsToCopy.Cells.Copy wsNew.Cells
Dim cell As Range
Dim bIsNumeric As Boolean
Dim testFormula As String
bIsNumeric = False
For Each cell In wsNew.Range("A1:M40")
If cell.HasFormula() = True Then
If bIsNumeric Then
If testFormula = CStr(cell.Formula) Then
cell.Value = "<"
Else
testFormula = cell.Formula
cell.Value = "F"
End If
Else
testFormula = cell.Formula
cell.Value = "F"
End If
bIsNumeric = True
ElseIf IsNumeric(cell) = True Then
bIsNumeric = False
If Len(cell) > 0 Then
cell.Value = "N"
End If
Else
bIsNumeric = False
cell.Value = "L"
End If
Next cell
End Sub
I want to extract column and row that applied in formula. For example,
if formula is =SUM(A10:F10) then I want both A10 and F10 then I remove that is there any way to find out that.
My actual purpose is finding formula without column and row value.
thanks in advance.
If you want to get A10 and F10 from the formula, you can use this, passing your range to strRange:
Sub Extract_Ranges_From_Formula()
Dim strRange As String
Dim rCell As Range
Dim cellValue As String
Dim openingParen As Integer
Dim closingParen As Integer
Dim colonParam As Integer
Dim FirstValue As String
Dim SecondValue As String
strRange = "C2:C3"
For Each rCell In Range(strRange)
cellValue = rCell.Formula
openingParen = InStr(cellValue, "(")
colonParam = InStr(cellValue, ":")
closingParen = InStr(cellValue, ")")
FirstValue = Mid(cellValue, openingParen + 1, colonParam - openingParen - 1)
SecondValue = Mid(cellValue, colonParam + 1, closingParen - colonParam - 1)
Debug.Print FirstValue
Debug.Print SecondValue
Next rCell
End Sub
It does a Debug.Print of the two returned values.

Find if a given value is in a cell, if so then try next value until unique

I have the below sub that checks on a separate worksheet if the created number in textbox8 already exists, at the moment there is a message box that alerts the user that the part number already exists, they have to click OK, then the number is incremented by 1, the process is repeated until a unique number is found. This is the written to the worksheet along with some other data.
What I need to do is remove the message box so it will automatically search and find the next available number.
I added the following code to the sub, but this has no effect:
Application.DisplayAlerts = False
MsgBox "This already exists"
SendKeys "{ENTER}"
Application.DisplayAlerts = True
code
'Create part number and check
Private Sub CommandButton2_Click()
With TextBox26
If myreset = True Then
.Tag = 0
myreset = False
End If
.Tag = Val(.Tag) + 1
.Text = "-" & VBA.Format(Val(.Tag), "0000")
End With
Dim iNum(1 To 8) As String
iNum(1) = TextBox24.Value
iNum(2) = TextBox25.Value
iNum(3) = TextBox26.Value
TextBox8.Value = iNum(1) + iNum(2) + iNum(3)
'check article exists
Dim emptyRow As Long
Dim rcnt As Long
Dim i As Long
ActiveWorkbook.Sheets("existing").Activate
rcnt = Sheets("existing").Range("A" & Rows.Count).End(xlUp).Row
For i = 2 To rcnt
If TextBox8.Text = Sheets("existing").Range("A" & i).Value Then
Application.DisplayAlerts = False
MsgBox "This already exists"
SendKeys "{ENTER}"
Application.DisplayAlerts = True
Exit Sub
End If
Next
Range("A1").Select
Do
If IsEmpty(ActiveCell) = False Then
ActiveCell.Offset(1, 0).Select
End If
Loop Until IsEmpty(ActiveCell) = True
ActiveCell.Value = TextBox8.Text
To remove the message Box all you need to do is delete the following lines in your code
Application.DisplayAlerts = False
MsgBox "This already exists"
SendKeys "{ENTER}"
Application.DisplayAlerts = True
I am not sure what the first part of the code is doing. if you could provide some example I can help with that. But I have rationalized the second part and this will now achieve what the original code was attempting to achieve with lesser lines.
'check article exists
Dim emptyRow As Long
Dim rcnt As Long
Dim i As Long
Dim varProdCode As Long
ActiveWorkbook.Sheets("existing").Activate
varProdCode = TextBox8.Text
rcnt = Sheets("existing").Range("A" & Rows.Count).End(xlUp).Row
Do Until varProdCode = 0
For i = 2 To rcnt
If varProdCode = Sheets("existing").Range("A" & i).Value Then
varProdCode = varProdCode + 1
Exit For
Else
Range("A" & Rows.Count).End(xlUp).Offset(1, 0).Value = varProdCode
varProdCode = 0
Exit Sub
End If
Next
Loop
This is the code that works
Private Sub CommandButton2_Click()
With TextBox26
If myreset = True Then
.Tag = 0
myreset = False
End If
.Tag = Val(.Tag) + 1
.Value = VBA.Format(Val(.Tag), "0000")
End With
Dim emptyRow As Long
Dim rcnt As Long
Dim c As Long
rcnt = Sheets("existing").Range("A" & Rows.Count).End(xlUp).Row
For c = 2 To rcnt
Dim iNum(1 To 8) As String
iNum(1) = TextBox24.Value
iNum(2) = TextBox25.Value
iNum(3) = TextBox26.Value
'check if article exists
ActiveWorkbook.Sheets("existing").Activate
If Sheets("existing").Range("A" & c).Value = iNum(1) & iNum(2) & "-" & iNum(3) Then
TextBox26.Value = TextBox26.Value + 1
iNum(3) = TextBox26.Value
End If
Next c
'create article number
TextBox8.Value = iNum(1) + iNum(2) + "-" + iNum(3)
'select first column
Range("A1").Select