Can you write a function in VBA that displays values in cells other than then cell the function is in? - vba

I am trying to write something like: If the value "O" is found in this range (M3:Q3) then fill all the cells in the range (M3:Q3) not containing the "O" with "X".
Here is what I have so far. I have had a lot of trouble getting the function to populate a value in anything other than the cell the function is being called.
Function positive(range_data As range)
Dim display As String
display = ""
Dim positiveValue As Boolean
For Each Item In range_data
If Item.Value = "O" Then
positiveValue = True
End If
Next
If positiveValue = True Then
For Each Item In range_data
If Item.Value = "" Then
Worksheets("Sheet1").Cells(Item.row, Item.Column).Value = "X"
End If
Next
End If
'positive = range_data
'positive = display
End Function
Thank you for your help!

your code seems to work fine, I just turned it into a Sub
Sub positive(range_data As Range)
Dim display As String
display = ""
Dim positiveValue As Boolean
For Each Item In range_data
If Item.Value = "O" Then
positiveValue = True
End If
Next
If positiveValue = True Then
For Each Item In range_data
If Item.Value <> "O" Then
Worksheets("Sheet1").Cells(Item.Row, Item.Column).Value = "X"
End If
Next
End If
'positive = range_data
'positive = display
End Sub
Sub MAIN()
positive Range("A1:C4")
End Sub

The function can work if called from a Sub:
Function positive(range_data As Range)
Dim display As String
display = ""
Dim positiveValue As Boolean
For Each Item In range_data
If Item.Value = "O" Then
positiveValue = True
End If
Next
If positiveValue = True Then
For Each Item In range_data
If Item.Value = "" Then
Worksheets("Sheet1").Cells(Item.Row, Item.Column).Value = "X"
End If
Next
End If
positive = "whatever"
End Function
Sub MAIN()
x = positive(Range("M3:Q3"))
End Sub
But there is no reason to use a function since the function's output is not used.

Related

Excel keeps crashing with Worksheet_selectionChange

I am running two VBA formulas.
The first hides all cells with empty information the first column.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim c As Range
On Error Resume Next
Application.ScreenUpdating = False
For Each c In Range("A3:A49")
If c.Value = vbNullString Then
c.EntireRow.Hidden = True
End If
Next c
For Each c In Range("A3:A47")
If c.Value <> vbNullString Then
c.EntireRow.Hidden = False
End If
Next c
Application.ScreenUpdating = True
End Sub
The second Formula strings data together and placeses this information in the next cell that is empty (aka the first hidden cell) when the button is clicked.
Option Explicit
Dim iwsh As Worksheet
Dim owsh As Worksheet
Dim output As String
Dim i As Integer
Sub Copy()
Set iwsh = Worksheets("Budget")
Set owsh = Worksheets("Release Burnup")
i = 3
While owsh.Cells(i, 1) <> ""
i = i + 1
Wend
output = "R" & iwsh.Cells(13, 2).Value & "-S" & iwsh.Cells(14, 2).Value
owsh.Cells(i, 1) = output
ActiveSheet.EnableCalculation = False
ActiveSheet.EnableCalculation = True
End Sub
Previously, this has been causing no problem... Something has happened that is causing the workbook to crash anytime I try to delete information out of one of the cells with the new data.
PS: This is the list of my other formulas. maybe there is something in these that is interacting with the formentioned code?
Private Sub NewMemberBut_Click()
'causes userform to appear
NewMember.Show
'reformats button because button kept changing size and font
NewMemberBut.AutoSize = False
NewMemberBut.AutoSize = True
NewMemberBut.Height = 40.25
NewMemberBut.Left = 303.75
NewMemberBut.Width = 150
End Sub
'Similar code to the problematic code in question, but this one works fine
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim c As Range
On Error Resume Next
Application.ScreenUpdating = False
For Each c In Range("A3:A35,A41:A80")
If c.Value = vbNullString Then
c.EntireRow.Hidden = True
End If
Next c
For Each c In Range("A3:A35,A41:A80")
If c.Value <> vbNullString Then
c.EntireRow.Hidden = False
End If
Next c
Application.ScreenUpdating = True
End Sub
'Code for UserForm
Option Explicit
Dim mName As String
Dim cName As String
Dim mRole As String
Dim cRole As String
Dim i As Integer
Dim x As Integer
Dim Perc As Integer
Dim Vac As Integer
Dim Prj As Worksheet
Dim Bud As Worksheet
Private Sub NewMember_Initialize()
txtName.Value = ""
cboRoleList.Clear
Scrum.Value = False
txtPercent.Value = ""
txtVacation.Value = ""
txtName.SetFocus
End Sub
Private Sub AddMember_Click()
If Me.txtName.Value = "" Then
MsgBox "Please enter a Member name.", vbExclamation, "New Member"
Me.txtName.SetFocus
Exit Sub
End If
If Me.cboRoleList = "Other" And Me.txtCustomRole = "" Then
MsgBox "Please provide a role name.", vbExclamation, "Other Role"
Exit Sub
End If
If Me.cboRoleList.Value = "" Then
MsgBox "Please select a Role.", vbExclamation, "Member Role"
Me.cboRoleList.SetFocus
Exit Sub
End If
If Me.cboRoleList <> "Other" And Me.txtPercent = "" Then
MsgBox "Please select a valid percentage to be applied to this sprint.", vbExclamation, "Sprint Percent"
Me.txtPercent.SetFocus
Exit Sub
End If
If Me.txtPercent.Value > 100 And Me.txtPercent <> "" Then
MsgBox "Please select a valid percentage to be applied to this sprint.", vbExclamation, "Sprint Percent"
Me.txtPercent.SetFocus
Exit Sub
End If
If Me.txtVacation.Value = "" Then
Me.txtVacation.Value = 0
End If
Dim i As Long
Set Prj = Worksheets("Project Team")
Set Bud = Worksheets("Budget")
Prj.Activate
i = 5
x = 1
If Me.cboRoleList.Value = "Other" Then
i = 46
End If
While Prj.Cells(i, 1) <> ""
i = i + 1
Wend
If cboRoleList = "Other" Then
Cells(i, x).Value = txtCustomRole.Value
End If
If cboRoleList <> "Other" Then
Cells(i, x).Value = cboRoleList.Value
End If
x = x + 1
Cells(i, x).Value = txtName.Value
x = x + 1
If Me.cboRoleList.Value <> "Other" Then
Cells(i, x).Value = txtPercent.Value
End If
Unload Me
End Sub
Private Sub CloseBut_Click()
Unload Me
End Sub
Change the event driven Worksheet_SelectionChange to Worksheet_Change and isolate further by only processing when something changes in A3:A49.
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A3:A49")) Is Nothing Then
On Error GoTo safe_exit
Application.EnableEvents = False
Application.ScreenUpdating = False
Dim c As Range
For Each c In Intersect(Target, Range("A3:A49"))
c.EntireRow.Hidden = CBool(c.Value = vbNullString)
Next c
End If
safe_exit:
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
Caveat: A Worksheet_Change is not triggered on the change in a cell from the cell's formula. Only by typing, deleting or dragging a cell's contents. Adding or removing a formula will trigger it but not when a formula's result changes from another value somewhere in the workbook changing. This should not affect you as no formula can return vbNullString but it is worth mentioning for others.

Excel: Selecting single cell vs. whole column in VBA

This is a newb question:
I have two sheets. Sheet 1 is where there is a form to enter data. When you double click on any cell in column A, a user form pop up comes up. You enter a few keys from any entry that is in the A column of sheet 2 and it autocompletes.
The problem I am having is: I only want to enter data on a specific cell, for instance A1 .. not the whole column of A. A second thing I wanted was that instead of a double click, I wanted it to work with a single click. Can anyone please help.
Here is the VBA code for Sheet 1 where you enter the data
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim uiChosen As String
Dim MyList As Range
Dim myPrompt As String
If Target.Column <> 1 Then Exit Sub
Set MyList = Sheet2.Range("Cariler")
myPrompt = "Lütfen Bir Cari Seçin"
uiChosen = UserForm1.ChooseFromList(MyList, myPrompt, Default:=Target.Value, xlFilterStyle:=xlContains)
If StrPtr(uiChosen) <> 0 Then
Target.Value = uiChosen
Cancel = True
End If
End Sub
Here is the code for the user form:
Option Explicit
' in userform's code module
Dim FullList As Variant
Dim FilterStyle As XlContainsOperator
Dim DisableMyEvents As Boolean
Dim AbortOne As Boolean
Const xlNoFilter As Long = xlNone
Private Sub butCancel_Click()
Unload Me
End Sub
Private Sub butOK_Click()
Me.Tag = "OK"
Me.Hide
End Sub
Private Sub ComboBox1_Change()
Dim oneItem As Variant
Dim FilteredItems() As String
Dim NotFlag As Boolean
Dim Pointer As Long, i As Long
If DisableMyEvents Then Exit Sub
If AbortOne Then AbortOne = False: Exit Sub
If TypeName(FullList) Like "*()" Then
ReDim FilteredItems(1 To UBound(FullList))
DisableMyEvents = True
Pointer = 0
With Me.ComboBox1
Select Case FilterStyle
Case xlBeginsWith: .Tag = LCase(.Text) & "*"
Case xlContains: .Tag = "*" & LCase(.Text) & "*"
Case xlDoesNotContain: .Tag = "*" & LCase(.Text) & "*": NotFlag = True
Case xlEndsWith: .Tag = "*" & LCase(.Text)
Case xlNoFilter: .Tag = "*"
End Select
For Each oneItem In FullList
If (LCase(oneItem) Like .Tag) Xor NotFlag Then
Pointer = Pointer + 1
FilteredItems(Pointer) = oneItem
End If
Next oneItem
.List = FilteredItems
.DropDown
DisableMyEvents = False
If Pointer = 1 Then .ListIndex = 0
End With
End If
End Sub
Private Sub ComboBox1_Click()
butOK.SetFocus
End Sub
Private Sub ComboBox1_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
Select Case KeyCode
Case vbKeyReturn: Call butOK_Click
Case vbKeyUp, vbKeyDown: AbortOne = True
End Select
End Sub
Private Sub Label1_Click()
End Sub
Private Sub UserForm_Activate()
ComboBox1.SetFocus
If ComboBox1.Text <> vbNullString Then
Call ComboBox1_Change
End If
End Sub
Private Sub UserForm_Initialize()
ComboBox1.MatchEntry = fmMatchEntryNone
End Sub
Public Function ChooseFromList(ListSource As Variant, Optional Prompt As String = "Choose one item", _
Optional Title As String = "Cari Arama Programı", Optional Default As String, _
Optional xlFilterStyle As XlContainsOperator = xlBeginsWith) As String
Dim Pointer As Long, oneItem As Variant
If TypeName(ListSource) = "Range" Then
With ListSource
Set ListSource = Application.Intersect(.Cells, .Parent.UsedRange)
End With
If ListSource Is Nothing Then Exit Function
If ListSource.Cells.Count = 1 Then
ReDim FullList(1 To 1): FullList(1) = ListSource.Value
ElseIf ListSource.Rows.Count = 1 Then
FullList = Application.Transpose(Application.Transpose(ListSource))
Else
FullList = Application.Transpose(ListSource)
End If
ElseIf TypeName(ListSource) Like "*()" Then
ReDim FullList(1 To 1)
For Each oneItem In ListSource
Pointer = Pointer + 1
If UBound(FullList) < Pointer Then ReDim Preserve FullList(1 To 2 * Pointer)
FullList(Pointer) = oneItem
Next oneItem
ReDim Preserve FullList(1 To Pointer)
ElseIf Not IsObject(ListSource) Then
ReDim FullList(1 To 1)
FullList(1) = CStr(ListSource)
Else
Err.Raise 1004
End If
Me.Caption = Title
Label1.Caption = Prompt
FilterStyle = xlFilterStyle
DisableMyEvents = True
ComboBox1.Text = Default
ComboBox1.List = FullList
DisableMyEvents = False
butOK.SetFocus
Me.Show
With UserForm1
If .Tag = "OK" Then ChooseFromList = .ComboBox1.Text
End With
End Function
There is no single click event. Use Intersect to test wherther or not the target cell is within a given range.
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
If Not Intersect(Target, Range("A1")) Is Nothing Then
Dim uiChosen As String
Dim MyList As Range
Dim myPrompt As String
If Target.Column <> 1 Then Exit Sub
Set MyList = Sheet2.Range("Cariler")
myPrompt = "Lütfen Bir Cari Seçin"
uiChosen = UserForm1.ChooseFromList(MyList, myPrompt, Default:=Target.Value, xlFilterStyle:=xlContains)
If StrPtr(uiChosen) <> 0 Then
Target.Value = uiChosen
Cancel = True
End If
End If
End Sub

Check if a cell from a selected range is visible

I have a VBA function in Excel returns a concatenated string of text from cells selected by users.
This works as I require, however if there are hidden cells in the selection, the value of the hidden cell is included, which is undesirable. An example of when this issue occurs is when a table is filtered.
Is there a way to amend my function to check if the cell that is being read is visible?
Sub ConcatEmialAddresses()
Dim EmailAddresses As String
ActiveSheet.Range("C3").Value = combineSelected()
ActiveSheet.Range("C3").Select
Call MsgBox("The email address string from cell ""C3"" has been copied to your clipboard.", vbOKOnly, "Sit back, relax, it's all been taken care of...")
End Sub
Function combineSelected(Optional ByVal separator As String = "; ", _
Optional ByVal copyText As Boolean = True) As String
Dim cellValue As Range
Dim outputText As String
For Each cellValue In Selection
outputText = outputText & cellValue & separator
Next cellValue
If Right(outputText, 2) = separator Then outputText = Left(outputText, Len(outputText) - 2)
combineSelected = outputText
End Function
To determine if a Range has an hidden cell, I would check that the height/width of each row/column is different from zero:
Function HasHiddenCell(source As Range) As Boolean
Dim rg As Range
'check the columns
If VBA.IsNull(source.ColumnWidth) Then
For Each rg In source.Columns
If rg.ColumnWidth = 0 Then
HasHiddenCell = True
Exit Function
End If
Next
End If
' check the rows
If VBA.IsNull(source.RowHeight) Then
For Each rg In source.rows
If rg.RowHeight = 0 Then
HasHiddenCell = True
Exit Function
End If
Next
End If
End Function
Sub UsageExample()
If HasHiddenCell(selection) Then
Debug.Print "A cell is hidden"
Else
Debug.Print "all cells are visible"
End If
End Sub
I used this
Function areCellsHidden(Target As Range)
areCellsHidden = False
If (Target.Rows.Hidden = True) Then
areCellsHidden = True
ElseIf (Target.Columns.Hidden = True) Then
areCellsHidden = True
ElseIf (Target.Count > 1) Then
If _
Target.Count <> Target.Columns.SpecialCells(xlCellTypeVisible).Count _
Or Target.Count <> Target.Rows.SpecialCells(xlCellTypeVisible).Count _
Then
areCellsHidden = True
End If
End If
End Function

Why is VBA array not loading integers

Here is a function I found online to help loop through values and select pivot filter items that match the values. My problem is that the array created in the Sub Filter_Bana does not get loaded with the values from the range named as "Bana" (aka varItemList). The range "Bana" consists of about twenty numbers (integers). When I run the sub (at the bottom), I keep receiving the MsgBox "None of filter list items found" from the function. I have been trying to figure this out for a while and don't think any of the named integer list "Bana" is getting loaded into "varItemList." In other words, when varItemList is passed to the function, the array is empty. Please see code:
**EDIT: I found the problem. The problems I was having related to two issues: 1) I am bad at VBA, 2) the data type of the pivot items in my pivot field did not match the data type of the array. I switched the array components to a 5-character text and adjusted the SQL query to bring in my investorNumber as a 5-character text (i.e. the array needed to be loaded with a character string and my pivot field needed data of the character type; if there was a way to do this with integers, I'd love to know. **
Private Function Filter_PivotField(pvtField As PivotField, _
varItemList As Variant)
Dim strItem1 As Long, blTmp As Boolean, i As Long
On Error Resume Next
Debug.Print varItemList
Application.ScreenUpdating = False
With pvtField
If .Orientation = xlPageField Then .EnableMultiplePageItems = True
For i = LBound(varItemList) To UBound(varItemList)
blTmp = Not (IsError(.PivotItems(varItemList(i)).Visible))
If blTmp Then
strItem1 = .PivotItems(varItemList(i))
Exit For
End If
Next i
If strItem1 = "" Then
MsgBox "None of filter list items found."
Exit Function
End If
.PivotItems(strItem1).Visible = True
For i = 1 To .PivotItems.Count
If .PivotItems(i) <> strItem1 And _
.PivotItems(i).Visible = True Then
.PivotItems(i).Visible = False
End If
Next i
For i = LBound(varItemList) To UBound(varItemList)
.PivotItems(varItemList(i)).Visible = True
Next i
End With
Application.ScreenUpdating = True
End Function
Sub Filter_Bana()
Filter_PivotField _
pvtField:=Sheets("Pres1&2_Pivot").PivotTables("PivotTable1").PivotFields("investorNumber"), _
varItemList:=Application.Transpose(Sheets("Controls").Range("Bana"))
End Sub`
This code works. The only other key is making sure my array is loaded with character type data (not integers) and that the pivot field also contains character type data (adjusted in the query/not displayed)
Private Function Filter_PivotField(pvtField As PivotField, _
varItemList As Variant)
Dim strItem1 As String, blTmp As Boolean, i As Long
Application.ScreenUpdating = False
With pvtField
If .Orientation = xlPageField Then .EnableMultiplePageItems = True
For i = LBound(varItemList) To UBound(varItemList)
blTmp = Not (IsError(.PivotItems(varItemList(i)).Visible))
If blTmp Then
strItem1 = .PivotItems(varItemList(i))
Exit For
End If
Next i
If strItem1 = "" Then
MsgBox "None of filter list items found."
Exit Function
End If
.PivotItems(strItem1).Visible = True
For i = 1 To .PivotItems.Count
If .PivotItems(i) <> strItem1 And _
.PivotItems(i).Visible = True Then
.PivotItems(i).Visible = False
End If
Next i
For i = LBound(varItemList) To UBound(varItemList)
.PivotItems(varItemList(i)).Visible = True
Next i
End With
Application.ScreenUpdating = True
End Function
Sub Filter_Bana()
Filter_PivotField _
pvtField:=ThisWorkbook.Worksheets("Pres1_2_Pivot").PivotTables("PivotTable1").PivotFields("investorNumber"), _
varItemList:=Application.Transpose(Sheets("Controls").Range("Bana"))
End Sub

Inputbox, empty input clears cell

I'm trying to make a macro which checks a cell for changes. If the cell changes a user has to provide a date. When cancel is pressed or the field is left blank, another cell should be cleared. When cancel is pressed the cell gets cleared, but when 'ok' is pressed without providing a date I get an error. My code is as follows:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim myValue As Date
If Target.Address = "$E$17" Then
myValue = Application.InputBox("Geef de revisiedatum op, DD/MM/JJ", "Revisie datum")
Range("$I$17").Value = myValue
If myValue = False Then
Range("$I$17") = ""
Exit Sub
End If
End If
If Target.Address = "$E$20" Then
myValue = Application.InputBox("Geef de revisiedatum op, DD/MM/JJ", "Revisie datum")
Range("$I$20").Value = myValue
If myValue = False Then
Range("$I$20") = ""
Exit Sub
End If
End If
End Sub
This is because you have myvalue declared as a date. When no value is returned it is a string that a date can't handle.
Change myValue to a string. Then your checks for myValue = False will change to If myValue = "" then.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim myValue As String
If Target.Address = "$E$17" Then
myValue = Application.InputBox("Geef de revisiedatum op, DD/MM/JJ", "Revisie datum")
Range("$I$17").Value = myValue
If myValue = "" Then
Range("$I$17") = ""
Exit Sub
End If
End If
If Target.Address = "$E$20" Then
myValue = Application.InputBox("Geef de revisiedatum op, DD/MM/JJ", "Revisie datum")
Range("$I$20").Value = myValue
If myValue = "" Then
Range("$I$20") = ""
Exit Sub
End If
End If
End Sub