How to develop a class module for a keyboard shortcut - vba

I have a custom button on a ribbon, and I want to create a keyboard shortcut using a class module. I have managed to start it, but I am not able to take the next step, which would be to perform the action. I give you the code:
`
Option Compare Database
Option Explicit
Private WithEvents Form As Access.Form
Private mPresionarTecla As Boolean
Public Property Get PresionarTecla() As Boolean
PresionarTecla = mPresionarTecla
End Property
Public Property Let PresionarTecla(ByVal vNewValue As Boolean)
mPresionarTecla = vNewValue
End Property
Public Sub InitalizeAutokeys(FName As Form)
Set Form = FName
mPresionarTecla = True
If mPresionarTecla = True Then
FName.OnKeyDown = "[Event Procedure]"
End If
End Sub
Private Sub frmCustomForm_KeyDown(KeyCode As Integer, Shift As Integer)
If mPresionarTecla = True Then
rbPegarSinFormato (Screen.ActiveControl)
End If
End Sub
`
I want to create a keyboard shortcut to execute a button on a ribbon. The ribbon button is a procedure created by me, which applies only to formatted text fields, to paste without formatting.
So that you understand me, in the toolbar you have a button, which can be executed by means of the combination of letters control + v. I want to achieve something similar.
Now if I do this through a macro, it's very limited because I can only do a few things, and with VBA I can do a lot more. That is, if I want to add conditions or, most importantly, execute a procedure, not a function, a macro is not worth it. Also, I can select which fields I want to apply it to, that is, since the function I'm using is to copy without formatting, it doesn't make sense for numeric fields to be applied

Related

How do I prevent control passing to the next Userform textbox?

I have a Userform with multiple Textboxes. All the Textboxes are in tab-order.
Each subsequent Textbox is back colored yellow to indicate to the user which is the next Textbox to complete.
If the Textbox Value is determined to be invalid I want the control to SetFocus back on that particular Textbox. However, control is automatically handed over to the next Textbox in the tab-order.
When I try to focus back on the required Textbox with the mouse, this fires an event on the next Textbox, which follows the rules of my program and requests the user to enter a valid value.
Below is a sample of two Textboxes, if the user fails to enter a first name I want the control to return to the tbxCustomerFirstName Textbox, however, the control is handed over to the tbxCustomerSurName Textbox, even though I've "tbxCustomerFirstName.SetFocus".
AstFlag = 2 means there has to be a valid value in the Textbox.
AstFlag = 1 means the Textbox value can be blank.
I stepped through the program and AstFlag does indeed = 2, and the set focus code is executed.
'====================================================================================
'
' Customer First Name
'
Private Sub tbxCustomerFirstName_Exit(ByVal Cancel As MSForms.ReturnBoolean)
Call tbxValues(3)
If tbxCancel = True Then
Cancel = True
End If
If AstFlag = 2 Then
tbxCustomerFirstName.SetFocus
End If
End Sub
'====================================================================================
'
' Customer Surname
'
Private Sub tbxCustomerSurName_Exit(ByVal Cancel As MSForms.ReturnBoolean)
Call tbxValues(4)
If tbxCancel = True Then
Cancel = True
End If
If AstFlag = 2 Then
tbxCustomerSurName.SetFocus
End If
End Sub
You want to set the Cancel parameter to True when you mean to cancel exiting the control.
I don't know what your tbxCancel flag logic is, but...
If AstFlag = 2 Then
theControlYouAreExiting.SetFocus
End If
Should be
If AstFlag = 2 Then
Cancel.Value = True
End If
That will prevent exiting the control, so there's no need to SetFocus on anything. Now again I don't know what tbxCancel is supposed to be doing, but it doesn't look right.
In fact I would scrap all this painfully duplicated conditional logic, and implement some Validate method that returns True if the field is valid (and thus can be exited) and False if the field is invalid (and thus Cancel.Value must be True and focus remains on the control).
Cancel.Value = Not Validate(args)
Where args could be theControlYouAreExiting.Tag, and then you could use the controls' Tag property to hold the metadata you're likely currently hard-coding in tbxValues.
Ultimately the problem you're solving is going to create a mess no matter how you put it, because everything is happening in the form's code-behind: you want to separate the View (the form/UI) from the Model (the data it's manipulating).
The solution is to create a class module that represents your model. This model exposes a property that each control on your form manipulates, and then the model itself knows how to validate each property.
Option Explicit
Private model As CustomerModel
Private Sub UserForm_Initialize()
Set model = New CustomerModel
End Sub
Public Property Get CustomerModel() As CustomerModel
Set CustomerModel = model
End Property
Public Property Set CustomerModel(ByVal value As CustomerModel)
Set model = value
End Property
Private Sub tbxCustomerSurName_Change()
model.Surname = tbxCustomerSurName.Text
End Sub
Private Sub tbxCustomerSurName_Exit(ByVal Cancel As MSForms.ReturnBoolean)
Cancel.Value = Not model.IsValidSurname
End Sub
And with that you don't need to maintain flags and state switches to keep track of what the metadata is for the control you're in.

access vba click buttons refresh form but trigger click event do not refresh the form

I have 2 buttons to filter / unfilter a subform. Event codes (private) are calling a public function:
I can click buttons to filter / unfilter, it is working well.
Option Compare Database
Option Explicit
Dim PartCalendarSubHeaderFilterOn As Boolean
Private Sub imgClearFilter_Click()
PartCalendarSubHeaderFilterOn = False
UpdatePartCalendarSubHeaderFilter
End Sub
Private Sub imgFavoriteFilter_Click()
PartCalendarSubHeaderFilterOn = True
UpdatePartCalendarSubHeaderFilter
End Sub
Public Sub UpdatePartCalendarSubHeaderFilter()
If PartCalendarSubHeaderFilterOn Then
PartCalendarSubHeaderFilter
Else
PartCalendarSubHeaderClearFilter
End If
Me.Requery
End Sub
Private Sub PartCalendarSubHeaderClearFilter()
Dim sql As String
sql = "... "
Me!frmPartCalendarSubHeaders.Form.RecordSource = sql
End Sub
Private Sub PartCalendarSubHeaderFilter()
Dim sql As String
sql = "... "
Me!frmPartCalendarSubHeaders.Form.RecordSource = sql
End Sub
From another form current event, I want to refresh the part calendar by calling the update filter sub by calling the public method:
Private Sub Form_Current()
...
'update part calendar
Form_frmPartCalendarSub.UpdatePartCalendarSubHeaderFilter
Me.Parent!frmPartCalendarSub.Requery
End Sub
When I follow line by line in debug mode, I can see it goes through the exact same code as when I click on filter / unfilter buttons, goes through the lines
Me!frmPartCalendarSubHeaders.Form.RecordSource = sql
and
Me.Requery
but the subform is not refreshing as expected.
Interresting thing: in debug mode, if I reset once just after entering code line
Public Sub UpdatePartCalendarSubHeaderFilter()
Then it works correctly: I can filter using buttons, and when I select another record it refreshes correctly, according to the filter selected.
Have you ever encountered this king of problem? What should I correct to make the code work from start, without having to break from debug mode ?
Try to replace
Form_frmPartCalendarSub.UpdatePartCalendarSubHeaderFilter
by
Forms("frmPartCalendarSub").UpdatePartCalendarSubHeaderFilter
First command doesn't guarantee that code executed in correct instance of form. It would be better to avoid using direct calls of Form's class methods, it may lead to unexpected results.

Treat Userform as a Function

I have created a VBA UserForm which is essentially a glorified input box
Just like an input box can be employed like this
Dim returnVal As String
returnVal = InputBox("Write some string")
I would like my userform to run like this
Dim returnVal As customClass
Set returnVal = MyUserForm([some arguments])
ie. the MyUserForm() code passes some arguments to the userform, and when the userform is closed, it gets some arguments back (in the form of a custom class rather than a plain string)
What's the best way of structuring my userform to allow this functionality?
Currently, I'm just declaring some variables and the custom class publicly. I'm catching command button clicks and Query_close() events to hide the form, then I read the outputVal and close the form completely. I don't like this because I'd like my form to be totally self contained, and I think the capturing of events is messy.
In simplified code (read/return a string):
Function myUf(inVal As String) As String
Dim frm As New frmTest
frm.inputval = inVal
frm.Init 'sets caption. We cannot rely on userform initialize as this runs before inputval is set
'We could pass a variable here to save writing to the public variable
frm.Show
myUf = frm.outputVal
Set frm = Nothing
End Function
And in my Userform called frmTest with a textbox called tb1
Public inputval As String
Public outputVal As String
Public Sub Init()
Me.Caption = inputval 'setting caption, but could pass this anywhere
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode <> 1 Then Cancel = 1
outputVal = tb1 'reading value from textbox, but could return anything here
Me.Hide
End Sub
You need to find a way to initiate the UserForm from a ClassObject. Then, you can use a simple factory pattern to create the UserForm exactly the way you want.
In general, I have copied a bit of the code of Mat's Mug somewhere in StackOverflow and I wrote an article about the User Forms. If you take a look here (http://www.vitoshacademy.com/vba-the-perfect-userform-in-vba/) you will find a way to initialize the form with Public Sub ShowMainForm() It's possible to add a parameter to the ShowMainForm, then pass it to the initializer of the class.
In general, take the code from the article, make sure it works, and change the ShowMainForm initializer to the following:
Public Sub ShowMainForm(strText As String, strText2 As String)
If (objPresenter Is Nothing) Then
Set objPresenter = New clsSummaryPresenter
End If
objPresenter.Show
Call objPresenter.ChangeLabelAndCaption(strText, strText2)
End Sub
Then, if you call like this in the immediate window:
call ShowMainForm("Just","testing")
You will get this:
Which is quite what you need. :)
The basic idea is:
Create a Function in witch you combine your arguments to a string like:
strOpenArg = "param1:=value1;param2:=value2;"
than open the form with the OpenArgs
DoCmd.OpenForm "UserForm", acNormal, , , , acDialog, strOpenArgs
get your value and close the Form
Value= Form_UserForm.Value
DoCmd.Close acForm, "UserForm", acSaveNo
in the UserForm set Form_open. Here you can get your parameters.you can devide this by string splitting.
Set also an OK Button, where you make the form just invisible and set the return value
Private Sub Form_Open(Cancel As Integer)
Dim strParameter as String
strParameter = Me.OpenArgs 'Here are your parmeters
End Sub
Private Sub ok_Click()
m_Value = "Your ReturnValue"
Me.Visible = False
End Sub
Private m_Value As String
Public Property Get Value() As String
Value = m_msgBoxResult
End Property
There's no way to one-liner the code like you want to, unfortunately. If all your userform code is self-contained then the only way for it to pass values out is to change the values of public variables. Mat's Mug's answer here is the layout I usually use when trying to simulate functions like 'InputBox' but you still can't get it in one line without writing a separate function. Using userform properties allows you to contain more of your code within the form itself.

Disable a button via a function?

Currently using vba with excel 2007...
I am currently testing the capabilities of functions and am a little stuck using buttons. I have two buttons, named ONE and TWO. Pressing either calls up a function Calc with each button passing the variable of the others name. As follows:
Private Sub ONE_Click()
Calc TWO
End Sub
Private Sub TWO_Click()
Calc ONE
End Sub
Function Calc(B As CommandButton)
B.Enabled = False
End Function
My understanding is that pressing button ONE passes the variable TWO to the Function Calc, and then disables button TWO.
I also have a button labeled Reset which serves as follows:
Private Sub Reset_Click()
ONE.Enabled = True
TWO.Enabled = True
End Sub
The results of this, if I press button ONE, button TWO greys out and appears "disabled". However when I hit the reset button, it stays grey. Investigating the properties of the button reveals that it doesn't actually get disabled. I installed another button that directly disables button two, i.e. TWO.Enabled = False, instead of using a variable to do so.
When the button is directly disabled using the direct button, resetting enables the button as it should, and the properties reflect that the button is disabled.
Would anyone know why using a variable to disable a button like this results in an illusionary disabling?
And better yet, how to overcome the issue?
If you have trouble calling through an indirrect reference, then use a static parameter.
Private Sub ONE_Click()
Calc "TWO"
End Sub
Private Sub TWO_Click()
Calc "ONE"
End Sub
Now you can use that parameter to act on the object.
Function Calc(B As String)
If B = "ONE" Then
ONE.Enabled = False
ElseIf B = "TWO" Then
TWO.Enabled = False
Else
Exit Function
End If
End Function

Having an MS Office UserForm detect which subroutine called it

In a VBA project of mine I am/will be using a series of reasonably complex userforms, many of which are visually identical but have different subroutines attached to the buttons. As a result I'm not overly keen on the idea of duplicating them multiple times in order to get different functionality out of the same layout. Is it possible to have a userform detect which subroutine called it and use this in flow control? I would like to be able to do something like this:
Private Sub UserForm_Initialize()
If [the sub that called the userform is called "foo"] then
Call fooSub
else
Call barSub
End If
End Sub
My backup plan is to have the calling subroutine set a global variable flag and have the userform check that, but that seems like a rather crude and clumsy solution.
Thanks everyone,
Louis
You can use the tag property of the form. Load the form, set the property, then show the form:
Sub PassCallerToForm()
Load UserForm1
UserForm1.Tag = "foo"
UserForm1.Show
End Sub
Now that the property is set, you can determine what to do in the form:
Private Sub UserForm_Activate()
If Me.Tag = "foo" Then
Call fooSub
Else
Call barSub
End If
End Sub
You can also use public variables:
' in userform
Public Caller As String
Private Sub UserForm_Click()
MsgBox Caller
Caller = Now()
Me.Hide
End Sub
' in caller
Sub callUF()
Dim frm As New UserForm1
frm.Caller = "Test Caller"
frm.Show
MsgBox frm.Caller ' valid after Me.Hide
Set frm = Nothing
End Sub
Personally, I would not have one userform doing two disparate activities. The code would get hard to read pretty quickly, I think. Copying the layout of a userform is pretty trivial.
To copy a userform: Open a blank workbook. In the Project Explorer, drag the userform to the new workbook. Rename the userform in the new workbook. Now drag it back to the original workbook. Change the code in the userform copy.
If you absolutely don't want separate userforms, I recommend setting up a property of the userform. Userforms are just classes except they have a user interface component. In the userform module
Private mbIsFoo As Boolean
Public Property Let IsFoo(ByVal bIsFoo As Boolean): mbIsFoo = bIsFoo: End Property
Public Property Get IsFoo() As Boolean: IsFoo = mbIsFoo: End Property
Public Sub Initialize()
If Me.IsFoo Then
FooSub
Else
BarSub
End If
End Sub
I always write my own Initialize procedure. In a standard module:
Sub OpenForm()
Dim ufFooBar As UFooBar
Set ufFooBar = New UFooBar
ufFooBar.IsFoo = True
ufFooBar.Initialize
ufFooBar.Show
End Sub