So I have a a function in my project that's is built to look for a workbook, check if it's open, then open that workbook if it's closed.
Public Function CheckOpen(wbName As String)
Dim wb As Workbook
Dim Ret_type As Integer
Dim strMsg As String
Dim strTitle As String
Dim tempstr As String
Dim x As Integer, y As Integer
On Error Resume Next
Set wb = Application.Workbooks(wbName)
On Error GoTo 0
strTitle = "Workbook Not Open"
strMsg = "Workbook " + wbName + " isn't open, press 'Retry' to fix."
If wb Is Nothing Then
'The workbook isn't open
Ret_type = MsgBox(strMsg, vbRetryCancel + vbQuestion, strTitle)
Select Case Ret_type
Case 4
'Retry Case
On Error Resume Next
For x = 1 To 2
For y = 1 To 2
Workbooks.Open (FindFilePath(x) + FileEndingManager(wbName, y))
Debug.Print (FindFilePath(x) + FileEndingManager(wbName, y))
Next y
Next x
Case 2
'Cancel Case
MsgBox "You clicked 'CANCEL' button."
End Select
End If
End Sub
TO CLARIFY: This function works when called directly. As in:
Sub TestCheck()
Call CheckOpen("WorkbookName")
End Sub
That's fine. Everything works. However, when I Call this function from a function entered in the actual spreadsheet the MsgBox appears, but it never opens the required workbook.
I don't understand what is going on.
Using Functions (UDFs)
As mentioned in comments #jkpieterse
A function called from a cell cannot do things like opening workbooks
Functions are able to be called from a cell just like Excel's built in functions and they always return something to the cell (even if it's only an error).
For a function to return something, other than an error, it must have a line like
FunctionName = *a value that complies with the function declaration*
Functions are available in auto-complete in Excel, even if they return a #VALUE! error which will occur if you don't set a return as described above. That's probably going to frustrate users!
Any other errors in a UDF will also cause a #VALUE! error and the only way to find them is by stepping through the function using F8. They don't show up at compile time!
So there are at least 2 reasons not to use a function where a sub would suffice.
If you want to change any book or sheet objects then declare a Sub procedure.
Use UDFs sparingly!
To answer your question:
I've tried to refactor your code into a worksheet callable function that will work with a Sub procedure while still using your variables.
I don't know what FindFilePath(x) or FileEndingManager(wbName, Y) do so assume they do work.
Public Function CheckOpen(wbName As String) As Boolean
Dim wb As Workbook
On Error Resume Next
Set wb = Application.Workbooks(wbName)
On Error GoTo 0
If Not wb Is Nothing Then
CheckOpen = True 'the essence of a "Function" - it returns a value!
End If
End Function
Or you could use this alternate function that doesn't require On Error Resume Next
Public Function CheckOpen1(wbName As String) As Boolean
Dim wb As Workbook
For Each wb In Workbooks 'in all the workbooks you have open
If wb.Name = wbName Then
CheckOpen = True 'if not, CheckOpen will remain FALSE
End If
Next
End Function
Then your Sub procedure looks like:
FileEndingManager(wbName, y)
Sub TestCheck()
Dim Ret_type As Integer
Dim strMsg As String
Dim strTitle As String
Dim tempstr As String
Dim x As Integer, y As Integer
If Not CheckOpen("WorkbookName") Then
strTitle = "Workbook Not Open"
strMsg = "WorkbookName isn't open, press 'Retry' to fix."
Ret_type = MsgBox(strMsg, vbRetryCancel + vbQuestion, strTitle)
Select Case Ret_type
Case 4
'Retry Case
On Error Resume Next
For x = 1 To 2
For y = 1 To 2
Workbooks.Open (FindFilePath(x) + FileEndingManager(wbName, y))
Debug.Print (FindFilePath(x) + FileEndingManager(wbName, y))
Next y
Next x
On Error GoTo 0 'end the Resume Next ASAP
'Check if the x, y loop has opened WorkbookName
If Not CheckOpen("WorkbookName") Then 'probably can't be opened
strMsg = "WorkbookName can't be opened! Clicking OK will exit sub."
MsgBox strMsg, vbCritical, strTitle
Exit Sub
End If
Case 2 'Tell user something they know! Not necessary
'Cancel Case
MsgBox "You clicked 'CANCEL' button." 'delete this line and just exit
Exit Sub
End Select
End If
'Haven't exited yet so safe to proceed
'...do things with WorkbookName
End Sub
Related
I recently found a macro to delete hidden names in a given workbook.
What I'd like to add to it is a pop up window that would first display how many names there are, and a yes no button for running it. Just to give the user an idea of how long the function will take to run if they choose to run it.
This is the code as is:
Dim RangeName As Name
On Error Resume Next
For Each RangeName In Names
ActiveWorkbook.Names(RangeName.Name).Delete
Next
On Error GoTo 0
End Sub
Try this:
Sub test()
Dim RangeName As Name
Dim answer As Integer
Dim str As String
On Error Resume Next
str = "Do you want to delete " & ActiveWorkbook.Names.Count & " names?"
answer = MsgBox(str, vbQuestion + vbYesNo + vbDefaultButton2, "Message Box Title")
If answer Then
For Each RangeName In Names
ActiveWorkbook.Names(RangeName.Name).Delete
Next
Else
Exit Sub
End If
On Error GoTo 0
End Sub
I am creating a macro that checks for errors in formulas for each sheet in a workbook. The macro runs when the BeforeSave hook is triggered and prompts the user decide if they still want to save when an error is found.
Public Sub errorCheck()
On Error Resume Next
Application.StatusBar = "Running: formulaErrorCheck"
Dim ws As Worksheet
Dim errors As range
Dim r As range
For Each ws In Worksheets
Set errors = Nothing
Set errors = ws.UsedRange.SpecialCells(xlCellTypeFormulas, xlErrors).cells
If Not (errors Is Nothing) Then
For Each r In errors
If IsError(r.Value) = True Then
Select Case r.Value
Case CVErr(xlErrValue), CVErr(xlErrDiv0), CVErr(xlErrName), CVErr(xlErrRef)
If MsgBox("Excel Sheet " + ws.name + " contains a reference error in cell " + r.Address(False, False) + ". Save anyway?", vbYesNo, "") = vbNo Then
Application.GoTo Reference:=r
GoTo quit_checking
End If
End Select
End If
Next
End If
Next
quit_checking:
Application.StatusBar = False
End Sub
Code in the Class Module that detects events:
OptionExplicit
Private WIthEvents App As Application
Private Sub App_WorkbookBeforeSave(ByVal wb As Workbook, ByVal SaveAsUI As Boolean, Cancel As Boolean)
Book_BeforeSave SaveAsUI, Cancel
End Sub
Public Sub Book_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Call errorCheck
End Sub
The issue that I'm having is that
Set errors = ws.UsedRange.SpecialCells(xlCellTypeFormulas, xlErrors).cells
returns the expected cells when I run the macro independently. However when the macro is called from the BeforeSave hook, it returns the entire used range. I thought maybe it was an issue with the reference to the workbook, but the worksheets are still iterated through as expected. I've tried removing On Error Resume Next to make sure there wasn't actually an error being thrown. I'm at a loss as to what the difference could be.
I am new at coding and am looking for some help. I have a code here that is giving me the "case outside select case" error, but my case is inside "select case" and "end select" so I'm not sure what I'm doing wrong.
Sub codematch()
Dim wbk As Workbook
Select Case response = MsgBox("Is the cursor in the first cell of the column?", vbYesNo, "Code Finder")
Case condition1
If response = 6 Then
Set wbk = Workbooks.Open("C:\test.xlsm")
Call wbk
ActiveCell.Offset(0, 1).Select
ActiveCell.FormulaR1C1 = "=VLOOKUP(RC[-1],'[test.xlsm]Sheet1'!C1:C2,2,0)"
Do Until ActiveCell = ""
Call wbk.Close(False)
Case condition2
If response = 7 Then
response = MsgBox("Position the Cursor in the correct location and try again", vbOKOnly)
End If
End Select
End Sub
Most of your code is pointless frankly. Who cares where the user has the cursor.
Sub codematch()
dim target as range
set target = range("B1") 'or whever you want to start
Dim wbk As Workbook
Set wbk = Workbooks.Open("C:\test.xlsm")
Target.FormulaR1C1 = "=VLOOKUP(RC[-1],'[test.xlsm]Sheet1'!C1:C2,2,0)"
do
set target = target.offset(1,0)
target.offset(-1,0).copy target
loop until target.offset(0,-1)=""
target.clear
target.parent.columns(target.column).cells.formula =
_target.parent.columns(target.column).cells.value 'convert to values
wbk.close false
end sub
Is what I think you were trying to do
basically I have a userform which I would like to use to enter 2 data into another macro which I already have. The userform is as below:
Basically, I would like the OK button to be clicked and the data in the two boxes will be entered into another macro that I have. It would also be great if the OK button can help in a sense that it will prompt a warning if one of the boxes is not filled up.
So far, I do not have much of a code for this..
Private Sub UserForm_Click()
TextBox1.SetFocus
Sub Enterval()
End Sub
Private Sub TextBox1_Change()
Dim ID As String
ID = UserForm3.TextBox1.Value
End Sub
Private Sub TextBox2_Change()
Dim ID2 As String
ID2 = UserForm3.TextBox2.Value
End Sub
Private Sub OKay_Click()
Enterval
End Sub
Would appreciate any tips and help. Thanks!
My other macro
Private Sub CommandButton1_Click()
Dim Name As String
Dim Problem As Integer
Dim Source As Worksheet, Target As Worksheet
Dim ItsAMatch As Boolean
Dim i As Integer
Set Source = ThisWorkbook.Worksheets("Sheet1")
Set Target = ThisWorkbook.Worksheets("Sheet2")
Name = Source.Range("A3")
Problem = Source.Range("I13")
Do Until IsEmpty(Target.Cells(4 + i, 6)) ' This will loop down through non empty cells from row 5 of column 2
If Target.Cells(4 + i, 6) = Name Then
ItsAMatch = True
Target.Cells(4 + i, 7) = Problem ' This will overwrite your "Problem" value if the name was already in the column
Exit Do
End If
i = i + 1
Loop
' This will write new records if the name hasn't been already found
If ItsAMatch = False Then
Target.Cells(3, 6).End(xlDown).Offset(1, 0) = Name
Target.Cells(4, 6).End(xlDown).Offset(0, 1) = Problem
End If
Set Source = Nothing
Set Target = Nothing
End Sub
Thats the macro i have. As u said, i change the
othermacro
to CommandButton1_Click()
but it doesn't work
Quoting geoB except for one thing: when you .Show your UserForm from a main Sub, you can also .Hide it at the end and the macro that called it will continue its procedures.
Sub Okay_Click()
Dim sID1 As String, sID2 As String
' A little variation
If Me.TextBox1 = "" Or Me.TextBox2 = "" Then
MsgBox "Please fill all the input fields"
Exit Sub
End If
Me.Hide
End Sub
To address your TextBox, you can write in your main Sub UserForm3.TextBox1 for example
There is no need for an Enterval function. Instead, assume the user can read and follow instructions, then test whether that indeed is the case. Note that in your code ID and ID2 will never be used because they exist only within the scope of the subroutines in which they are declared and receive values.
To get started:
Sub Okay_Click()
Dim sID1 As String, sID2 As String
sID1 = UserForm3.TextBox1.Value
sID2 = UserForm3.TextBox2.Value
If Len(sID1 & vbNullString) = 0 Then
MsgBox "Box A is empty"
Exit Sub
End If
If Len(sID2 & vbNullString) = 0 Then
MsgBox "Box B is empty"
Exit Sub
End If
'Now do something with sID1, sID2
otherMacro(sID1, sID2)
End Sub
For your other macro, declare it like this:
Sub otherMacro(ID1, ID2)
...
End Sub
Also, the SetFocus method should occur in the form open event.
I'm working on an Excel user form where the user can input a range. For example, they can put in "B5" and "B20".
I'm trying to do error handling to prevent the user from putting in an incorrect range. For Example, "asdf" and "fdsa".
The following code fails:
Private Sub cmdSend_Click()
Dim beginTerm As String
Dim endTerm As String
beginTerm = TermsBegin.Text
endTerm = TermsEnd.Text
If (IsError(Worksheets("Account Information").Range(beginTerm + ":" + endTerm)) = True) Then
MsgBox "Cell Range is invalid."
Exit Sub
End If
End Sub
I also tried the following:
Private Sub cmdSend_Click()
Dim beginTerm As String
Dim endTerm As String
beginTerm = TermsBegin.Text
endTerm = TermsEnd.Text
Dim myRange As Range
myRange = Worksheets("Account Information").Range(beginTerm + ":" + endTerm)
On Error GoTo ErrHandler
On Error GoTo 0
'other code ...
ErrHandler:
MsgBox "Cell Range is invalid."
Exit Sub
End Sub
My question is how can I handle the case that it fails?
Thanks!
You have to put
On Error GoTo ErrHandler
before the line that could throw the error.
If you need to get a range from a user, I would recommend using Application.InputBox with a Type = 8. This allows the user to select a range from the worksheet.
Check this out:
http://www.ozgrid.com/VBA/inputbox.htm
Also, if you are using a userform, you can also add a command button that will call the Application.InputBox to allow a user to select a range.
Quick example:
Private Sub CommandButton1_Click()
Dim r As Range
On Error Resume Next
Set r = Application.InputBox(Prompt:= _
"Please select a range with your Mouse to be bolded.", _
Title:="SPECIFY RANGE", Type:=8)
If Not r Is Nothing Then MsgBox r.Address
On Error GoTo 0
End Sub