I'm trying to click a button in a form in MS Access with VB .net. However, there isn't much I can find in this area and have a bit of a long way of getting the button. Then I'm stuck - there seems to be no way to activate the click event.
Using :
Imports Microsoft.Office.Interop
I have the following to get the button:
Dim acc As New Access.Application
acc.OpenCurrentDatabase("C:\path\to\db\aDatabase.accdb")
acc.Visible = True
For i = 0 To acc.Forms.Count - 1
If acc.Forms.Item(i).Name = "formName" Then
For j = 0 To acc.Forms.Item(i).Controls.Count - 1
If acc.Forms.Item(i).Controls.Item(j).name = "btnEnter" Then
Dim btn As Access.CommandButton = acc.Forms.Item(i).Controls.Item(j)
'
' click on button??
'
End If
Next
End If
Next
I've had a guess at trying the following:
acc.Application.Run(btn.OnClickMacro)
acc.Application.Run(btn.OnClick)
btn.OnClickMacro
btn.OnClick
btn.performclick()
none of which work.
By default, the click event handler is declared in VBA as
Private Sub Command0_Click()
' do something
End Sub
You won't be able to programmatically click the button outside of Access' VBA code if it's private (unless the developer intentionally declared it public). Best practice is to make a function which is called by the handler, which can be called elsewhere as well, instead of clicking buttons through code.
Private Sub Command0_Click()
DoSomething
End Sub
' you would have a better chance calling this from .NET
Public Sub DoSomething()
' do something
End Sub
If you don't have access to the Access VBA code, outside of .NET you could use something to automate mouse clicks like this: https://autohotkey.com/
I mean, if you have to have Access intslled, open and running, then why is such code not being run from Access? So it does not make a whole lot of sense to call such code from .net.
However, simply make the private sub click as public, and then you can use this form:
MyAccessApp.Forms("main").command44_click
So you don’t have to create any external functions etc., but if you wish to call that code directly then the form MUST be opened, and with a running instance of that application from .net, then the above syntax will work.
I would suggest you just use Access here, and it nots clear why .net is involved.
Related
I'm trying to create a add in for MS Word with VBA.
It has a "AutoExec" procedure that creates a new item in the CommandBar("Text") collection (right click menu) and a "AutoExit" that deletes this created item.
As an example, I tried the code below that create an item "How Many Pages?", which executes a macro displaying the number of pages in the active document.
This is the AutoExec Code:
Public Sub AutoExec()
Dim objcommandbutton As CommandBarButton
Call MsgBox("AutoExec")
Set objcommandbutton = Application.CommandBars("Text").Controls.Add _
(Type:=msoControlButton, Before:=1)
objcommandbutton.Caption = "How Many Pages?"
objcommandbutton.OnAction = "HowManyPages"
End Sub
This is the AutoExit Code:
Public Sub AutoExit()
Dim objcommandbutton As CommandBarControl
Call MsgBox("AutoExit")
For Each objcommandbutton In Application.CommandBars("Text").Controls
If objcommandbutton.Caption = "How Many Pages?" Then
objcommandbutton.Delete
End If
Next objcommandbutton
End Sub
This is the Main Macro Code:
Public Sub HowManyPages()
If Documents.Count > 0 Then
Call MsgBox(ActiveDocument.BuiltInDocumentProperties("Number of Pages"))
Else
Call MsgBox("No document is currently active.")
End If
End Sub
When exiting the document, the Button previously added in CommandBars("Text") collection is not deleted. I see this when I open a new blank Word document and the button remains in the right click menu.
I know that the routine is performed correctly because there is a MsgBox instruction to verify it.
This only happen with the AutoExit subroutine of a add-in, that is, loaded as a add-in: running the code in a macro with vba module works fine.
Any help?
When working with the CommandBars object model in Word it's necessary to always specify the Application.CustomizationContext.
Word can save keyboard layouts and CommandBar customizations in various places: the Normal.dotm template, the current template or the current document. The default when you create a CommandBar addition may not be the3 same as the default when trying to delete something.
Since this is an add-in, I assume you want the change for the entire Word environment (any open document). In that case, use the context NormalTemplate. Use this before any calls to CommandBar:
Application.CustomizationContext = NormalTemplate
Note: for saving a customization in the current document: = ActiveDocument; for saving in the template attached to the current document: = ActiveDocument.AttachedTemplate.
I solved my problem with a workaround:
I was trying to "add" a template (.dotm) as a add-in (in the "Templates and Add-ins" window) to use my VBA project in a new document. That's why I was using the AutoExec() and AutoExit() procedures.
But only now I figure out that just "attaching" the .dotm template to the active document (in the same "Templates and Add-ins" window, as show in the figure below) makes the functions Private Sub Document_Open() and Private Sub Document_Close() to run normally. Which solves my problem.
Even so, I think there is a certain "issue" with the AutoExit() procedure when trying to change the CommandBars itens. But that's ok for now.
enter image description here
I have a very simple form that, when loaded, should create a new object Thing that is defined in a specific Class Module. The UF also has several buttons associated to different functions.
This is the UF's code associate to the load event:
Option Explicit
Private mth As Thing
Private Sub UserForm_Load()
Set mth = New Thing
mth.Name = InputBox("Enter a name for the Thing")
End Sub
If I F5 directly on this, the UF shows up but the code doesn't run furth and the Thing object mth is not even created ...
I also tried to call the form from a module with the following code but the result was the same:
Sub test()
Dim uf As Object
Set uf = New Dinamico 'this is the name of the UserForm
Load uf
End Sub
As result, I would like that each time that the form is loaded, a new mth was created and an InputBox asking for the name appeared. I have the feeling to be missing something very stupid...could you help me out please ?
Write your code within
Private Sub UserForm_Initialize()
End Sub
UserForm_Load is in VB.Net while in VBA its UserForm_Initialize.
I am building an Access report (2010 version), and would like to be able to customize it based on the user's selections on a form. When I run it, I get error 2771: The bound or unbound object frame you tried to edit does not contain an OLE object.
This is the code to pass the parameter:
Private Sub Command120_Click()
DoCmd.OpenReport ReportName:="rpt_EODGraph", View:=acViewPreview, _
OpenArgs:=Me!Text0.Value
End Sub
This is the code to open the report.
Private Sub Report_Open(Cancel As Integer)
Dim ch As Chart
Set ch = Me.Graph3.Object.Application.Chart 'This line generates the error
ch.ChartTitle.text = OpenArgs
End Sub
I've found at least one person saying that this is not actually possible to do on a report. (http://www.access-programmers.co.uk/forums/showthread.php?t=177778&page=2 Note that this is page 2 of a 2 page forum discussion...) Can anyone corroborate or refute?
Apparently the Report has to have some kind of focus before OLE objects are accessible.
It is enough if you click on it or set the focus to something:
Private Sub Report_Open(Cancel As Integer)
Dim ch As Object
Me.RandomButton.SetFocus
Set ch = Me.Diagramm11.Object
ch.ChartTitle.Text = "Hello"
End Sub
This works. I just set a button on the report that gets the focus. Perhaps you find something more elegant ;)
I am developing an Excel (2010+) Application using VBA and have run into an issue where the AfterRefresh event function is not being invoked once the query finishes executing.
I have not been able to find many decent resources or documentation for how to have this event function triggered in a Class Module. I decided to use the Class Module design route instead of putting the event handlers in the worksheet after receiving a response to an earlier question about QueryTables (found here Excel VBA AfterRefresh).
Here is the code for my Class Module called CQtEvents
Option Explicit
Private WithEvents mQryTble As Excel.QueryTable
Private msOldSql As String
' Properties
Public Property Set QryTble(ByVal QryTable As QueryTable): Set mQryTble = QryTable:
End Property
Public Property Get QryTble() As QueryTable: Set QryTble = mQryTble:
End Property
Public Property Let OldSql(ByVal sOldSql As String): msOldSql = sOldSql:
End Property
Public Property Get OldSql() As String: OldSql = msOldSql:
End Property
Private Sub Class_Initialize()
MsgBox "CQtEvents init"
End Sub
' Resets the query sql to the original unmodified sql statement
' This method is invoked when the Refresh thread finishes executing
Private Sub mQryTble_AfterRefresh(ByVal Success As Boolean)
' Problem is here
' This function is never called :( Even if the query successfully runs
Me.QryTble.CommandText = Me.OldSql
End Sub
Here is a quick snapshot of the code the creates an instance of this class, finds a relevant QueryTable, then calls Refresh
Option Explicit
Sub RefreshDataQuery()
'Dependencies: Microsoft Scripting Runtime (Tools->References) for Dictionary (HashTable) object
'From MGLOBALS
cacheSheetName = "Cache"
Set cacheSheet = Worksheets(cacheSheetName)
Dim querySheet As Worksheet
Dim interface As Worksheet
Dim classQtEvents As CQtEvents
Set querySheet = Worksheets("QTable")
Set interface = Worksheets("Interface")
Set classQtEvents = New CQtEvents
Dim qt As QueryTable
Dim qtDict As New Scripting.Dictionary
Set qtDict = UtilFunctions.CollectAllQueryTablesToDict
Set qt = qtDict.Item("Query from fred2")
''' Building SQL Query String '''
Dim sqlQueryString As String
sqlQueryString = qt.CommandText
Set classQtEvents.QryTble = qt
classQtEvents.OldSql = sqlQueryString ' Cache the original query string
QueryBuilder.BuildSQLQueryStringFromInterface interface, sqlQueryString
' Test message
MsgBox sqlQueryString
qt.CommandText = sqlQueryString
If Not qt Is Nothing Then
qt.Refresh
Else
' ... Error handling code here...
End If
''' CLEAN UP '''
' Free the dictionary
Set qtDict = Nothing
End Sub
Also here is a screenshot of the Module structure http://imgur.com/8fUcfLV
My first thought on what might be the issue was passing the QueryTable by value. I am not the most experienced VBA developer, but I reasoned this would create a copy and be calling the event on an unrelated table. However, this was not the case and passing by Reference did not fix the problem either.
Also the query is confirmed to run successfully as the data is correctly showing up and being refreshed.
EDIT
I added the BeforeRefresh event function to CQtEvents class Module and confirmed this function is called once Refresh is called
Private Sub mQryTble_BeforeRefresh(Cancel As Boolean)
MsgBox "Start of BeforeRefresh"
End Sub
How might I alter this code get my QueryTable from the QTableModule's RefreshDataQuery() Sub routine to have the AfterRefresh function invoked when the query is successfully ran?
How to catch the AfterRefresh event of QueryTable?
Explanation: in your situation, before event was fired you lost reference of your QueryTable by setting it to nothing when you made cleaning or procedure ended.
General solution: you must be sure that your code is still running and/or you need to keep any references to your QueryTable.
1st solution. When calling QT.Refresh method set the parameter to false in this way:
qt.Refresh false
which will stop further code execution until your qt is refreshed. But I don't consider this solution to be the best one.
2nd solution. Make your classQtEvents variable public and after RefreshDataQuery sub is finished check the status with some other code.
in you CQtEvents class module add the following public variable:
Public Refreshed As Boolean
in your BeforeRefresh event add this:
Refreshed = False
in your AfterRefresh event add this line of code:
Refreshed = True
Make your classQtEvents variable declaration public. Put this before Sub RefreshDataQuery()
Public classQtEvents as CQtEvents
but remove appropriate declaration from within your sub.
Now, even your sub is finished you will be able to check status of refreshment by checking .Refreshed property. You could do it in Immediate or within other Sub. This should work for Immediate:
Debug.Print classQtEvents.Refreshed
3rd solution. (a bit similar to 1st one) Follow steps 1 to 3 from 2nd solution. After you call qt.Refresh method you could add this loop which will stop further code execution until qt is refreshed:
'your code
If Not qt Is Nothing Then
qt.Refresh
Else
' ... Error handling code here...
End If
'checking
Do Until classQtEvents.Refreshed
DoEvents
Loop
Final remark. I hope I didn't mixed up qt variable with classQtEvents variable. I didn't tried and tested any solution using your variables but wrote all above with referenced to code I use.
A github repo that demonstrates the minimum code needed to get this working can be found here.
As mentioned, if your event handler isn't in scope, or your QueryTable reference is lost, you won't catch the event. The key factors to ensuring you catch the event are:
Declare a global variable of your event-handling class module's type outside of any subroutines/methods, at the top of a file (I chose the ThisWorkbook file).
Add a Workbook_Open event handler and instantiate that variable there, so that it is available immediately and will remain in scope (since it's global).
At that point, or at any downstream point when you have a QueryTable you're interested in, pass that QueryTable to the global instance to wire up its events.
(It took me a couple tries to figure this out myself, when someone pointed me in this direction as an answer to this question.)
I want create one form from another. But the Form class has no the Show method, which described at http://msdn.microsoft.com/en-us/library/office/gg251540.aspx
It's code in Form_Main:
Private Sub btnTemp_Click()
Dim frmOpt As Form_Option
Set frmOpt = New Form_Option
frmOpt.Show vbModal
End Sub
But I received the "Compile error: Method or data member not found".
Where I made mistake?
Thanks
(VBA version 6.5; Access 2007)
=====
Sorry for my previous comment: right now I see that comment isn't obvious.
I don't have subForm on my mainForm.
I have two simple form: Form_Main and Form_Option. And I want to be the next logic:
Form_Main has button "btnOption"
Click on "btnOption". The Form_Option is opening
I change options on Form_Option
And click the btnSave button on Form_Option, and the next idea is executing:
Form_Main.TimerInterval = CLng(Form_Option.edtTimerInterval.Value)
At the moment I made it simple. And that is enough for me.
I write so:
Private Sub btnOptions_Click()
' After changing options, refresh timer interval of main form
DoCmd.OpenForm "Options", , , , , acDialog
Me.TimerInterval = 1000 * CLng(MOptions.loadOption("fPeriodVerifyNoticeInterval"))
End Sub
Where fPeriodVerifyNoticeInterval is parameter that stored in the options table.
And the Options Form changes the "fPeriodVerifyNoticeInterval" parameters at saving.
My problem is solved, Thanks
The "mistake" is that Show isn't a valid Method for Access Forms. The link you provided is for UserForms which are forms made in VBA.
If you want to create a new form that way what you want is something like this:
frmOpt.Modal = true
frmOpt.Visible = true
Though what I would recommend is doing this instead:
DoCmd.OpenForm "Option", , , , , acDialog which will open the Option form as a dialog.
Caution: If you create your form using New even though you set it as modal it will not halt the progress of VBA code. This means that your variable will go out of scope as soon as the code finishes. If you want your form to remain open, you will need to set it as static within the sub or declare it outside the sub like this:
static frmOpt As Form_Option
or outside the sub private frmOpt = Form_Option or public frmOpt = Form_Option