The code in my question in inspired by the solution in the answer provided by this question:
How to add a menu item to the default right click context menu
I have a ListBox object on a form showing a list of Actions. I want the user to be able to right click an item of this list to show a contextual menu where he can either :
open a new form where he can view and edit the action (corresponds to the execution of a double click event on the list item)
delete the item from the list
Private Sub List_actions_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single
'set up commandBar
Dim combo As CommandBarControl
'Since it may have been defined in the past, it should be deleted,
'or if it has not been defined in the past, the error should be ignored
On Error Resume Next
CommandBars("RCActionContextMenu").Delete
On Error GoTo 0
'Make this menu a popup menu
With CommandBars.Add(Name:="RCActionContextMenu", Position:=msoBarPopup)
Set combo = .Controls.Add(Type:=msoControlButton)
combo.BeginGroup = True
combo.Caption = "View action" ' Add label the user will see
combo.OnAction = "List_actions_DblClick" 'Add the name of a function to call
Set combo = .Controls.Add(Type:=msoControlButton)
combo.Caption = "Delete action"
combo.OnAction = DelAction()
End With
If Button = acRightButton Then
DoCmd.CancelEvent
CommandBars("RCActionContextMenu").ShowPopup
End If
End Sub
Public Function DelAction()
If Not IsNull(Me.Controls("RCActionContextMenu").Column(0)) Then
CurrentDb.Execute "DELETE * FROM T_ACTIONS " & _
"WHERE ID = " & List_actions.Column(9) & ";"
MsgBox "Action supprimée", vbInformation, "Information"
End If
End Function
Private Sub List_actions_DblClick(Cancel As Integer)
Dim vStatus As String
'Get the record's index of the action
rowNumber = Me.List_actions.ListIndex + 1
id_action = List_actions.Column(9, rowNumber)
vStatus = List_actions.Column(5, rowNumber)
'Open the action
DoCmd.OpenForm "F_ACTIONS", , , "[ID] = " & List_actions.Column(9)
Form_F_ACTIONS.Effective_date.Visible = Effdatefunction(vStatus)
End Sub
The problem i get is that the DelAction() function is executed before the pop-up is shown and i get a run-time error 2465 stating "Microsoft Access can't find the field 'RCActionContextMenu' referred to in your expression."
I've tried repalcing the row combo.OnAction = DelAction() by combo.OnAction = "DelAction". It results in the conextual menu showing itself but nothing happens when i click on either button.
There are a few problems here.
combo.OnAction = DelAction()
This will call the function, as you have seen. You need to set a string here.
combo.OnAction = "DelAction()"
This still won't work, since DelAction() is in your form module.
Either move the function to a public module, with parameters, or hardcoding the object names there,
combo.OnAction = "DelAction(""MyFormName"", ""List_actions"")"
or try (not sure if this works):
combo.OnAction = "Form_YourFormName_DelAction()"
It's the same with "List_actions_DblClick" - the function needs to be called "from the outside", like from the Immediate window.
If Not IsNull(Me.Controls("RCActionContextMenu").Column(0)) Then
You context menu command bar isn't a control, what you want is the list box:
If Not IsNull(Me.Controls("List_actions").Column(0)) Then
or simply
If Not IsNull(Me!List_actions.Column(0)) Then
After deleting an action, you need to requery the listbox.
CurrentDb.Execute "DELETE * FROM T_ACTIONS " ...
Me!List_actions.Requery
Related
I'm trying to do what, I think, ought to be the simplest of things, but I can't get it to work. i have an MS Word document with a number of legacy drop-down and text fields:
The first option in each drop-down is "Select ...", and if a user tabs out of one of the drop-downs without choosing something other than the first "Select ...", I want a msgbox to appear to tell them to make a selection, which works. What doesn't work, is that after the user dismisses the msgbox, I want the insertion point to return to the drop-down that they didn't select.
I understand that VBA has "timing issues", and from what I've read, one way to address these timing issues is to call the "return" macro from the validation macro. So I've written two macros:
Sub Validate()
' Dim strBookmark As String
' strBookmark = Selection.Bookmarks(1).Name
10: If (bool_debug) Then MsgBox ("NotSelected() - 10: strBookmark = " & strBookmark)
With ActiveDocument
If (strBookmark = "Locality") Then
Call Salary_Step
ElseIf (strBookmark = "Series") Then
20: If (bool_debug) Then MsgBox ("NotSelected() - 20: .FormFields(strBookmark).Name = " _
& .FormFields(strBookmark).Name)
If ((Len(.FormFields(strBookmark).Result) <> 4) Or (Not IsNumeric(.FormFields(strBookmark).Result))) Then _
MsgBox ("Please enter a 4 digit number.")
Call GoBackToPrevious(.FormFields(strBookmark).Name)
ElseIf (.FormFields(strBookmark).DropDown.Value = 1) Then
MsgBox ("Please select a " & Replace(Selection.FormFields(strBookmark).Name, "_", " ") & ".")
Call GoBackToPrevious(.FormFields(strBookmark).Name)
End If
End With
End Sub
and
Sub GoBackToPrevious(strPreviousField)
10: If (bool_debug) Then MsgBox ("GoBacktoPrevious - 10: strPreviousField = " & strPreviousField)
ActiveDocument.Bookmarks(strPreviousField).Range.Fields(1).Result.Select
End Sub
But when I tab out of any of the form fields, the insertion point jumps to the next form field and not back to the one that I just tabbed out of.
I know from the debug code that GoBackToPrevious is being passed the name of the current form field, but MS Word advances to the next field regardless.
I'd really appreciate it if someone can tell me how make MS Word return to and select the drop-down the user did not select appropriately instead of jumping to and selecting the next form field in the document.
Thank you.
P James Norris
EDIT:
Based on #TimothyRylatt comments, I have modified my macro and when they're called.
I have edited Validate as above (commenting out the Dim the strBookmark assignment, and I call it "on entry" to the next form field.
strBookmark is Dimed on the module's declaration section:
Option Explicit
Const bool_debug As Boolean = True
Const str_password As String = "###" ' I have a different password
Public strBookmark As String
and "on exit" from the "current" form field, I attempt to store the "current" bookmark name:
Sub StoreBookmark()
strBookmark = Selection.Bookmarks(1).Name
10: If (bool_debug) Then MsgBox ("StoreBookmark() - 10: strBookmark = " & strBookmark)
End Sub
which I call from the current form field "on exit".
But when I tab out of the current form field to the next form field, the insertion point doesn't go back to the "current" but instead stays in the next form field.
Anyone have any other suggestions/insights?
Thanks,
P James Norris
Is there any way to write a VBA Macro to input another VBA Macro into multiple excel workbooks? If so, how do I start?
Any and all help is greatly appreciated.
you'll need a reference first
Microsoft Visual Basic For Applications Extensibility 5.3
And here you go. Have fun
Public Sub AddNewModule()
Dim proj As VBIDE.VBProject
Dim comp As VBIDE.VBComponent
Set proj = ActiveWorkbook.VBProject
Set comp = proj.VBComponents.Add(vbext_ct_StdModule)
comp.Name = "MyNewModule"
Set codeMod = comp.CodeModule
With codeMod
lineNum = .CountOfLines + 1
.InsertLines lineNum, "Public Sub ANewSub()"
lineNum = lineNum + 1
.InsertLines lineNum, " MsgBox " & """" & "I added a module!" & """"
lineNum = lineNum + 1
.InsertLines lineNum, "End Sub"
End With
End Sub
You can also just use the workbook with the code in it as a reference as well. Then you can call the module remotely.
As #BruceWayne mentioned, there is also sotring it in the personal book.
tl;dr - there's a few options that can get you there.
I recommend storing them in the Personal.xslb file which is accessible across Excel.
See this page or this page for more detail, but generally a quick way to get started is:
Press ALT+F11 to open the VBEditor.
Right click the "VBAProject (PERSONAL.XLSB)" and Add a new module
Add your code in the module.
Now, when you go to View --> Macros, you can choose to see those stored in the Personal.xlsb file:
(I "whited out" my macros for privacy, but they'll be listed by name)
Note: If you do not have a "Personal.xlsb", then you must create it. Simply record a new macro, but choose to store it in "Personal Macro Workbook". Then you should see it in the VBEditor.
I would think the easiest way to have the same code in slightly different Excel files is to have one 'template' and save it several times as several slightly different files. Or, if you want to get fancy, you can create an AddIn to make an Excel Macro available to all workbooks.
Option Explicit
Dim cControl As CommandBarButton
Private Sub Workbook_AddinInstall()
On Error Resume Next 'Just in case
'Delete any existing menu item that may have been left.
Application.CommandBars("Worksheet Menu Bar").Controls("Super Code").Delete
'Add the new menu item and Set a CommandBarButton Variable to it
Set cControl = Application.CommandBars("Worksheet Menu Bar").Controls.Add
'Work with the Variable
With cControl
.Caption = "Super Code"
.Style = msoButtonCaption
.OnAction = "MyGreatMacro"
'Macro stored in a Standard Module
End With
On Error GoTo 0
End Sub
Private Sub Workbook_AddinUninstall()
On Error Resume Next 'In case it has already gone.
Application.CommandBars("Worksheet Menu Bar").Controls("Super Code").Delete
On Error GoTo 0
End Sub
This code will be all you need to add a single menu item (called Super Code) to the end of the existing Worksheet Menu Bar as soon as the Add-in is installed by the user via Tools>Add-ins. When the Super Code menu item is clicked a macro (that is within a standard module of the add-in) is run. As mentioned earlier, the above code MUST be placed in the Private Module of ThisWorkbook for the Add-in.
If you want the Super Code menu item added, say before the Format menu item, you could use some code like this.
Option Explicit
Dim cControl As CommandBarButton
Private Sub Workbook_AddinInstall()
Dim iContIndex As Integer
On Error Resume Next 'Just in case
'Delete any existing menu item that may have been left
Application.CommandBars("Worksheet Menu Bar").Controls("SuperCode").Delete
'Pass the Index of the "Format" menu item number to a Variable.
'Use the FindControl Method to find it's Index number. ID number _
is used in case of Customization
iContIndex = Application.CommandBars.FindControl(ID:=30006).Index
'Add the new menu item and Set a CommandBarButton Variable to it.
'Use the number passed to our Integer Variable to position it.
Set cControl = Application.CommandBars("Worksheet Menu Bar").Controls.Add(Before:=iContIndex)
'Work with the Variable
With cControl
.Caption = "Super Code"
.Style = msoButtonCaption
.OnAction = "MyGreatMacro"
'Macro stored in a Standard Module
End With
On Error GoTo 0
End Sub
There would be no need to change the Workbook_AddinUninstall() code in this case.
We have covered ID numbers while working with CommandBars etc in a P rior Newsletter Issue The link to the Microsoft site that has a BIG list of all the ID numbers for working with CommandBars can be Found Here
The above examples actually have the all the menu item code in the Workbook_AddinInstall and Workbook_AddinUnInstall Not a problem when the code is only adding one menu item. If however, you will be adding more then one and perhaps even Sub menus, you should place it in a Procedure (or 2) inside a standard Module. Then use some code as shown below
Private Sub Workbook_AddinInstall()
Run "AddMenus"
End Sub
Private Sub Workbook_AddinUninstall()
Run "DeleteMenu"
End Sub
Then in the standard module put some code perhaps like this
Sub AddMenus()
Dim cMenu1 As CommandBarControl
Dim cbMainMenuBar As CommandBar
Dim iHelpMenu As Integer
Dim cbcCutomMenu As CommandBarControl
'(1)Delete any existing one.We must use On Error Resume next _
in case it does not exist.
On Error Resume Next
Application.CommandBars("Worksheet Menu Bar").Controls("&NewMenu").Delete
'(2)Set a CommandBar variable to Worksheet menu bar
Set cbMainMenuBar = Application.CommandBars("Worksheet Menu Bar")
'(3)Return the Index number of the Help menu. We can then use _
this to place a custom menu before.
iHelpMenu = cbMainMenuBar.Controls("Help").Index
'(4)Add a Control to the "Worksheet Menu Bar" before Help
'Set a CommandBarControl variable to it
Set cbcCutomMenu = cbMainMenuBar.Controls.Add(Type:=msoControlPopup, Before:=iHelpMenu)
'(5)Give the control a caption
cbcCutomMenu.Caption = "&New Menu"
'(6)Working with our new Control, add a sub control and _
give it a Caption and tell it which macro to run (OnAction).
With cbcCutomMenu.Controls.Add(Type:=msoControlButton)
.Caption = "Menu 1"
.OnAction = "MyMacro1"
End With
'(6a)Add another sub control give it a Caption _
and tell it which macro to run (OnAction)
With cbcCutomMenu.Controls.Add(Type:=msoControlButton)
.Caption = "Menu 2"
.OnAction = "MyMacro2"
End With
'Repeat step "6a" for each menu item you want to add.
'Add another menu that will lead off to another menu
'Set a CommandBarControl variable to it
Set cbcCutomMenu = cbcCutomMenu.Controls.Add(Type:=msoControlPopup)
' Give the control a caption
cbcCutomMenu.Caption = "Next Menu"
'Add a control to the sub menu, just created above
With cbcCutomMenu.Controls.Add(Type:=msoControlButton)
.Caption = "&Charts"
.FaceId = 420
.OnAction = "MyMacro2"
End With
On Error GoTo 0
End Sub
Sub DeleteMenu()
On Error Resume Next
Application.CommandBars("Worksheet Menu Bar").Controls("&NewMenu").Delete
On Error GoTo 0
End Sub
You can find all details here.
http://www.ozgrid.com/VBA/excel-add-in-create.htm
For various reasons, I want to set up a custom button on my forms to save the current record. I use a navigation form and want to trigger the same process (integrity-checks, user input etc.) whenever the entry is saved, thus whenever the user presses the "save"-button or switches to another form. The user will conditionally be asked to confirm the process and is thus able to cancel it as well.
Everything is running smoothly with one really odd and annoying exception: Whenever I click the save button on a new record and prompt a message within the "BeforeUpdate" event, I receive
RTE 3021 ("no current record")
Without the MsgBox, everything is fine. Even more strange:
When I trigger the save process by switching to another form using the navigation form (or simply press "outside" the form used for data entry), everything is fine as well.
Here is a minimalistic example (similar results with DoCmd.Save, Requery or acCmdSaveRecord):
Private Sub vt_save_Click()
Me.Dirty = False
End Sub
Private Form_BeforeUpdate(Cancel As Integer)
Cancel = True
MsgBox "Test"
End Sub
Any ideas? I simply can't wrap my head around that error.
You could maybe try to run a query using the values in the form while checking if the record exists or not.
Is there a primary key on the table? if so, the primary key will be your focal point.
Private Sub vt_Save_Click()
dim rst as DAO>Recordset
Dim strSQL as String
Dim strID as string
strID = me.YourPrimaryKeyField
strSQL = "SELECT * " & _
"FROM YourTableName " & _
"WHERE (((YourTableName.YourFieldName) =" & me.PrimaryKeyField & "));"
set rst = currentdb.openrecordset(strsql)
if rst.recordcount = 0 then
currentdb.execute "INSERT INTO YourTableName ( List All Fields to Add ) " & _
"SELECT List All Field controls with values to add;"
End IF
'Anything else you want the code to do from here
EndCode:
If not rst is nothing then
rst.close
set rst = nothing
End IF
End Sub
Repeat this process for the Form_LostFocus() event. If you want to make it easier, make this code a module and call within both event triggers on your form.
If this doesn't work please let me know and I will be happy to further assist.
The most straight forward and reasonable solution is to use an Error Handler - which I ignored so far tenaciously.
Private Sub save_Click()
On Error GoTo Err_Handler
Me.Dirty = False
Exit_Here:
Exit Sub
Err_Handler:
If Err.Number = 2101 Then
'ignore or message
Else
MsgBox Err.Description
End If
Resume Exit_Here
End Sub
First of all, you start with a form named LoginF. Once you choose your login ID, and password; and log in it takes data from the table LoginIntoT for the login ID you chose, and creates a query with said data using this code:
On Error Resume Next
DoCmd.DeleteObject acQuery, "IsAdminQ"
On Error GoTo Err_LoginBtn_Click
Dim qdef As DAO.QueryDef
Set qdef = CurrentDb.CreateQueryDef("IsAdminQ", _
"SELECT IsAdmin " & _
"FROM LoginInfoT " & _
"WHERE EmployeeID = " & LoginCmBx.Value)
Exit_LoginBtn_Click:
DoCmd.Close acForm, "LoginF", acSaveNo
DoCmd.OpenForm "MenuF"
Exit Sub
Err_LoginBtn_Click:
MsgBox Err.Description
Resume Exit_LoginBtn_Click
From there in that query after you log in is only 1 column and 1 row; meaning one piece of data. This data is a yes/no field which is either Yes or No depending on who you logged in as.
On the form it opens after you click the login button it has a logout button. The logout button brings you to the previous login form, and deletes the query (IsAdminQ).
What I am trying to do is attach a yes/no button on a form to take that data, and output if it's yes or no on the query.
I've tried putting this in it's control source:
=[IsAdminQ].[IsAdmin]
Though what that does is output it as a filled in square instead of a checkmark or empty. I have triple state set as no.
How would I attach the checkbox to the query so if the data says yes, then it's a check mark and if it says no it is an empty box?
I understand you.
"On the form it opens after you click the login button it has a logout button", we call it frmLogout. You shall do this:
Solution I:
frmLogout.RecordSource = "IsAdminQ"
Then for your checkbox named MyCheckbox, we set it this:
Me.MyCheckbox.ControlSource = "IsAdmin"
You cannot use this:
Me.MyCheckbox.ControlSource = "[IsAdminQ].[IsAdmin]" ' <= here it's impossible.
Solution II:
On the form frmLogout without setting IsAdminQ as .RecordSource,
In a Public Module, insert this:
Function GetLoginStateIsAdmin()
'
Dim rst As DAO.Recordset
Set rst = CurrentDb.OpenRecordSet("IsAdminQ")
GetLoginStateIsAdmin = Nz(rst(0), False)
Set rst = Nothing
'
End Function
Then in the private module of any form, as frmLogout:
Private Sub Form_Open(Cancel As Integer)
'
Me.MyCheckbox.Value = GetLoginStateIsAdmin()
'
End Sub
Set it in addition in design mode:
Me.MyCheckbox.TripleState = false
And check also if the query IsAdminQ has been successfully created in the login step. And open it in Access Navigation Pane by double-clicking on it. And see the value of the query.
I would like to programmatically display the right click context menu with focus through a VBA macro in Word 2007.
This would allow me to map the macro to a hotkey and expose the menu with focus without leaving the keyboard. I assumed this would be done through the Application object's CommandBars collection, accessed along the lines of :
Application.CommandBars.'access appropriate mehod or member here'
But I do not see any method or member that seems like it would show the context menu. Is it possible to achieve this through a VBA macro?
EDIT:
As suggested I looped through each CommandBar and obtained the name and index to try and find out which CommandBar index to use:
Sub C_RightClick()
'Activates right-click context menu
'
Dim cbar As Office.CommandBar
Dim cbarIndex As Integer
Dim testString As String
Dim cBarsArray(0 To 500)
Dim arrayCounter As Integer
testString = ""
arrayCounter = 1
For Each cbar In CommandBars
'TRUE if right-click
'If LCase(cbar.Name) = 'right-click' Then
' cbarIndex = cbar.Index
'End If
testString = testString + CStr(cbar.Index) + " " + cbar.Name + " " + CStr(cbar.Type = msoBarTypePopup) + vbCrLf
Debug.Print cbar.Name; " "; cbar.Type = msoBarTypePopup
'Add name to array and increment counter
cBarsArray(arrayCounter) = cbar.Name
arrayCounter = arrayCounter + 1
Next cbar
MsgBox testString
'Application.CommandBars(cbarIndex).ShowPopup
End Sub
However, I do not see any titled 'Right-Click'. I thought it may be 'Standard' , whose index is 1, but received an error when I attempted to access it.
If anyone knows the correct name for the default right-click context menu that appears in Word 2007 when the Home tab is selected, it would be appreciated. Otherwise, I will take that question to SuperUser and research on my own. Thank you for the help.
Try something like:
Application.CommandBars(100).ShowPopup
The argument can be the Commandbar index or caption.
To execute a particular command on a commandbar, try something like:
Application.CommandBars(100).Controls("Paste").Execute
To print a list of all commandbars to the Immediate Window:
Sub test()
Dim cbar As Office.CommandBar
For Each cbar In CommandBars
'TRUE if right-click
Debug.Print cbar.Name; " "; cbar.Type = msoBarTypePopup
Next cbar
End Sub
EDIT:
In answer to your question about the right-click menu that you get over the HOME tab, I think it's a different kind of control from CommandBar.
To get a better idea of the right-click menu names and indexes, I've modified the code above slightly. This now tries to add a control to each right-click menu. The added control's caption is the menu's name and Index. The controls are temporary - they'll be gone the next time you open Word.
Sub test()
Dim cbar As Office.CommandBar
Dim ctl As Office.CommandBarControl
For Each cbar In Application.CommandBars
With cbar
On Error Resume Next
'this will delete any customizations
.Reset
Set ctl = .Controls.Add(Type:=msoControlButton, Temporary:=True)
ctl.Caption = .Index & " - " & cbar.Name
Debug.Print "Name: "; cbar.Name; " Right-click: "; cbar.Type = msoBarTypePopup; " Error descr: "; Err.Description
On Error GoTo 0
End With
Next cbar
End Sub
It also prints out the error message, if there was one, to the immediate window.
The reason I don't think you'll have luck with the "Home" context menu is that no control is added to it. Here's a pic of a menu with the control added: