My app needs to click a button on a page x number of times (user defined).
The page works like this: After you click a button, the page reloads with the same yes/no buttons. Using my code, the app goes crazy and starts clicking way too fast and then crashes.
What can I do so it waits until the page reloads?
int toGet = Convert.ToInt32(numberOfTimes.Text);
int got = 0;
while (got < toGet)
{
while (webBrowser1.ReadyState != WebBrowserReadyState.Complete) Application.DoEvents();
webBrowser1.Document.GetElementById("votea").InvokeMember("click");
got++;
}
Add a delay, e.g. 500ms, between the clicks inside your while loop by using setTimeout() function
Related
I have a C# 4.0 WinForms application, which has a WebBrowser control and 2-buttons.
Clicking the first button sends a URL to the browser to navigate to a specified webSite.
Clicking the second button parses the OuterHtml of the webBrowser1.Document, looking for an "https://..." link for File Download.
The code then uses a webClient.DownloadFileAsync to pull down a file for further use in the application.
The above code successfully works, if I manually click those buttons.
In an effort to automate this for the end-user, I place the first button's click event, i.e. btnDisplayWeb.PerformClick(); in the form's Form1_Load event. This also works, allowing the webBrowser1 to populate its Document with the desired webSite.
However, I am unable to programatically click the 2nd button to acquire the web link for file download.
I have tried to place the 2nd buttons click event within the browser's DocumentCompleted event, as shown below.
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
btnMyUrl.PerformClick();
}
However, from what I've read on StackOverFlow and other sites, it's possible that this particular event gets called more than once, and hence it fails.
I've also attempted to loop for a number of seconds, or even use a Thread.Sleep(xxxx), but the browser window fails to populate until the sleep or timer stops.
I attempted to use the suggestions found on the following StackOverFlow site shown below.
How to use WebBrowser control DocumentCompleted event in C#?
private void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
string url = e.Url.ToString();
if (!(url.StartsWith("http://") || url.StartsWith("https://")))
{
// in AJAX
}
if (e.Url.AbsolutePath != this.webBrowser.Url.AbsolutePath)
{
// IFRAME
}
else
{
// REAL DOCUMENT COMPLETE
}
}
However, in parsing the OuterHtml, nothing is returned in the first two sections, and in the third section, other elements are returned instead of the desired "https://..." link for File Download.
Interestingly, if I use a webBrowser1.ReadyState event, as shown below, and place a MessageBox inside DocumentCompleted, this seems to allow the browser document to complete, because after clicking the OK button, the parsing is successful.
if (webBrowser1.ReadyState == WebBrowserReadyState.Complete)
{
MessageBox.Show("waiting", "CHECKING");
btnMyUrl.PerformClick();
}
However, I then have the difficulty of finding a way to click the OK button of the MessageBox.
Is there another event that occurs after the DocumentCompleted event.
OR, can someone suggest how to programmatically close the MessageBox?
If this can be done in code, then I can perform the buttonClick() of the 2nd button in that section of code.
After finding that the addition of a MessageBox allows the webBrowser1.Document to complete, and using webBrowser1.ReadyState event within the webBrowser_DocumentCompleted event, all I needed to do, was to find a way to programmatically close the MessageBox.
Further searching on StackOverFlow revealed the following solution on the site below.
Close a MessageBox after several seconds
Implementing the AutoClosingMessageBox, and setting a time interval, closed the MessageBox and allowed my button click, i.e. btnMyUrl.PerformClick(); to successfully parse the OuterHtml and now the code works properly.
Hopefully, if someone else discovers that placing a MessageBox within the webBrowser_DocumentCompleted event allows the document to complete; the aforementioned AutoClosingMessageBox will assist them as well.
I'm trying to make an app where when the user presses Tab two times in a row, the second Tab should act like a Ctrl + Tab instead.
I'm using a toggle = !toggle to detect the second time user presses Tab. Do you know how can I do this without using the KeyDown event (webbrowser control doesn't have those type of events) but using PreviewKeyDown or something similar?
It's expected behavior:
First time user presses Tab, the input should be detected and go through the WebBrowser control (normal behavior)
Second time user presses Tab should be handled and transformed into Ctrl + Tab.
In the below example, I used KeyDown event of WebBrowser.Document.Body. And checked for Tab key and cached the 2nd time the user press it and changed it to a Ctrl+Tab:
int i = 0;
void Body_KeyDown(object sender, HtmlElementEventArgs e)
{
if (e.KeyPressedCode == (int)Keys.Tab && !e.CtrlKeyPressed)
{
if (i == 1)
{
e.ReturnValue = false;
SendKeys.Send("^({TAB})");
this.Text += "[C+T]"; /*Just to be obvious in title-bar for test*/
}
else
{
this.Text += "[T]"; /*Just to be obvious in title-bar for test*/
}
i++;
}
}
To test the solution, make sure you are attached event handlers correctly and after the document load completed, press Tab more than 2 times and look at title bar of form.
The expected result of code is:
First Tab press, acts normally.
Second Tab press will be changed to Ctrl+Tab
Third and so on acts normally
Ctrl+Tab acts normally
Note:
You will see [T][C+T][T][T][T]... in title bar. If you want to see [T][C+T][T][C+T][T][C+T]... just change the criretia to if (i % 2 == 1).
I have a page that is dynamically loaded and contains a button. I am trying to wait for the button to be available to be clicked with selenium using the C# bindings. I have the following code:
WebDriverWait wait = new WebDriverWait(Driver.Instance, TimeSpan.FromSeconds(30));
wait.Until(ExpectedConditions.ElementToBeClickable(By.Id("addInspectionButton")));
var button = Driver.Instance.FindElement(By.Id("addInspectionButton"));
button.Click();
this doesn't work though. The click event is never fired. The selenium script doesn't throw an exception alerting that the element with an ID of "addInspectionButton" doesn't exist. It just isn't able to click it. If i add a Thread.Sleep(3000) Between the wait statement and the line where I get a handle on the button element it works.
Am i not using the ExpectedConditions.ElementToBeClickable correctly here?
It turns out that an event was being bound to the button after the button was dynamically added to the page. So the button WAS being clicked but nothing was happening. The sleep thread being placed in the code was just giving the client side event time to be bound.
My solution was to click the button, check for the expected result, and then repeat if the expected result wasn't in the DOM yet.
Since the expected result was for a form to open I polled the DOM like this:
button.Click();//click button to make form open
var forms = Driver.Instance.FindElements(By.Id("inspectionDetailsForm"));//query the DOM for the form
var times = 0;//keep tabs on how many times button has been clicked
while(forms.Count < 1 && times < 100)//if the form hasn't loaded yet reclick the button and check for the form in the DOM, only try 100 times
{
button.Click();//reclick the button
forms = Driver.Instance.FindElements(By.Id("inspectionDetailsForm"));//requery the DOM for the form
times++;// keep track of times clicked
}
I am trying to make my WebBrowser wait until the page fully loads, then proceed to the next step. I've research how to do this, yet, my code keeps running before the page loads.
private void AdobeConnect_Load(object sender, EventArgs e)
{
for (int x = 1; x <= 3; x++)
{
while (acBrwsr.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
}
adobeStepper(x);
}
}
The problem is you are relying on ReadyState and you shouldn't be. In WebBrowser.DocumentComplete you need to check e.Url == WebBrowser.Url then check for the ready state. DocumentComplete fires multiple times with forms when you have frames and that messes with ReadyState.
What I do with my bots that use WebBrowser is I activate a timer when I have a document complete state for the actual page then grab my next item in the queue to process for that page like 1 second after the completion. (Of course always turn off the timer in the OnTick event.)
I wrote a queue that groups a set of tasks together where I can prioritize and remove any items like a list so I don't repeat tasks but only perform when DocumentCompleted e.url == webBrowser.url and my ReadyState is Complete.
I have a problem while doing this in the code-behind file of a winform :
// Waiting Cursor + disabling form
Cursor = Cursors.WaitCursor;
this.Enabled = false;
// Synchronous method
SomeWork();
// Re-enabling form
Cursor = Cursors.Default;
this.Enabled = true;
Current Behaviour
Clicking on a button for example during Somework() will execute the method associated to the button after re-enabling the form.
Expected Behaviour
I don't expect from the form to store the clicking events of the user while the form is disabled.
Question
Is there a way to empty the Clicking cache of the form (So that I'd do it before re-enabling the form) ?
IMPORTANT EDIT
A possible easy solution would be implementing the IMessageFilter interface in the code behind of the form. Disabling the left seems easy using this PreFilterMessage :
public bool PreFilterMessage(ref Message m)
{
// Blocks all the messages relating to the left mouse button.
return (m.Msg >= 513 && m.Msg <= 515) ;
}
But once again, disabling and re-enabling the mouse's left clicks DOES NOT EMPTY THE MOUSE BUFFER ...
The problem is that the process is running in the same thread, so the form doesn't actually get disabled before the process starts running. The easy thing to do would be use Application.DoEvents() to force it to set everything to disabled before starting the process, but the more professional (and probably safer) method is to run the time-consuming process in another thread.
NOTE: After running into another hitch in my own programming I found that you may have to run Application.DoEvents() before enabling everything again--it will fire any clicks the user made on the disabled controls, instead of waiting for the process to complete--enabling the controls--and THEN firing the click.
Obviously DoEvents is messy and I should be using threads.