At least one cell in a range is not empty - excel

I want to check if specific range (L32,M32;N32;O32;P32;Q32,R32;S32;T32).
If one of the cells is not empty a message should be displayed "FSFV check".
For Each cell In Range("L32:T32")
If cell.Value <> "" Then
MsgBox "Check with CRA if FSFV was performed and notify RA"
Else
End If
Next
End Sub
It displays the message eight times but I only want it once.

How about :
Sub Test()
Dim AnyData As Integer
AnyData = WorksheetFunction.CountA(Range("L32:T32"))
If AnyData = 0 Then
Exit Sub
Else
MsgBox "Check with CRA if FSFV was performed and notify RA"
End If
End Sub

If a Cell in a Range Is Blank...
If you're practicing loops, you could do the following.
Sub Test1()
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim cell As Range
For Each cell In ws.Range("L32:T32").Cells
If Len(CStr(cell.Value)) = 0 Then ' cell is blank
MsgBox "Check with CRA if FSFV was performed and notify RA", _
vbExclamation
Exit For ' blank cell found, stop looping
' Or:
'Exit Sub ' blank cell found, stop looping
End If
Next cell
' With 'Exit For' you'll end up here
' and you could continue with the sub.
End Sub
If not, rather use the following.
Sub Test2()
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
If Application.CountBlank(ws.Range("L32:T32")) > 0 Then
MsgBox "Check with CRA if FSFV was performed and notify RA", _
vbExclamation
End If
End Sub
Hardly Related But Interesting
If you were wondering what happens to an object-type Control variable (in this case cell) in a For Each...Next loop when the loop has finished uninterrupted, the following example proves that it is set to Nothing.
Sub Test3()
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim cell As Range
For Each cell In ws.Range("L32:T32").Cells
If Len(CStr(cell.Value)) = 0 Then Exit For
Next cell
If Not cell Is Nothing Then
MsgBox "Check with CRA if FSFV was performed and notify RA", _
vbExclamation
Exit Sub
End If
MsgBox "Continuing...", vbInformation
End Sub

Let me give you the simplest approach:
Dim Found As Boolean
Found = False
For Each cell In Range("L32:T32")
If cell.Value <> "" Then
Found = True
End If
Next
If Found Then
MsgBox "Check with CRA if FSFV was performed and notify RA"
End If
As you see, the fact that you have found an empty cell is kept in a Boolean variable, and afterwards you just use that information for showing your messagebox.

Related

Worksheet_Change Event only when call value changes

I want the user's name and the date to be entered into a specified column when any change is made.
I also have a snippet of code that forces any data that is pasted into the sheet to be pasted as values so the sheet's formatting is maintained.
I was able to write code that functioned properly, but the event was also being triggered even when the user double clicked in a cell and clicked out of the cell (i.e., no change was made). A user could accidentally click into a cell and leave it without making changes, but their name would be left behind as having made an edit.
I tried to incorporate this solution. Here is a simplified version of my code:
Private Sub Worksheet_Change(ByVal Target as Range)
Dim DesiredRange as Range
Dim TOld, TNew as String
Set DesiredRange as 'Whatever range I'm using
If Not Intersect(Target, DesiredRange) is Nothing Then
TNew = Target.Value
With Application
.EnableEvents = False
.Undo
End With
TOld = Target.Value
Target.Value = TNew
If Application.CutCopyMode = xlCopy Then
Application.EnableEvents = False
Application.Undo
Target.PasteSpecial Paste:=xlPasteValues
Application.EnableEvents = True
End if
If TOld <> TNew Then
Target.Offset(0, 23 - Target.Column) = Application.Username & vbNewLine & Date
End If
Application.EnableEvents = True
End if
End Sub
I am encountering the following issue:
When a user double clicks into a cell and clicks into another cell, the event is not triggered (i.e., the user's name and date is not left in the cell) but the active cell is reverted into the original cell, rather than the one they clicked into after double-clicking.
So a user would double click into a cell, do nothing, then click into another cell, and the active cell would revert to the first cell they were in.
This is also happening after the user inputs their change into the cell and presses enter.
I also encounter an error when something is pasted into the sheet, causing the code to not execute properly.
Prevent Worksheet Change When No Change
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo ClearError
Dim srg As Range: Set srg = Range("B5:E10")
Dim irg As Range: Set irg = Intersect(srg, Target)
If irg Is Nothing Then Exit Sub
Dim coll As Collection: Set coll = New Collection
Dim iCell As Range
For Each iCell In irg.Cells
coll.Add iCell.Value
Next iCell
With Application
.EnableEvents = False
.Undo
End With
Dim drg As Range
Dim n As Long
For Each iCell In irg.Cells
n = n + 1
If iCell.Value <> coll(n) Then
iCell.Value = coll(n) ' write different value
If drg Is Nothing Then ' combine the cells for user and date
Set drg = iCell
Else
Set drg = Union(drg, iCell)
End If
End If
Next iCell
If Not drg Is Nothing Then
' Use 'Now' while testing or you will see no difference.
' Later switch to 'Date'.
Intersect(drg.EntireRow, Columns("W")).Value = Application.UserName _
& vbNewLine & Format(Now, "mm/dd/yyyy hh:mm:ss") ' Date
End If
SafeExit:
If Not Application.EnableEvents Then Application.EnableEvents = True
Exit Sub
ClearError:
Debug.Print "Run-time error '" & Err.Number & "': " & Err.Description
Resume SafeExit
End Sub

Appending text to cell but stuck in loop

My problem is that my code doesn't work how I think it should work. I have Loop in my worksheet_change macro, and in that loop I want that if statement is correct (MsgBox button pressed Yes), what is written in that cell would have appended value at the end of that text.
But if I run this macro and I press Yes - cell value has the value at the end, but MsgBox comes right again, and I'm stuck in that loop... I'm new to VBA programming and syntax.
Can someone help me and explain my mistake?
Code:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rLockable As Range
Dim cl As Range
Set rLockable = Range("C13:J1000")
Set cl = Range("C13:J1000")
Set cele = Range("K13:K1000")
vardas = ActiveWorkbook.Sheets("Login").Range("O8").Value
Select Case True
Case Not Intersect(rLockable, Target) Is Nothing
'If target is within the range then do nothing
If Intersect(rLockable, Target) Is Nothing Then Exit Sub
ActiveSheet.Unprotect Password:="1234"
For Each cl In Target
If cl.Value <> "" Then
check = MsgBox("Ar áraðyti áraðà? Koreguoti áraðo nebegalësite.", vbYesNo, "Áraðo iðsaugojimas")
If check = vbYes Then
Target.Worksheet.Unprotect Password:="1234"
cl.MergeArea.Locked = True
cl.Value = cl.Value & " " + vardas
Else
cl.Value = ""
ActiveSheet.Protect Password:="1234"
End If
End If
Exit For
Next cl
Case Not Intersect(Range("K13:K1000"), Target) Is Nothing
ActiveSheet.Unprotect Password:="1234"
For Each cele In Target
If cele.Value <> "" Then
cele.Offset(0, 2).MergeArea.Value = vardas
End If
Exit For
Next cele
End Select
ActiveSheet.Protect Password:="1234"
End Sub
Before making a change to the worksheet you need to set enableEvent to false or the worksheet_change event will kick in again.
application.enableEvents=false
'change the worksheet
application.enableEvents=true
'resets the worksheet_change event

excel vba - iterate through specific sheets in range

I would like to iterate through a list of sheets where the list is determined by a Range.
If I hard-code the list everything is fine.
what I'd like is to refer to a range that contains the sheet names (as it's variable).
Set mySheets = Sheets(Array("sheetOne", "sheetTwo", "sheetThree"))
With ActiveWorkbook
For Each ws In mySheets
'do the stuff here
Next ws
End With
so something like this:
Set mySheets = Sheets(Range("A1:E1"))
Any ideas?
This will work:
Sub MySub()
On Error Resume Next
Set mySheets = Sheets(removeEmpty(rangeToArray(Range("A1:E1"))))
If Err.Number = 9 Then
MsgBox "An error has occurred. Check if all sheet names are correct and retry.", vbCritical
Exit Sub
End If
On Error GoTo 0
With ActiveWorkbook
For Each ws In mySheets
'do the stuff here
Next ws
End With
End Sub
'This will transpose a Range into an Array()
Function rangeToArray(rng As Range) As Variant
rangeToArray = Application.Transpose(Application.Transpose(rng))
End Function
'This will remove empty values and duplicates
Function removeEmpty(arr As Variant) As Variant
Dim result As New Scripting.Dictionary
Dim element As Variant
For Each element In arr
If element <> "" And Not result.Exists(element) Then
result.Add element, Nothing
End If
Next
removeEmpty = result.Keys
End Function
This will load dynamically Sheets contained in your Range.
Edit
Added Function removeEmpty(...) to remove empty values and duplicates.
Note: the Function rangeToArray() is needed to return data in Array() format.
I hope this helps.
I would provide this solution, which does load the sheetnames into an array:
Notice that you have to transpose the Data if the values are ordered horizontal.
Public Sub test()
Dim mySheet As Variant
Dim sheet As Variant
mySheet = Application.Transpose(Tabelle1.Range("A1:E1").Value) 'load your Values into an Array, of course the range can also be dynamic
For Each sheet In mySheet
Debug.Print sheet 'print the sheet names, just for explaining purposes
'it may be necessary to use CStr(sheet) if you want to refer to a sheet like Thisworkbook.Worksheets(CStr(sheet))
'Do something
Next sheet
Erase mySheet 'delete the Array out of memory
End Sub
I demonstrate the code below which does what you want using an animated gif (click for better detail)
Option Explicit
Sub iterateSheets()
Dim sh As Worksheet, shName As String, i As Integer
i = 0
For Each sh In ThisWorkbook.Worksheets
shName = sh.Range("A1").Offset(i, 0)
Worksheets(shName).Range("A1").Offset(i, 0).Font.Color = vbRed
i = i + 1
Next
End Sub
you could do like this:
Sub DoThat()
Dim cell As Range
For Each cell In Range("A1:E1").SpecialCells(xlCellTypeConstants)
If Worksheets(cell.Value2) Is Nothing Then
MsgBox cell.Value2 & " is not a sheet name in " & ActiveWorkbook.Name & " workbook"
Else
With Worksheets(cell.Value2)
'do the stuff here
Debug.Print .Name
End With
End If
Next
End Sub
or the other way around:
Sub DoThatTheOtherWayAround()
Dim sht As Worksheet
For Each sht In Worksheets
If Not IsError(Application.Match(sht.Name, Range("A1:E1"), 0)) Then
'do the stuff here
Debug.Print sht.Name
End If
Next
End Sub
but in this latter case, you wouldn't be advised in case of any A1:E1 value not corresponding to actual sheet name

Check if a Sheet is Protected in Excel using VBA

I have a code which deletes a value from a locked sheet. Whenever I run the code, Error message
Delete method of Range class failed
is displayed. How do I prompt the user with a message such as first unprotect the sheet?
Sub DeleteRow()
Dim rng As Range
On Error Resume Next
With Selection.Cells(1)
Set rng = Intersect(.EntireRow, ActiveCell.ListObject.DataBodyRange)
On Error GoTo 0
If rng Is Nothing Then
MsgBox "Please select a valid table cell.", vbCritical
Else
rng.delete xlShiftUp
End If
End With
End Sub
This will Work:
Activesheet.ProtectContents will tell you if a sheet is protected or not.
Sub DeleteRow()
Dim rng As Range
On Error Resume Next
If ActiveSheet.ProtectContents = False Then
With Selection.Cells(1)
Set rng = Intersect(.EntireRow, ActiveCell.ListObject.DataBodyRange)
On Error GoTo 0
If rng Is Nothing Then
MsgBox "Please select a valid table cell.", vbCritical
Else
rng.Delete xlShiftUp
End If
End With
Else: MsgBox "Unprotect the Sheet First!"
End If
End Sub

Print Macro either gives me error 1004 or crashes excel

I am currently trying to build a a button click print macro to print all but two sheets (currently 5 total sheets). In addition I have a third sheet that I want the user to define how many pages of it to print. When I try to to have it be based off of a cell I get error 1004. To see if the rest of the code works and I make the module have a defined number excel crashes on me after it does the first sheet to print.
Sub Button1_Click()
Dim Wks As Worksheet, xcell As Integer
'The next line is where I get 1004 but when I change it to a fixed number it crashes excel'
xcell = Sheets("Print Page").Range("B12").Value
If xcell < 1 Then
MsgBox ("Please Enter the number of pages needed")
Exit Sub
Else
For Each Wks In ActiveWorkbook.Worksheets
If Wks.Visible = xlSheetVisible Then
If Wks.Name = "Print Page" Then
Else
If Wks.Name = "Specs" Then
Else
If Wks.Name = "Data" Then
Wks.PrintOut From:=1, To:=xcell
Else
Wks.PrintOut
End If
End If
End If
End If
Next Wks
End If
End Sub
There is probably a cleaner way to write this that I'm not aware of.
Not sure will these work on Mac. Below will keep prompting a value > 0 if that B12 is less than 1. Simplified your worksheet conditions and refers all objects within the workbook the macro is in.
Sub Button1_Click()
Dim Wks As Worksheet, xcell As Long, sInput As String
With ThisWorkbook
With .Worksheets("Print Page").Range("B12")
xcell = CLng(.Value)
If xcell < 1 Then
Do
sInput = InputBox("Please Enter the number of pages needed:", "Pages to print", 1)
If Len(sInput) = 0 Then Exit Sub ' Abort if clicked Cancel/left empty and clicked OK
If IsNumeric(sInput) Then xcell = CLng(sInput)
Loop Until xcell > 0
.Value = xcell
End If
End With
For Each Wks In .Worksheets
If Wks.Visible = xlSheetVisible Then
Select Case Wks.Name
Case "Print Page", "Specs" ' Skip!
Case "Data"
Wks.PrintOut From:=1, To:=xcell
Case Else
Wks.PrintOut
End Select
End If
Next Wks
End With
End Sub

Resources