I'm new using Selenium Driver in VBA and I have problems doing a click. I use ".Wait" before the ".Click" but sometimes works and sometimes not.
With MyBrowser
.Wait (WaitTime)
.FindElementByXPath("//div[text()='" + cl.Value + "']", timeout).Click
End With
There's any way to make a loop until the ".Click" is successfully clicked?
Thanks!
I created a loop using "On Error GoTo" that I don't know whether is the best way but works well for me.
Sub WaitElements(element As String)
Retry:
On Error GoTo Counter
MyBrowser.FindElementByXPath("//div[text()='" + element + "']").Click
Exit Sub
Counter:
i = i + 1
Debug.Print i
If i = 10 Then
On Error GoTo 0
MyBrowser.FindElementByXPath("//div[text()='" + element + "']").Click
End If
Resume Retry
End Sub
I added a counter for avoid an infinite loop. If counter reach 10 then display the error. Also I printed how many loop I got in the Immediate window using debug.print.
I hope this helps others!
Related
Extremely puzzling:
Upon opening a simple form from another form by vba, the cursor moves to a particular field.
However, when this field is Null there is each second time Error 2110. The syntax to be used changes every time as shown below.
Even more puzzling:
Upon clicking "Debug", the error proves to be imaginary: on the corresponding code line, one can simply continue with F5 or F8 and the procedure ends correctly with the focus where desired.
I found a provisory workaround which does not generate the error message but would like if possible to avoid such limping coding:
'…
Debug.Print Me![MyTextField].Enabled ' always True
Debug.Print Me.Name ' always correct form
Me.Repaint
On Error Resume Next
[MyTextField].SetFocus ' without Me!
Me![MyTextField].SetFocus
' Forms![MyForm]![MytextField] : same result as with Me!]
' one time error with Me! but not without Me!,
' next time vice versa, and so forth…
On Error GoTo 0
'…
When [MyTextField] is not Null, both syntaxes work fine without generating an error.
What is wrong with this .SetFocus command ? "Repairing" the database didn't help.
You can't set focus to the control that has focus. It would give you a very easy way to set up a infinite loop.
You would have to set focus to another control first.
Minty's right. An easy workaround is doing it in an if statement or create a bool to see if IsNull(fieldhere) = true then exit sub or your action here. Or more simply maybe try:
if MyTextField.gotfocus = true then
do something
else
MyTextField.setfocus
I recently ran into this issue in something similar and had to create a public bool function and test it on load events. Please note that I'm a beginner in access so there's probably many better ways to complete this :)
I am using a DAO handle (represented in the below code) to improve the speed and performance of my Access database which is found on a shared network and is quite slow. The below code was offered to me by an expert to help the database improve it's speed and performance. As you can see, the database upon opening opens the handle (OpenAllDatabases True), and then closes it upon closing the database (OpenAllDatabases False).
My issue arrives when the database unexpectedly closes. When this happens, I am then informed that i no longer can get into edit mode of the database because it is already opened by another user. I imagine that this is the case because the the 'OpenAllDatabases' was set to TRUE when the database unexpectedly closed. When this happens, i am forced to open the database in exclusive only deactive the code, close and re-open the database and then rebuild the code. This for me is quite risky especially since there are multiple users using the tool. Below is my code:
On the main form:
Form_Load()
OpenAllDatabases True
End Sub
On the command buttons to close the database:
Private Sub cmdCloseDatabase_Click()
OpenAllDatabases False
End Sub
Module
Sub OpenAllDatabases(pfInit As Boolean)
' Open a handle to all databases and keep it open during the entire time the application runs.
' Params : pfInit TRUE to initialize (call when application starts)
' FALSE to close (call when application ends)
' Source : Total Visual SourceBook
Dim x As Integer
Dim strName As String
Dim strMsg As String
' Maximum number of back end databases to link
Const cintMaxDatabases As Integer = 2
' List of databases kept in a static array so we can close them later
Static dbsOpen() As DAO.Database
If pfInit Then
ReDim dbsOpen(1 To cintMaxDatabases)
For x = 1 To cintMaxDatabases
' Specify your back end databases
Select Case x
Case 1:
strname="S:\Apps\PRESTO\BE.accdb"
End Select
strMsg = ""
On Error Resume Next
Set dbsOpen(x) = OpenDatabase(strName)
If Err.Number > 0 Then
strMsg = "Trouble opening database: " & strName & vbCrLf & _
"Make sure the drive is available." & vbCrLf & _
"Error: " & Err.Description & " (" & Err.Number & ")"
End If
On Error GoTo 0
If strMsg <> "" Then
MsgBox strMsg
Exit For
End If
Next x
Else
On Error Resume Next
For x = 1 To cintMaxDatabases
dbsOpen(x).Close
Next x
End If
End Sub
In Sub OpenAllDatabases, I see a problem with these two lines:
Const cintMaxDatabases As Integer = 2
' ...
For x = 1 To cintMaxDatabases
Select Case x
Case 1:
strname="S:\Apps\PRESTO\BE.accdb"
End Select
You are going through the loop twice, but are only setting the database path once. If you follow your code, you are making TWO connections to "S:\Apps\PRESTO\BE.accdb".
Fix this error so you are only making one connection, and see if your problem goes away.
OK, thanks for fixing that issue.
I use similar code, which works all the time. I have been comparing your code to mine, and trying to think what the difference could be.
The next thing I would like for you to try is change this line:
Set dbsOpen(x) = OpenDatabase(strName)
To:
Set dbsOpen(x) = OpenDatabase(strName, ReadOnly:=True)
In my quick tests, this will still improve the performance of the application, and your forms can still write to the backend data.
This way, OpenAllDatabases cannot get a write lock on your backend database. See if this solves the issue when your frontend closes unexpectedly.
In access using VBA.
I have a quite simple situtation, and I see some different solutions online, but non of them works for me.
I have a form with a textbox that needs to be updated in a for loop.
I have reduced the script to the following:
For counter = 1 To 100
Application.Echo False
DoCmd.OpenQuery "Query1"
DoCmd.Close
Application.Echo True
strStatus = "Loop is " & counter & " % done"
Call dsp_progress_AfterUpdate
Me.Refresh
DoEvents
Next
And the sub thats called:
Private Sub dsp_progress_AfterUpdate()
Me.Dirty = False
End Sub
The textbox controlsource is the strStatus (Through a function).
Every loop takes about 4 seconds (The query), so it is not because its over in 2 ms.
It only updates when the for loop is finished.
The strange thing is, if i use the curser and manually click on the text box while the loop is running, it actually works.........
So the question is, how do i make it update "live" without having to click on the textbox with the mouse/curser? That is not convenient for a status display.
Help, please... :)
Best Regards, Emil.
I'm not sure what the root cause of it failing to update the text box is, but adding in one line fixed this issue for me. Setting focus to the textbox that you are updating (which is what you are doing when you click on it), causes it to update.
textBox.SetFocus
Add that in to your code before starting the loop.
Changing the code to the following should get rid of the flickering and the fact that the text is highlighted.
For counter = 1 To 100
Application.Echo False
DoCmd.OpenQuery "Query1"
DoCmd.Close
strStatus = "Loop is " & counter & " % done"
Call dsp_progress_AfterUpdate
Me.Refresh
TextBox.SetFocus
TextBox.SelStart = 0
TextBox.SelLength = 0
DoEvents
Application.Echo True
Next
The below code is bound to a button. For some reason the below code executes correctly the first time. On the second time, for some users, it does not populate the fields on the website. What could the issue be?
Me!WebBrowser0.Navigate "http://bk00app0001/PMD Image Upload/"
While Me.WebBrowser0.Busy
DoEvents
Wend
Me!WebBrowser0.Document.getElementById("MainContent_Year").Value = Forms![Main Menu]![SeasonList].Value
Me!WebBrowser0.Document.getElementById("MainContent_ItemNumber").Value = Me.OpenArgs
Me!WebBrowser0.Document.getElementById("MainContent_btnLock").Click
I believe I need to set Me!WebBrowser0 = Nothing on the Close event.
However, Me!WebBrowser0 = Nothing throws an error. Any ideas as to how to do this?
UPDATE
On the second time this code is called MS Access throws the following error when it hits this line:
Me!WebBrowser0.Document.getElementById("MainContent_Year").Value = Forms![Main Menu]![SeasonList].Value
'Object variable or With block variable not set'
Looks like it was a timing issue. I was able to solve it by adding the following code before I set any of the values on the webpage:
While Me!WebBrowser0.Document.getElementsByTagName("p").Length < 10
DoEvents
Wend
I am now getting the error: "Runtime error '430' class does not support Automation or does not support expected interface"
This error refers to the following line of code:
Set Cnxn = New ADODB.Connection
Here is a screenshot for the line of code above:
http://postimage.org/image/2v7p8diis/
The above code is called when I close the form that has the button that calls the browser code.
While Me!WebBrowser0.Document.getElementsByTagName("p").Length < 10
DoEvents
Wend
Solved the first issue.
The second issue was due to referencing a non-existent DLL on the user's machine.
Is there any method to avoid the annoying write conflict messages by automating and hiding the process so that it doesn't appear as if the program is defective? There doesn't seem to be any point to these messages anyway as there is only one real choice which is to drop the changes.
The only way I know to avoid that message is to requery your screen after running a process or changing data on the backend database (or sql server)
You should be able to handle these errors in a combination of two places. The first, and most important is on the Form_Error event. Your code will look something like this:
Private Sub Form_Error(DataErr As Integer, Response As Integer)
If DataErr = 7787 Then
MsgBox "Oops, this record was edited by someone else or " & _
"in another screen while you were making edits." & _
"Your edits cannot be saved."
Response = acDataErrContinue
End If
End Sub
You will also need to handle error 3021 anywhere you run a Save command in VBA, like this:
Private Sub cmdSave_Click()
On Error GoTo ErrHandler
DoCmd.RunCommand acCmdSaveRecord
Exit Sub
ErrHandler:
If Err.Number = 3021 Then
'Do Nothing
Resume Next
Else
'Handle other errors here
Resume Next
End If
End Sub
Now, I readily agree with one of the comments that it's more important that you try to resolve whatever is causing these errors rather than coding around them. In my case I am using the above solution to handle write conflicts that occur when a user opens two instances of the same form to the same record and makes edits in both instances. It would be better if I would prevent the user from opening the same record twice or prevent edits by only allowing the user to make edits in one of the open form instances but neither of these are exactly easy to implement, especially when you are using your own forms collection so I guess you could say I'm waiting for a "rainy day".