How to stop a private sub from running with a password - vba

I have two private subs for active x check boxes. When I click one button another button is locked. I want to create a button that would act as an override, where if I type a password then the two private subs won't run.
Private Sub CRM_box_Click()
If CRM_box.Value = True Then
CheckBox14.Value = False
CheckBox14.Enabled = False
Else
CheckBox14.Value = False
CheckBox14.Enabled = True
End If
Private Sub RMP_box_Click()
If RMP_box.Value = True Then
CRM_box.Value = False
CRM_box.Enabled = False
Else
CRM_box.Value = False
CRM_box.Enabled = True
End If

To 'override' the Private Sub for your check boxes, you could do the following:
In your VBE, create a Module (if you don't already have one) and enter Public override As Boolean on the first line.
For each of your Private Subs for the Check Boxes, add the following above your current If statement: If override = False Then Exit Sub like so:
Private Sub CRM_box_Click()
If override = False Then Exit Sub
If CRM_box.Value = True Then
RMP_box.Value = False
RMP_box.Enabled = False
Else
RMP_box.Value = False
RMP_box.Enabled = True
End If
End Sub
For the password part of your question you could create a UserForm.
If you're not firmiliar with UserForms, there are a lot of tutorials and instructions on how to create them by searching something like "How to create a userform in excel" in Google or equivalent.
I made a simple UserForm with a label, textbox and commandbutton. See here.
For the commandbutton, you could use the following code:
Private Sub CommandButton1_Click()
Dim pwrd As String
Dim setPwrd As String
pwrd = UserForm1.TextBox1.Text
setPwrd = "abc" 'Change this string to whatever you want your password to be
If pwrd = setPwrd Then
override = False
Unload UserForm1
Else
MsgBox "Incorrect Password" & vbNewLine & "Please try again.", vbCritical
End If
End Sub
When you enter your password into the textbox and click the commandbutton, the vba compares your input with the variable setPassword. If it's a match it set's the Public variable override to False. If the password doesn't match you get a message to try again.
If override = False it directs your Private Sub for your check boxes to Exit Sub before executing the code - Your code is 'overridden'.
In summary:
Declare a Public Variable as a Boolean.
Set an If statement in each Private Sub for your check boxes.
Create a UserForm to enter a Password.
Write the code to set a password and for what happens when you submit your password.
You may find this answer helpful in understanding ways you can prevent other users from getting around your password form.

Related

How can I fix a run-time error when capturing text from a textbox in a form?

I am trying to capture the value entered into the text box of a form instance which was created using the following module code:
Public myForm As Form_Form1
'Dim myForm As Form_Form1 ' tried this
Sub test()
'Dim myForm As New Form_Form1 ' tried this
Set myForm = New Form_Form1
With myForm
.Visible = True
' .SetFocus ' tried this
' .Modal = True ' tried this
If .IsCancelled Then
Exit Sub
End If
Debug.Print .RptDt
End With
The form is very basic with an OK and Cancel button and a single text box named Text7. The form code-behind is:
Private cancelling As Boolean
Public Property Get RptDt() As String
RptDt = Text7.Text
End Property
Public Property Get IsCancelled() As Boolean
IsCancelled = cancelling
End Property
Private Sub Command2_Click()
'DoCmd.Close acForm, Me.Name
Me.Visible = False
'Me.Visible
End Sub
Private Sub Command4_Click()
cancelling = True
'DoCmd.Close acForm, Me.Name
'MsgBox Me.Name
'MsgBox Me.OpenArgs
'Me.Hide
Me.Visible False
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = VbQueryClose.vbFormControlMenu Then
cancelling = True
Me.Visible = False
End If
End Sub
When I run the code as is I get:
"Run-time error '2185' You can't reference a property or method for a control unless the control has the focus"
I have also tried:
Public Property Get RptDt() As String
RptDt = Text7.Value
End Property
I then get Run-time error '94' Invalid use of null. The code above was modified from a comparable Excel VBA code which uses the Userform Show method (only works in Excel) in the Sub Test() instead of .Visible = True.
Just a side point, but on the Command4_Click event change the below line to add an equals:
Me.Visible = False
This can change the syntax and is a different function to the one intended.
The .Text property can only be called when a Control has the focus. Similarly, a null exception will be called if you use .Value in it's place when the textbox is empty.
There are two ways around this:
Option 1 - Handling the NULL value
Public Property Get RptDt() As String
If IsNull(Text7.Text) = True Then
RptDt = "EmptyString" 'Or whatever string you want to set this to
Else
RptDt = Text7.Text
End if
End Property
Option 2 - Setting the focus
Public Property Get RptDt() As String
Text7.SetFocus
RptDt = Text7.Text
End Property

How to check if a userform is closed with "X" Windows button?

There is a sub, it creates a CourtForm userform and then takes a data from it. The problem appears when said form is closed prematurely, by pressing "X" window button and I get a runtime error somewhere later. For reference, this is what I'm talking about:
In my code I tried to make a check to exit sub:
Private Sub test()
'Create an exemplar of a form
Dim CourtForm As New FormSelectCourt
CourtForm.Show
'The form is terminated at this point
'Checking if the form is terminated. The check always fails. Form exists but without any data.
If CourtForm Is Nothing Then
Exit Sub
End If
'This code executes when the form proceeds as usual, recieves
'.CourtName and .CourtType variable data and then .hide itself.
CourtName = CourtForm.CourtName
CourtType = CourtForm.CourtType
Unload CourtForm
'Rest of the code, with the form closed a runtime error occurs here
End Sub
Apparently the exemplar of the form exists, but without any data. Here's a screenshot of the watch:
How do I make a proper check for the form if it's closed prematurely?
Add the following code to your userform
Private m_Cancelled As Boolean
' Returns the cancelled value to the calling procedure
Public Property Get Cancelled() As Boolean
Cancelled = m_Cancelled
End Property
Private Sub UserForm_QueryClose(Cancel As Integer _
, CloseMode As Integer)
' Prevent the form being unloaded
If CloseMode = vbFormControlMenu Then Cancel = True
' Hide the Userform and set cancelled to true
Hide
m_Cancelled = True
End Sub
Code taken from here. I would really recommend to have a read there as you will find a pretty good basic explanation how to use a userform.
One of the possible solutions is to pass a dictionary to the user form, and store all entered data into it. Here is the example:
User form module code:
' Add reference to Microsoft Scripting Runtime
' Assumed the userform with 2 listbox and button
Option Explicit
Public data As New Dictionary
Private Sub UserForm_Initialize()
Me.ListBox1.List = Array("item1", "item2", "item3")
Me.ListBox2.List = Array("item1", "item2", "item3")
End Sub
Private Sub CommandButton1_Click()
data("quit") = False
data("courtName") = Me.ListBox1.Value
data("courtType") = Me.ListBox2.Value
Unload Me
End Sub
Standard module code:
Option Explicit
Sub test()
Dim data As New Dictionary
data("quit") = True
Load UserForm1
Set UserForm1.data = data
UserForm1.Show
If data("quit") Then
MsgBox "Ввод данных отменен пользователем"
Exit Sub
End If
MsgBox data("courtName")
MsgBox data("courtType")
End Sub
Note the user form in that case can be closed (i. e. unloaded) right after all data is filled in and action button is clicked by user.
Another way is to check if the user form actually loaded:
Sub test()
UserForm1.Show
If Not isUserFormLoaded("UserForm1") Then
MsgBox "Ввод данных отменен пользователем"
Exit Sub
End If
End Sub
Function isUserFormLoaded(userFormName As String) As Boolean
Dim uf As Object
For Each uf In UserForms
If LCase(uf.Name) = LCase(userFormName) Then
isUserFormLoaded = True
Exit Function
End If
Next
End Function

VBA multiple option dialog box output

I created a user form with multiple options and now I want that the option the user selects is shown in a label under the button that calls the user form.I changed the caption in the text box under the button to resemble what should happen
However my options aren't working. Should I save the output in a global variable and then call it back to change the label and if so how do I do that? Or is it possible to just call the selection within the user form?
The code I was trying to run was this one to call the message box and then change the text box which is actually a label called "labelpage"
Private Sub CommandButton1_Click()
UserForm1.Show
If UserForm1.OptionButton1 = True Then LabelPage.Caption = "Company Restricted"
If UserForm1.OptionButton2 = True Then LabelPage.Caption = "Strictly Confidential"
If UserForm1.OptionButton2 = True Then LabelPage.Caption = "Public Information (does not need to be marked)"
End Sub
I also had this for each button click just to close them after selection, within the user form code.
Private Sub OptionButton1_Click()
OptionButton1.Value = True
Unload Me
End Sub
Private Sub OptionButton2_Click()
OptionButton2.Value = True
Unload Me
End Sub
Private Sub OptionButton3_Click()
OptionButton3.Value = True
Unload Me
End Sub
Is there just a tiny mistake of syntax or something like that or is this just completely wrong? Thank you in advance for your help.
The issue is that you are unloading the UserForm, meaning the controls are not available to you. The solution is to just hide the UserForm:
Private Sub OptionButton1_Click()
Hide
End Sub
Private Sub OptionButton2_Click()
Hide
End Sub
Private Sub OptionButton3_Click()
Hide
End Sub

How can I run a macro as a workbook opens for the first time only?

I've got a workbook which runs a macro to show the userform Open1 as it opens, using the (very basic) code:
Private Sub Workbook_Open()
Open1.Show
End Sub
This does its job fine - each time I open the workbook, the userform pops up and runs perfectly.
But, I want the userform to appear the first time the workbook is opened only. Is there a way to allow this to happen?
You could use a dummy module which gets deleted the first time you open the spreadsheet...
Something like:
If ModuleExists("DummyModule") Then
Open1.Show
DoCmd.DeleteObject acModule, "DummyModule"
End If
Function ModuleExists(strModuleName As String) As Boolean
Dim mdl As Object
For Each mdl In CurrentProject.AllModules
If mdl.Name = strModuleName Then
ModuleExists = True
Exit For
End If
Next
End Function
Update: as stated, DoCmd isn't used in excel vba. That will teach me to write code without testing it!
The following updated code will work, but in order to access the VB environment, excel needs to be trusted.
There is a setting in the Trust Center>Macro Settings that you can tick for this code to work under Developer Macro Settings
As such, this may not be the way to go as it opens up the possibility of security issues...
Sub RemoveModule()
If ModuleExists("DummyModule") Then
Open1.Show
Dim vbCom As Object: Set vbCom = Application.VBE.ActiveVBProject.VBComponents
vbCom.Remove VBComponent:=vbCom.Item("DummyModule")
End If
End Sub
Function ModuleExists(strModuleName As String) As Boolean
Dim mdl As Object
For Each mdl In Application.VBE.ActiveVBProject.VBComponents
If mdl.Name = strModuleName Then
ModuleExists = True
Exit For
End If
Next
End Function
Try this:
If Sheets("Hide").Cells(1,1) = "1" Then
Open1.Show
Sheets("Hide").Cells(1,1) = "0"
End if
You must create the sheet Hide, and give the cell A1 the value 1, in that case the form will be shown.
After you create the sheet, hide it with this
Sheets("Hide").Visible = xlVeryHidden
And show it with this
Sheets("Hide").Visible = True
Here's an alternative bit of code that will persist between saves and allow you to reset it. No need to create a hidden sheet.
Put this in a module (invoke the DisplayFormIfFirstTime from your Workbook_Open event handler....)
Option Explicit
Private Const cMoniker As String = "FormHasBeenDisplayed"
Private Sub DisplayFormIfFirstTime()
If HasBeenOpened = False Then DisplayForm
End Sub
Public Sub DisplayForm()
MsgBox "Ok, its not a form but a dialog box...", vbInformation
End Sub
Public Function HasBeenOpened() As Boolean
Dim oName As Name
On Error Resume Next
Set oName = Application.Names(cMoniker)
On Error GoTo 0
If Not oName Is Nothing Then
HasBeenOpened = True
Else
Call Application.Names.Add(cMoniker, True, False)
End If
End Function
'Call this to remove the flag...
Public Sub ResetOpenOnce()
On Error Resume Next
Application.Names(cMoniker).Delete
End Sub
Based on the idea supplied by PaulG, I have coded an upgrade that will check for the name and if not found run a function, add the name and save the workbook for a more seemless approach to this problem...
Placed in ThisWorkbook
Private Sub Workbook_Open()
Run "RunOnce"
End Sub
Placed in a module
Sub RunOnce()
Dim Flag As Boolean: Flag = False
For Each Item In Application.Names
If Item.Name = "FunctionHasRun" Then Flag = True
Next
If Flag = False Then
Call Application.Names.Add("FunctionHasRun", True, False)
Application.DisplayAlerts = False
ActiveWorkbook.Save
Application.DisplayAlerts = True
Call RunOnceFunction
End If
End Sub
Private Function RunOnceFunction()
Open1.Show
End Function
Sub ResetRunOnce()
For Each Item In Application.Names
If Item.Name = "FunctionHasRun" Then
Application.Names.Item("FunctionHasRun").Delete
Application.DisplayAlerts = False
ActiveWorkbook.Save
Application.DisplayAlerts = True
End If
Next
End Sub

Detect which save function the user chooses in Office2007 using Word VBA

I'm writing a custom Word template which does some processing upon saving the current document.
In Office2007, to save a document you can use the Save and the Save As functions, and handle the events with the FileSave and FileSaveAs macros.
But when the user hovers over the SaveAs option, other sub-options are displayed: Save as a document, as a Word template, as Word 97-2003 document, etc. These sub-options don't seem to have their own events, but I'd like to know when the user uses them.
So I came up with the idea to use the DocumentBeforeSave event, but then I still have to figure out if the save occured with the standard Save/SaveAs options or with the sub-options.
I thought about setting a variable to True in the Save/SaveAs functions, which the DocumentBeforeSave event would check to see if one of the normal save methods occured, then it would set the variable back to False.
But after experimenting with different methods, I can't figure out how I can pass the value of a variable between ThisDocument and the Class Module which has the BeforeSave event.
Any ideas? Thanks!
Edit: Example code that doesn't work:
ThisDocument:
Public pSSave As Boolean
Public Property Get SSave() As Boolean
SSave = pSSave
End Property
Public Property Let SSave(Value As Boolean)
pSSave = Value
End Property
Sub FileSave()
Me.SSave = True
If SSave = True Then
MsgBox "True"
End If
Application.ActiveDocument.Save
If SSave = True Then
MsgBox "True"
End If
End Sub
Class Module:
Private Sub App_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean)
If Application.ActiveDocument.SSave = False Then
MsgBox "False"
End If
End Sub
The class module registration is done properly, I won't paste the code her.
The result displayed is True, False, True while theoretically, it should be True, True.
I still miss something in your logic. In comments I thought about different-reverse logic which would go this way. This code below is a mix of my way and code you presented.
Class Module
Public WithEvents App As Word.Application
Public pSSave As Boolean 'your class variable/property
Private Sub App_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean)
If pSSave = False Then
MsgBox pSSave
Else
MsgBox pSSave
End If
End Sub
Module1
'class initialization
Public wrdAPP As New myClass
Sub set_References()
Set wrdAPP.App = Application
End Sub
ThisDocument Module
Private Sub Document_Open()
'to initialize public variable when open
Call Module1.set_References
End Sub
Sub FileSave()
wrdAPP.pSSave = True
Application.ActiveDocument.Save
If wrdAPP.pSSave = True Then
MsgBox "True"
End If
End Sub
I don't know which way you are going to run FileSave sub. But after it is run it pass the value to class property which you could check in your event.
Hope it would help you anyway.