How can i call a Sub using hyperlinks - vba

I am newbie to VBA; I have a question:
How can I call sub to delete a cell in a sheet by using a Hyperlinks from another sheet.
A structure of the code is greatly appreciated.

Event handler in worksheet which contains the hyperlink:
Private Sub Worksheet_FollowHyperlink(ByVal Target As Hyperlink)
If Target.TextToDisplay = "Clear Cell" Then
ClearThatCell
End If
End Sub
Note there's also a Workbook-level event: use that if you'd like to be able to trap any hyperlink click in the workbook.
Private Sub Workbook_SheetFollowHyperlink(ByVal Sh As Object, _
ByVal Target As Hyperlink)
End Sub
Called code:
Sub ClearThatCell()
ThisWorkbook.Sheets("Sheet2").Range("A1").ClearContents
End Sub

' Public m_data_wks As Worksheet
Sub init_links
Dim v_r As Range, n_rows as Integer
Set v_r = m_data_wks.Cells(1, 1)
n_rows = 3 'is an example of filling up cells with hyperlinks
For I = 1 To n_rows
v_r.Value = I
'The key: adding hyperlink to the v_r cell with a special subaddress for alternative usage.
'The hyperlink looks like the ordinary but points to itself.
m_data_wks.Hyperlinks.Add Anchor:=v_r, Address:="", SubAddress:=v_r.Address(External:=False, RowAbsolute:=False, columnAbsolute:=False)
Set v_r = v_r.Offset(1)
Next I
end sub
'Private WithEvents App As Application
Private Sub App_SheetFollowHyperlink(ByVal Sh As Object, ByVal Target As Hyperlink)
Dim v_dst As Worksheet, v_index_s As String, v_index_i As Integer
'get_context sets v_dst the proper worksheet
'get_context Sh.Parent, v_dst
If v_dst Is Nothing Then Exit Sub
On Error GoTo Ext
'Using the value of the cell for choosing which to delete
v_index_s = CStr(Sh.Range(Target.SubAddress).Value)
If v_index_s = "#" Then
v_index_i = 0
Else
v_index_i = CLng(v_index_s)
End If
'Here the v_index_i points to the row instead of a cell for deleting
v_dst.Rows(v_index_i).Delete
Exit Sub
Ext:
If Err.Number <> 0 Then
MsgBox "Error occured while deleting by hyperlink: " & Err.Number & ", " & Err.Description, vbExclamation, "Non critical error"
Err.Clear
End If
End Sub

Related

Private Sub Worksheet_SelectionChange/Worksheet_Change

Good day, I need to create 2 private macros in one workbook - one which stores content of cell after clicking on it and second one which will store the new value of cell and send and email with body of old text in cell and new text in cell.
Truth to be told, I'm not sure if this is the right way to do it (or if it's even possible) but I don't work with private macros often so I will appreciate any help. Thanks a lot!
That is what i got so far:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Cells.Count > 1 Then Exit Sub
OldCellValue = ActiveCell.text
old_value = OldCellValue
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Area As Range
Dim OutlApp As Object
Dim IsCreated As Boolean
Dim cell As String
Dim old_value As String
Dim new_value As String
Set Area = Range("A1:E20")
If Target.Cells.Count > 1 Then Exit Sub
If Not Intersect(Target, Area) Is Nothing Then
cell = ActiveCell.Address
new_value = ActiveCell.text
On Error Resume Next
Set OutlApp = GetObject(, "Outlook.Application")
If Err Then
Set OutlApp = CreateObject("Outlook.Application")
IsCreated = True
End If
OutlApp.Visible = True
On Error GoTo 0
With OutlApp.CreateItem(0)
.Subject = "Change in table"
.to = "someones email"
.HTMLBody = "Change in cell " & "<B>" & cell & "</B><br>" _
& "Old value: " & old_value & "New value: " & new_value
On Error Resume Next
.Send
Application.Visible = True
On Error GoTo 0
End With
If IsCreated Then OutlApp.Quit
Set OutlApp = Nothing
End With
End If
End Sub
Not re-writing all your code, but in essence you have to do this to store the value when the cell is selected and then after it is changed. You don't need the newcellvalue variable as Target captures that.
Dim OldCellValue
Private Sub Worksheet_Change(ByVal Target As Range)
Dim newcellvalue
newcellvalue = Target.Value
MsgBox "Old " & OldCellValue & ", New " & newcellvalue
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
OldCellValue = Target.Value
End Sub
I think you're on the right track. I would use a global variable to track the current/old value so you can compare it in the Worksheet_Change event.
Something like this:
Private old_value As String
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Cells.Count > 1 Then Exit Sub
old_value = Target.Text
'Debug to check the old_value
'Debug.Print "old_value = " + old_value
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Area As Range
Dim OutlApp As Object
Dim IsCreated As Boolean
Dim cell As String
Dim new_value As String
Set Area = Range("A1:E20")
If Target.Cells.Count > 1 Then Exit Sub
If Not Intersect(Target, Area) Is Nothing Then
new_value = Target.Text
'Debug to compare values
'Debug.Print "new_value = " + new_value
'Debug.Print "old_value = " + old_value
If new_value <> old_value Then
'Debug to compare
'Debug.Print "new_value and old_value are different"
End If
End If
End Sub
Keep in mind that the Worksheet_SelectionChange event is going to fire every time you navigate between cells. So, if you change the value of a cell and press the Enter key, the value of old_value is going to change because you are resetting the value in the Worksheet_SelectionChange event. You need to perform the comparison and send the email before the selection changes.
Also, you'll probably want to use .Value for the cell instead of .Text. See this post for the differences: What is the difference between .text, .value, and .value2?

VBA 2 IF conditions applied for column

this is my first post, please be patient if I'm doing/asking something wrong.
My issue is:
I got 2 columns, A is number of children, B is name of those children.
Those values are manually entered, I simply would like to have B mandatory if A is filled.
Here is what I thought:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
If Not IsEmpty(Sheet1.Range("A1")) Then
If IsEmpty(Sheet1.Range("B1")) Then
MsgBox "Please fill in cell B1 before closing."
Cancel = True
Else '
End If
End If
End Sub
This is actually working perfectly, unfortunately I can't manage to extend it for whole columns, when replacing A1 with A1:A1000 and B1 with B1:B1000 for instance,it doesn't work.
How can I validate this for both entire column A and B?
thanks in advance!
Try this
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Cancel = Evaluate("SUMPRODUCT(--(ISBLANK(Sheet1!B:B) <> ISBLANK(Sheet1!A:A)))")
If Cancel Then MsgBox "Please fill in column B before closing."
End Sub
EDIT
In order to take the user to the place where data is missing, and taking into account the additional information you provided about your data, try this:
'Private Sub Workbook_BeforeClose(Cancel As Boolean)
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Dim r: r = Evaluate( _
"MATCH(FALSE, ISBLANK('ELENCO AGGIORNATO'!V:V) = ISBLANK('ELENCO AGGIORNATO'!W:W), 0)")
If IsError(r) Then Exit Sub ' All is fine
Cancel = True
Application.Goto Sheets("ELENCO AGGIORNATO").Cells(r, "V").Resize(, 2)
msgBox "Please fill missing data before saving."
End Sub
Also note that I recommend Workbook_BeforeSave instead of Workbook_BeforeClose, because there's no harm if the user decides to drop his (incomplete) work and close the workbook without saving.
You may try something like this...
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Dim str As String
Dim Rng As Range, Cell As Range
Dim FoundBlank As Boolean
Set Rng = Sheet1.Range("A1:A1000")
str = "Please fill the cells listed below before colsing..." & vbNewLine & vbNewLine
For Each Cell In Rng
If Cell <> "" And Cell.Offset(0, 1) = "" Then
FoundBlank = True
str = str & Cell.Address(0, 1) & vbNewLine
End If
Next Cell
If FoundBlank Then
Cancel = True
MsgBox str, vbExclamation, "List of Blank Cells Found!"
End If
End Sub

WORD VBA - Userform - Auto fill

I am trying to create a user form in VBA on Microsoft word.
I have been following http://gregmaxey.com/word_tip_pages/create_employ_userform.html
to create the form.
I am very very very new to programming and have basically just been teaching myself as I go.
I get a "compile error: Sub of Function not defined" when I try and step through Call UF
I've attached the whole code for you to look at and tell me where I've gone wrong, happy for any suggestions.
Module - modMain
Option Explicit
Sub Autonew()
Create_Reset_Variables
Call UF
lbl_Exit:
Exit Sub
End Sub
Sub Create_Reset_Variables()
With ActiveDocument.Variables
.Item("varFormNumber").Value = " "
.Item("varTitle").Value = " "
.Item("varGivenName").Value = " "
.Item("varFamilyName").Value = " "
.Item("varStreet").Value = " "
.Item("varSuburb").Value = " "
.Item("varState ").Value = " "
.Item("varPostCode").Value = " "
.Item("varInterviewDate").Value = " "
End With
myUpdateFields
lbl_Exit:
Exit Sub
End Sub
Sub myUpdateFields()
Dim oStyRng As Word.Range
Dim iLink As Long
iLink = ActiveDocument.Sections(1).Headers(1).Range.StoryType
For Each oStyRng In ActiveDocument.StoryRanges
Do
oStyRng.Fields.Update
Set oStyRng = oStyRng.NextStoryRange
Loop Until oStyRng Is Nothing
Next
End Sub
Form - frmLetter13
Option Explicit
Public boolProceed As Boolean
Sub CalUF()
Dim oFrm As frmLetter13
Dim oVars As Word.Variables
Dim strTemp As String
Dim oRng As Word.Range
Dim i As Long
Dim strMultiSel As String
Set oVars = ActiveDocument.Variables
Set oFrm = New frmLetter13
With oFrm
.Show
If .boolProceed Then
oVars("varFormNumber").Value = TextBoxFormNumber
oVars("varTitle").Value = ComboBoxTitle
oVars("varGivenName").Value = TextBoxGivenName
oVars("varFamilyName").Value = TextBoxFamilyName
oVars("varStreet").Value = TextBoxStreet
oVars("varSuburb").Value = TextBoxSuburb
oVars("varState").Value = ComboBoxState
oVars("varPostCode").Value = TextBoxPostCode
oVars("varInterviewDate").Value = TextBoxInterviewDate
End If
Unload oFrm
Set oFrm = Nothing
Set oVars = Nothing
Set oRng = Nothing
lbl_Exit
Exit Sub
End Sub
Private Sub TextBoxFormNumber_Change()
End Sub
Private Sub Userform_Initialize()
With ComboBoxTitle
.AddItem "Mr"
.AddItem "Mrs"
.AddItem "Miss"
.AddItem "Ms"
End With
With ComboBoxState
.AddItem "QLD"
.AddItem "NSW"
.AddItem "ACT"
.AddItem "VIC"
.AddItem "TAS"
.AddItem "SA"
.AddItem "WA"
.AddItem "NT"
End With
lbl_Exit:
Exit Sub
End Sub
Private Sub CommandButtonCancel_Click()
Me.Hide
End Sub
Private Sub CommandButtonClear_Click()
Me.Hide
End Sub
Private Sub CommandButtonOk_Click()
Select Case ""
Case Me.TextBoxFormNumber
MsgBox "Please enter the form number."
Me.TextBoxFormNumber.SetFocus
Exit Sub
Case Me.ComboBoxTitle
MsgBox "Please enter the Applicant's title."
Me.ComboBoxTitle.SetFocus
Exit Sub
Case Me.TextBoxGivenName
MsgBox "Please enter the Applicant's given name."
Me.TextBoxGivenName.SetFocus
Exit Sub
Case Me.TextBoxFamilyName
MsgBox "Please enter the Applicant's family name."
Me.TextBoxFamilyName.SetFocus
Exit Sub
Case Me.TextBoxStreet
MsgBox "Please enter the street address."
Me.TextBoxStreet.SetFocus
Exit Sub
Case Me.TextBoxSuburb
MsgBox "Please enter the suburb."
Me.TextBoxSuburb.SetFocus
Exit Sub
Case Me.ComboBoxState
MsgBox "Please enter the state."
Me.ComboBoxState.SetFocus
Exit Sub
Case Me.TextBoxPostCode
MsgBox "Please enter the postcode."
Me.TextBoxPostCode.SetFocus
Exit Sub
Case Me.TextBoxInterviewDate
MsgBox "Please enter the interview date."
Me.TextBoxInterviewDate.SetFocus
Exit Sub
End Select
'Set value of a public variable declared at the form level.'
Me.boolProceed = True
Me.Hide
lbl_Exit:
Exit Sub
End Sub
There are a couple of issues here.
The first issue is that you do not have a routine named UF for Call UF to call.
The routine that you have named CalUF should not be in the code for the UserForm but should be in modMain and renamed CallUF.
There is no need to include an exit point in your routine as you don't have an error handler.
Your AutoNew routine could be rewritten as:
Sub Autonew()
Create_Reset_Variables
CallUF
End Sub
I have commented your sub myUpdateFields for you.
Sub myUpdateFields()
Dim oStyRng As Word.Range
Dim iLink As Long
iLink = ActiveDocument.Sections(1).Headers(1).Range.StoryType
' logically, iLink should be the StoryType of the first header in Section 1
' Why would this be needed in all StoryRanges?
' Anyway, it is never used. Why have it, then?
' This loops through all the StoryRanges
For Each oStyRng In ActiveDocument.StoryRanges
' This also loops through all the StoryRanges
Do
oStyRng.Fields.Update
Set oStyRng = oStyRng.NextStoryRange
Loop Until oStyRng Is Nothing
'And after you have looped through all the StoryRanges
' Here you go back and start all over again.
Next oStyRng End Sub
Frankly, I don't know if the Do loop does anything here. Perhaps it does. Read up about the NextStoryRange property here. I also don't know if using the same object variable in the inside loop upsets the outside loop. I don't know these things because I never needed to know them. Therefore I wonder why you need them on your second day in school.
You are setting a number of document variables. These could be linked to REF fields in your document which you wish to update. I bet your document has only one section, no footnotes and no textboxes with fields in them. Therefore I think that the following code should do all you need, if not more.
Sub myUpdateFields2()
Dim Rng As Word.Range
For Each Rng In ActiveDocument.StoryRanges
Rng.Fields.Update
Next Rng
End Sub
To you, the huge advantage of this code is that you fully understand it. Towards this end I have avoiding using a name like oStyRng (presumably meant to mean "StoryRange Object"). It is true that a Word.Range is an object. It is also true that the procedure assigns a StoryRange type of Range to this variable. But the over-riding truth is that it is a Word.Range and therefore a Range. Code will be easier to read when you call a spade a spade, and not "metal object for digging earth". My preferred variable name for a Word.Range is, therefore, "Rng". But - just saying. By all means, use names for your variables which make reading your code easy for yourself.

Passing an user form result to vba code variable

I have a code that counts the files in a folder if they contain a specific string on their name.
For example: If I want it to count the files with close on their name (Close_26_03_2003.csv).
Currently the code reads the value of a cell in the sheet and searches for that string in the file name with the (InStr function). Problem is I have to write the type of file in the cell.
What I am trying to do is create an user form, with three option buttons (open, close and cancel). For open it sets the string equal to open, and search for files that have it on their name (same as for close). Cancel ends the sub.
Problem is I don't know which code I have to use in the user form for this and don't know how to pass it to the code that counts files (I though about assigning it to a variable).
Code as is:
Sub CountFiles3()
Dim path As String, count As Integer, i As Long, var As Integer
Dim ws As Worksheet
Dim Filename As String
Dim FileTypeUserForm As UserForm1
Application.Calculation = xlCalculationManual
path = ThisWorkbook.path & "\*.*"
Filename = Dir(path)
'the problem is here:
'x = user form result***************
'if cancel = true, end sub
Set ws = ThisWorkbook.Sheets("FILES")
i = 0
Do While Filename <> ""
'var = InStr(Filename, ws.Cells(2, 7).Value) 'this is current code, it checks if the cell has open or close
var = InStr(Filename, x)
If var <> 0 Then
i = i + 1
ws.Cells(i + 1, 1) = Filename
Filename = Dir()
Else: Filename = Dir()
End If
Loop
Application.Calculation = xlCalculationAutomatic
ws.Cells(1, 2) = i
MsgBox i & " : files found in folder"
End Sub
And this is my current user form code:
Private Sub Cancel_Click()
Me.Tag = 3 ' EndProcess
Me.Hide
End Sub
Private Sub ClosingType_Click()
Me.Tag = 2 ' "CLOSING"
Me.Hide
End Sub
Private Sub OpeningType_Click()
Me.Tag = 1 ' "OPENING"
Me.Hide
End Sub
Any ideas?
add following code to your CountFiles3() sub in the "'the problem is here:" section:
Dim x As String
x = GetValue
If x = "end" Then Exit Sub
then add following code in any module:
Function GetValue()
With MyUserForm '<--| change "MyUserForm " to your actual UserForm name
.Show
GetValue = .Tag
End With
Unload MyUserForm '<--| change "MyUserForm " to your actual UserForm name
End Function
and change your Userform code as follwos
Private Sub Cancel_Click()
Me.Tag = "end" ' EndProcess
Me.Hide
End Sub
Private Sub ClosingType_Click()
Me.Tag = "close" ' "CLOSING"
Me.Hide
End Sub
Private Sub OpeningType_Click()
Me.Tag = "open" ' "OPENING"
Me.Hide
End Sub

Setting validation for combo box error

#
updated codes
Function condition(ByRef objCmb As ComboBox)
If objCmb.Value ="" And objCmb.Value = "g" Then
Call MsgBox("gg", vbOKOnly, "error")
End If
End Function
Private Sub ComboBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
condition (ComboBox1)
End Sub
'other codes for reference:
Private Sub CommandButton1_Click()
Dim lastrow As Integer
lastrow = Cells(Rows.Count, "A").End(xlUp).Row
For i = 1 To 3
For j = 1 To 5
With Me.Controls("ComboBox" & (i - 1) * 5 + j)
If .Text <> "" Then
Cells(lastrow + i, j) = .Text
Else
Exit Sub
End If
End With
Next j
Next i
End Sub
I have 50 combo and text boxes in VBA user panel. As it is too troublesome to set constraints in every combo or text box, I want a function to apply to every combo and text box.
For the codes above , it shows up cant find objecterror
How to solve ?
Btw , how to set the function statement for textbox ?
is it Function condition2(ByRef objCmb As textbox)...
You are receiving an error because ComboBox is not ByRef objCmb As ComboBox. Don't use parenthesis when calling a sub. Don't use parenthesis when calling function if you are not using the functions return value. If a function does not return a value it should be a sub.
Sub condition(ByRef objCmb As MSForms.ComboBox)
If objCmb.Value <> "" And objCmb.Value = "g" Then
MsgBox "gg", vbOKOnly, "error"
objCmb.Value = ""
End If
End Sub
Private Sub ComboBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
condition ComboBox1
End Sub
I wrote a function to help you generate the Exit event code for all your text and combo boxes.
Sub AddCodeToCipBoard(frm As UserForm)
Const BaseCode = " Private Sub #Ctrl_Exit(ByVal Cancel As MSForms.ReturnBoolean)" & vbCrLf & _
" condition ComboBox1" & vbCrLf & _
" End Sub" & vbCrLf & vbCrLf
Dim s As String
Dim ctrl
Dim clip As DataObject
Set clip = New DataObject
For Each ctrl In frm.Controls
If TypeName(ctrl) = "ComboBox" Or TypeName(ctrl) = "TextBox" Then
s = s & Replace(BaseCode, "#Ctrl", ctrl.Name)
End If
Next
clip.SetText s
clip.PutInClipboard
End Sub
Put this code in a module and call it like this:
AddCodeToCipBoard Userform1
Now all the Exit event code will be copied into the Windows Clipboard. Go into your Userforms code module and paste the new code.
Example Output:
Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
condition ComboBox1
End Sub
Private Sub TextBox2_Exit(ByVal Cancel As MSForms.ReturnBoolean)
condition ComboBox1
End Sub
Private Sub TextBox3_Exit(ByVal Cancel As MSForms.ReturnBoolean)
condition ComboBox1
End Sub
Private Sub ComboBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
condition ComboBox1
End Sub
Private Sub TextBox4_Exit(ByVal Cancel As MSForms.ReturnBoolean)
condition ComboBox1
End Sub