Combine Multiple MsgBox to one - excel

I am trying to combine multiple msgbox but i couldnot figure out.
Here is my Code:
If InStr(ThisWorkbook.Worksheets("DailyReport").Range("F8").Value, "DCT") > 0 Then
If IsEmpty(Sheet2.Range("G34").Value) Then
MsgBox ("The Following Test is not Performed " & Sheet2.Range("E34").Value)
End If
If IsEmpty(Sheet2.Range("G35").Value) Then
MsgBox ("The Following Test is not Performed " & Sheet2.Range("E35").Value)
End If
If IsEmpty(Sheet2.Range("G36").Value) Then
MsgBox ("The Following Test is not Performed " & Sheet2.Range("E36").Value)
End If
End If
I want to search for word DCT in Cell F8 of Worksheets DailyReport and if it exist then I want to look at multiple cell like G34,G35,G36.... and if these cell are empty then display msgbox saying "The following Test is Not performed: E34,E35,E36...."
Let's Say if G34 and G35 is Empty then the msg box should display
The following Test is not Performed:
Cell value in E34
Cell Value in E35
Msgbox Should have continue and Cancel button
If User hit Continue Then Continue the sub
If user Hit Cancel then Exit the sub

Return Combined Messages in a Message Box
Sub CombineMessages()
Dim CheckCells() As Variant: CheckCells = Array("G34", "G35", "G36")
Dim ValueCells() As Variant: ValueCells = Array("E34", "E35", "E36")
Dim CheckString As String
CheckString = CStr(ThisWorkbook.Worksheets("DailyReport").Range("F8").Value)
Dim UntestedCount As Long, MsgString As String
If InStr(CheckString, "DCT") > 0 Then
Dim n As Long
For n = LBound(CheckCells) To UBound(CheckCells)
If IsEmpty(Sheet2.Range(CheckCells(n))) Then
MsgString = MsgString & vbLf & " " _
& CStr(Sheet2.Range(ValueCells(n)).Value)
UntestedCount = UntestedCount + 1
End If
Next n
End If
If UntestedCount > 0 Then
MsgString = "The following test" _
& IIf(UntestedCount = 1, " is", "s are") & " not performed:" _
& vbLf & MsgString & vbLf & vbLf & "Do you want to continue?"
Dim Msg As Long: Msg = MsgBox(MsgString, vbQuestion + vbYesNo)
If Msg = vbNo Then Exit Sub
End If
MsgBox "Continuing...", vbInformation
End Sub

I want to look at multiple cell like G34,G35,G36....
if these cell are empty then display msgbox saying "The following Test is Not performed: E34,E35,E36...."
G34,G35,G36.... Looks like this range is dynamic? Or will it always be these 3? And if it is dynamic then how are you deciding the range. For example why G34 and not G1? Or till where do you want to check? Till last cell in G? All this will decide how you write a concise vba code. I am going to assume that you want to check till last cell in column G. In case it is say from G34 to say G60(just an example), then change the For Next Loop from For i = 34 To lRow to For i = 34 To 60
Is this what you are trying? (UNTESTED)
Option Explicit
Sub Sample()
Dim i As Long
Dim lRow As Long
Dim CellAddress As String
If InStr(ThisWorkbook.Worksheets("DailyReport").Range("F8").Value, "DCT") > 0 Then
With Sheet2
'~~> Find last row in Col G
lRow = .Range("G" & .Rows.Count).End(xlUp).Row
'~~> Check the range for blank cells
For i = 34 To lRow
If Len(Trim(.Range("G" & i).Value2)) = 0 Then
CellAddress = CellAddress & "," & "E" & i
End If
Next i
End With
'~~> Check if any addresses were found
If CellAddress <> "" Then
CellAddress = Mid(CellAddress, 2)
Dim ret As Integer
'~~> Ask user. There is no CONTINUE button. Use YES/NO
ret = MsgBox("The following Test is Not performed:" & _
vbNewLine & CellAddress & vbNewLine & _
"Would you like to continue?", vbYesNo)
If ret = vbYes Then
'~~> Do what you want
Else
'~~> You may not need the else/exit sub part
'~~> Depending on what you want to do
Exit Sub
End If
'
'
'~~> Rest of the code
'
'
End If
End If
End Sub

Related

Identify duplicate values with MsgBox

I have written VBA code to find the duplicate value and bulk upload the data to another sheet.
If any duplicate in A, B, C Columns I need a message box, and to cancel the bulk upload.
Example of my columns - marked in red are duplicate values:
Option Explicit
Private Sub CommandButton1_Click()
Dim rng As Range
Dim l As Long, r As Long, msg As String
Dim lRow, lRow1 As Long
Application.ScreenUpdating = False
l = Range("A" & Rows.Count).End(xlUp).Row
For r = 2 To l
If Evaluate("COUNTIFS(A:A,A" & r & ",B:B,B" & r & ",C:C,C" & r & ")") > 1 Then msg = msg & vbCr & r
Next
MsgBox msg, vbInformation, "DUPLICATE ROWS"
Exit Sub
lRow = [Sheet2].Cells(Rows.Count, 1).End(xlUp).Row
lRow1 = [Sheet3].Cells(Rows.Count, 1).End(xlUp).Row + 1
[Sheet2].Range("A4:N" & lRow).Copy
[Sheet3].Range("A" & lRow1).PasteSpecial xlPasteValues
Application.CutCopyMode = False
Sheet3.Select
[Sheet3].Range("A1").Select
Sheet2.Select
[Sheet2].Range("A1").Select
End Sub
Something like this should work fine:
For r = 2 To l
If Evaluate("COUNTIFS(A:A,A" & r & ",B:B,B" & r & ",C:C,C" & r & ")") > 1 Then
msg = msg & vbCr & r
End If
Next r
If Len(msg) > 0 Then
MsgBox msg, vbInformation, "DUPLICATE ROWS"
Exit Sub
End If
Extended Formula evaluation without loops
Extending on Tim's row-wise formula evaluation a couple of tips:
Fully qualify your range references; without explicit indications VBA assumes the active sheet, which needn't be the one you have in mind.
Execute a worksheet-related evaluation for the same reason; doing so it suffices here to indicate e.g. "A:A" instead of inserting a sheet prefix "Sheet1!..." each time.
Example procedure
Option Explicit ' force declaration of variables on top of code module
Sub IdentifyDuplicateRows()
With Sheet1 ' using the project's Sheet Code(Name)
'1. get last row & build formula
Dim l As Long
l = .Range("A" & Rows.Count).End(xlUp).Row
Dim myFormula As String
myFormula = "=IF(COUNTIFS(A:A,A2:A" & l & ",B:B,B2:B" & l & ",C:C,C2:C" & l & ")>1,""Duplicate Row "" & Row(A2:A" & l & "),"""")"
'2. get results & write to target
Dim results As Variant
results = .Evaluate(myFormula) ' note the "."-prefix!
With .Range("D2").Resize(UBound(results))
.Value = results 'write results to target
End With
'3. optional additional MsgBox info (see below)
' ...
End With
End Sub
Note to optional message box info
If you prefer a further info via message box you could insert the following block before End With:
'3. optional display in message box
'filter only elements containing "Dup" (change to flat & eventually 0-based array)
results = Application.Transpose(results)
results = Filter(results, "Dup") ' omitted default argument Include:=True
'count duplicate rows and display message
Dim cnt As Long
cnt = UBound(results) + 1
MsgBox Join(results, vbNewLine), vbInformation, cnt & " Duplicate Rows"

code for extracting and loading value to the userform

I'm trying to write a code that will extract value from the database where it is stored, load the value in the user form for editing purposes. The values from the database are stored as a " multi-line " cell entry. and here's the code im stuck on.
Private Sub cmd_Continue_Click()
Dim TargetRow As Integer
TargetRow = Application.WorksheetFunction.Match(ColumnC_Menu, Sheets("Data").Range("Dyn_Business_Name_Website"), 0)
MsgBox (TargetRow)
End Sub
Here's the Code from the main Userform:
Private Sub cmd_Submit_Click()
'When we click Submit button'
Dim TargetRow As Integer 'variable for position control
Dim BusinessName As String
TargetRow = Sheets("Engine").Range("B3").Value + 1 'make variable equal to COUNTA formula on worksheet +1
BusinessName = Txt_BusinessName & vbNewLine & Txt_Website
If Application.WorksheetFunction.CountIfs(Sheets("Data").Range("Dyn_Business_Name_Website"), BusinessName) > 0 Then
MsgBox ("Name Already Exists"), 0, "Check!"
Exit Sub
End If
'Begin Input into the data'
Sheets("Data").Range("Data_Start").Offset(TargetRow, 0).Value = Txt_Rank
Sheets("Data").Range("Data_Start").Offset(TargetRow, 1).Value = Txt_BusinessName & vbNewLine & Txt_Website
Sheets("Data").Range("Data_Start").Offset(TargetRow, 2).Value = Txt_Address & vbNewLine & Txt_Phone

First iteration jumping four rows instead of the expected one row

Why is my first iteration in Sub throughCols that is intended to move one row down each time jumping four rows?
Option Explicit
Dim txt As String
Dim i As Long
Dim strTest As String
Dim strArray() As String
Dim lCaseOn As Boolean
Dim firstRow As Long, startIt As Long
Dim thisCell As Range
Dim lastRow As Long
Dim resetAddress As Range
Sub throughCols()
' Dim thisCell As Range
' get start and end of column data
' NB sheet name is hard coded twice
Call dataRange
startIt = firstRow + 1
For i = 1 To 8 Step 1
' after testing use startIt To lastRow Step 1
' by using activeCell I dont have to pass range through to the sub
Sheets("test").Range("B" & i).Select
MsgBox "this is itteration " & i & " which will output to " & ActiveCell.Offset(0, 2).Address
Call arrayManip
Call cleanTxt(txt)
Next i
End Sub
Sub arrayManip()
' clear out all data
Erase strArray
txt = ""
'set default case
lCaseOn = False
' string into an array using a " " separator
strTest = WorksheetFunction.Proper(ActiveCell.Value)
strTest = Replace(strTest, "-", " - ")
strTest = Replace(strTest, "‘", " ‘ ")
strArray = Split(strTest, " ")
' itterate through array looking to make text formats
For i = LBound(strArray) To UBound(strArray)
If strArray(i) = "-" Then
lCaseOn = True
GoTo NextIteration
End If
If strArray(i) = "‘" Then
lCaseOn = True
GoTo NextIteration
End If
If lCaseOn Then
strArray(i) = LCase(strArray(i))
lCaseOn = False
NextIteration:
End If
Next
End Sub
Function cleanTxt(txt)
' loop through the array to build up a text string
For i = LBound(strArray) To UBound(strArray)
txt = txt & strArray(i) & " "
Next i
' remove the space
txt = Trim(Replace(txt, " - ", "-"))
txt = Trim(Replace(txt, " ‘ ", "‘"))
' MsgBox "active cell is " & activeCell.Address
ActiveCell.Offset(0, 2).Select: ActiveCell.Value = txt
' MsgBox "final output would be " & txt & " to " & activeCell.Address
' this is a thumb suck to attempt to reset the active cell to the itteration address that started it
ActiveCell.Offset(0, -2).Select
MsgBox "next itteration should start with active cell set as " & ActiveCell.Address
End Function
Sub dataRange()
With Sheets("test").Columns("B")
If WorksheetFunction.CountA(.Cells) = 0 Then '<--| if no data whatever
MsgBox "Sorry: no data"
Else
With .SpecialCells(xlCellTypeConstants) '<--| reference its cells with constant (i.e, not derived from formulas) values)
firstRow = .Areas(1).Row
lastRow = .Areas(.Areas.Count).Cells(.Areas(.Areas.Count).Rows.Count).Row
End With
' MsgBox "the first row is " & firstRow
' MsgBox "last row is " & lastRow
End If
End With
End Sub
You are declaring your i variable at module scope, which makes it accessible everywhere within the module; it's modified when you call arrayManip and the value changes.
If you declare a local ind variable inside this routine it won't happen, because the variable will only be accessible to the scope it's declared in. Try the code below:
Sub throughCols()
' Dim thisCell As Range
Dim ind As Long '<-- DECLARE local variable
' get start and end of column data
' NB sheet name is hard coded twice
Call dataRange
startIt = firstRow + 1
' ===== loop on ind and not i (changes when you call arrayManip) ====
For ind = 1 To 8 ' Step 1 <-- actually not needed, that's the default increment value
' after testing use startIt To lastRow Step 1
' by using activeCell I dont have to pass range through to the sub
Sheets("test").Range("B" & ind).Select
MsgBox "this is itteration " & ind & " which will output to " & ActiveCell.Offset(0, 2).Address
Call arrayManip
Call cleanTxt(txt)
Next ind
End Sub

my code is not working: Error 424 and error 9

I have an excel file that has 3 worksheets called, statistics, geography and economics and there are student names/ ids there. I wrote a userform with a textbox and 3 option buttons (statistics, geography and economics) and two command buttons called search and cancel. When you write a name on the textbox and choose one of the option buttons, it will search for the name on the chosen worksheet (which was given in the user form as option buttons). if the name is found then I added a label that will notify its cell address, and if it is not found it will say that the name wasnt found. and when I click cancel it will give me as a message box all the names that weren't found while searching (I used an array to do so). And this is the code I wrote:
Dim s(1 To 20) As String, count As Integer
Private Sub CommandButton1_Click()
Dim wsheet As String
If OptStat = True Then wsheet = OptStat.Caption 'OptStat.Caption = Statistics - it's the name of the worksheet called Statistics
If OptGeo = True Then wsheet = OptGeo.Caption 'OptGeo.Caption = Geography - it's the name of the worksheet called Geography
If OptEco = True Then wsheet = OptEco.Caption 'OptEco.caption = Economics - it's the name of the worksheet called Economics
Worksheets(wsheet).Select
Set r = Cells.Find(TextBox1.Text, Range("a1"), xlFormulas, xlPart, xlByRows, xlNext, False, , False)
If r Is Nothing Then
count = count + 1
s(count) = TextBox1.Text & "in " & wsheet
Label2.Caption = TextBox1.Text & " is not found in " & wsheet
TextBox1.Text = ""
Worksheets(1).Select
Exit Sub
Else
Address = r.Address
If TextBox1.Text = r.Value Then
r.Activate
Label2.Caption = TextBox1.Text & " found in worksheet " & wsheet & " on cell " & Address
TextBox1.Text = ""
Exit Sub
Else
r = Cells.FindNext(r)
Do While r.Address <> a
If TextBox1.Text = r.Value Then
r.Activate
Label2.Caption = TextBox1.Text & " found in worksheet " & wsheet & " on cell " & Address
TextBox1.Text = ""
Exit Sub
Else
r = Cells.FindNext(r)
End If
Loop
If r.Address = a Then
count = count + 1
s(count) = TextBox1.Text & "in " & wsheet
Label2.Caption = TextBox1.Text & " not founf in " & wsheet
TextBox1.Text = ""
Exit Sub
End If
End If
End If
End Sub
Private Sub CommandButton2_Click()
Unload Me
Dim names As String
names = "The Following names are missing" & vbNewLine
For i = 1 To count
names = names & s(i) & vbNewLine
Next i
count = 0
Worksheets(1).Select
MsgBox (names)
End Sub
I have two problems with the code, maybe someone can tell me what the problem is. The first problem, I might have, for example the name Tomphson on cell a1 and the name Tom at a2 and I am looking for the name Tom, so searching for tom will give me the cell tomphson first. So I used while loop to take care of that. However it gives me the error 424 object required.
The second problem is with the array. I am trying to print all the names that werent found (as a msgbox) but when ever it goes in the for loop that I wrote in commandbutton2 command, it gives me error 9 subscript out of range.
I've been sitting on this for a while, but I couldn't find what the problem is. I would really appreciate some help. Thanks!
A few modifications can simplify your code and make it work as desired.
1- You dont need to do a loop to find a whole match for the name, you can use xlWhole parameter instead of xlPart.
2- Before adding a new name to your array s, check if the upper bound is already reached.
3- for button 2, dont unload the form before computing the message, because the array s is a member of the form, so if you unload it, the array is no longer valid in memory.
This modification of your code should work:
Private Sub CommandButton1_Click()
Dim wsheet As String
If OptStat = True Then wsheet = OptStat.Caption 'OptStat.Caption = Statistics - it's the name of the worksheet called Statistics
If OptGeo = True Then wsheet = OptGeo.Caption 'OptGeo.Caption = Geography - it's the name of the worksheet called Geography
If OptEco = True Then wsheet = OptEco.Caption 'OptEco.caption = Economics - it's the name of the worksheet called Economics
Set r = Worksheets(wsheet).Cells.Find(TextBox1.text, Range("a1"), xlFormulas, xlWhole, xlByRows, xlNext, False, , False)
' ^^^^^^^^
If Not r Is Nothing Then
Application.Goto r
Label2.Caption = TextBox1.text & " found in worksheet " & wsheet & " on cell " & r.address
TextBox1.text = ""
Else
If Count < UBound(s) Then ' <-- Check before adding a name to array
Count = Count + 1
s(Count) = TextBox1.text & " in " & wsheet
End If
Label2.Caption = TextBox1.text & " not found in " & wsheet
TextBox1.text = ""
End If
End Sub
Private Sub CommandButton2_Click()
Dim names As String
names = "The Following names are missing" & vbNewLine
For i = 1 To Count
names = names & s(i) & vbNewLine
Next i
Count = 0
Unload Me ' <--- here, not before, we still needed the array s
Worksheets(1).Select
MsgBox (names)
End Sub

Extracting Rows Based On Search Criteria

My issue is that I am trying to extract some information from a very large data sheet. The information that is being extracted is based on some search criteria that is entered on a form. The search form counts how many occurrences of this criteria exist, but then I need to extract the individual rows into a second sheet.
The bit I'm having difficulty with is understanding how to actually structure the extraction code. I'm in need of being pointed in the right direction. If the code can count how many occurrences there are, surely I can get the row numbers for those occurrences and extract the information, I'm just not getting anywhere trying to figure it out.
Here's my SEARCH code (this code works to get the number of occurrences based on the criteria asked)
Public Sub Run_Count_Click()
'// Set Ranges
Dim Cr_1, CR1_range, _
Cr_2, CR2_range, _
Cr_3, CR3_range, _
Cr_4, CR4_range, _
Cr_5, CR5_range _
As Range
'// Set Integers
Dim CR1, V1, CR1_Result, _
CR2, V2, CR2_Result, _
CR3, V3, CR3_Result, _
CR4, V4, CR4_Result, _
CR5, V5, CR5_Result, _
total_result, _
total_result2, _
total_result3, _
total_result4, _
total_result5 _
As Integer
'Set Strings
Dim V_1, V_2, V_3, V_4, V_5 As String
Dim ws As Worksheet
Set ws = Worksheets("database")
Sheets("Settings").Range("Start_Date").Value = Format(Me.R_Start.Value, "mm/dd/yyyy")
Sheets("Settings").Range("End_Date").Value = Format(Me.R_End.Value, "mm/dd/yyyy")
'Collect Start & End Dates
Dim dStartDate As Long
Dim dEndDate As Long
dStartDate = Sheets("Settings").Range("Start_Date").Value
dEndDate = Sheets("Settings").Range("End_Date").Value
ws.Activate
On Error GoTo error_Sdate:
Dim RowNum As Variant
RowNum = Application.WorksheetFunction.Match(dStartDate, Range("B1:B60000"), 0)
'MsgBox "Found " & Format(dStartDate, "dd/mm/yyyy") & " at row : " & RowNum
On Error GoTo error_Edate:
Dim RowNumEnd As Variant
RowNumEnd = Application.WorksheetFunction.Match(dEndDate, Range("B1:B60000"), 1)
' MsgBox "Found " & Format(dEndDate, "dd/mm/yyyy") & " at row : " & RowNumEnd
GoTo J1
error_Sdate:
Dim msg As String
msg = "You entered " & Format(dStartDate, "dd/mm/yyyy") & " as your Start Date, but no referrals were made on that date"
msg = msg & vbCrLf & "Please enter a different date in the Start Date box"
MsgBox msg, , "Start Date Not Found"
Err.Clear
Exit Sub
error_Edate:
msg = "You entered " & Format(dEndDate, "dd/mm/yyyy") & " as your End Date, but no referrals were made on that date"
msg = msg & vbCrLf & "Please enter a different date in the End Date box"
MsgBox msg, , "End Date Not Found"
Err.Clear
Exit Sub
J1:
'// Get Criteria From Form And Search Database Headers
Set Cr_1 = ws.Cells.Find(What:=Me.Count_Criteria_1.Value, After:=ws.Cells(1, 1), MatchCase:=False)
If Not Cr_1 Is Nothing Then
CR1 = Cr_1.Column '//Set CR1 as the Column in which the Criteria Header was found
Else
MsgBox "Criteria 1 Has Not Been Found In The Database. Report Has Failed To Generate"
Exit Sub
End If
'// Get Variable Value From Form And Set Shortcode
V_1 = Me.Criteria_1_Variable.Value
Set CR1_range = ws.Range(ws.Cells(RowNum, CR1), ws.Cells(RowNumEnd, CR1))
CR1_Result = Application.CountIf(CR1_range, V_1)
Me.Count_Result.visible = True
Me.Count_Result.Value = "Based On Your Search Criteria Of:" & vbNewLine & vbNewLine & _
"- " & Me.Count_Criteria_1.Value & ": " & Me.Criteria_1_Variable.Value & vbNewLine & vbNewLine & _
"The Results Are: " & CR1_Result & " entries found between the dates " & Format(dStartDate, "dd/mm/yyyy") & _
" and " & Format(dEndDate, "dd/mm/yyyy")
Exit Sub
Is there an easy way of doing this with a loop? I know loops are not the best way of handling things, but Im looking for anything that works and I can tweak to suit my needs.
Thanks if you can help in advance, it's a monster of a spreadsheet!
----------------------------
*Update With Accepted Answer:*
----------------------------
Public Sub Count_Extract_Click()
'Collect Information To Be Extracted
Set ws = Worksheets("database")
Set ps = Worksheets("Extracted Rows")
ps.Range("A3:AM60000").Clear
For i = RowNum To RowNumEnd
If ws.Cells(i, CR1).Value = V_1 Then
ws.Range("A" & i & ":AM" & i).Copy
ps.Activate
'find first empty row in database
emR = ps.Cells.Find(What:="*", SearchOrder:=xlRows, _
SearchDirection:=xlPrevious, LookIn:=xlValues).Row + 1
ps.Range("A" & emR & ":AM" & emR).PasteSpecial
End If
Next i
End If
End Sub
You should be able to set a For loop to check each value in the range you've found and copy it to (another cell, an array, whatever you like.)
For i = rowNum To rowNumEnd
If Cells(i,CR1).Value = V_1 Then
MsgBox "Found match on row " & i
End If
Next i
I haven't tested this, but it should work. Let me know if you get any errors.
I can't really try this out, but maybe you can. Keep the line V_1 = Me.Criteria_1_Variable.Value but replace the next 2 by :
CR1_Result = 0 'Initiates counter at 0
Dim CR1_Lines(1000) As Long 'Declares an array of 1001 (indexes 0-1000) Longs (big integers)
For x = RowNum To RowNumEnd 'Loops through all the rows of CR1
If ws.Cells(x, CR1) = V_1 Then 'Match!
'Double array size if capacity is reached
If CR1_Result = UBound(CR1_Lines) Then
ReDim Presrve CR1_Lines(UBound(CR1_Lines) * 2)
End If
'Store that line number in the array
CR1_Lines(CR1_Result) = x
'Increment count of matches
CR1_Result = CR1_Result + 1
End If
Next x 'Next row!
You can then loop through that array with this code :
For i = 0 to UBound(CR1_Lines)
'Do something! (Why not just an annoying pop-up box with the content!)
MsgBox CR1_Lines(i)
Next i
EDIT : I just read that the spreadsheet is monstruous, and re-dimensioning every time a new match is found might be neat, but it's a hell of a performance drop. I made some changes directly in the above code to make it somewhat more effective.
EDIT #2 : I've simplified code so you don't have anything to do but a copy paste (please forgive me not assuming RowNum and RowNumEnd had valid data). It should work exactly as accepted answer, but was posted a bit before and actually shows how to extract the line number. I understand if all you needed is a pop-up box with the line number, and will be satisfied with the upvote already received.

Resources