C# WebBrowser Button Click, then go to another page - c#

I need to click an html button and navigate to another page. After click I need to wait for page loading, and go to the new page only when the old page loaded.
Here is the code, that click a button:
element = webBrowser1.Document.GetElementById("LoginButton");
element.InvokeMember("click");
webBrowser has got a IsBusy property, but it don`t works after button click:
element = webBrowser1.Document.GetElementById("LoginButton");
element.InvokeMember("click");
if(webBrowser1.IsBusy)
{
MessageBox.Show("Busy"); // Nothing happens, but page is not full loaded.
}
If I add System.Threading.Thread.Sleep(1000) the page loads and I can go to next page, but page loading time on other computers can be more.
What can I do to load another page only after the previous page has loaded?
P.S: I am from Russia, so sorry for bad English.

If your webpage has any javascript blocks, you won't be able to solve the problem using the WebBrowser control itself. You should wait for a document.ready event using javascript code and let know your C# program about it.
Previously, I made a javascript block that provides the webpage state. It looks like this:
var isBusy = true;
function getIsScriptBusy () {
return isBusy;
}
// when loading is complete:
// isBusy = false;
// document.ready event, for example
and a C# code that waits for it to return true:
void WaitForCallback(int timeout) {
Stopwatch w = new Stopwatch();
w.Start();
Wait(delegate() {
return (string)Document.InvokeScript("getIsScriptBusy") != "false"
&& (w.ElapsedMilliseconds < timeout || Debugger.IsAttached);
});
if(w.ElapsedMilliseconds >= timeout && !Debugger.IsAttached)
throw new Exception("Operation timed out.");
}
void Wait(WaitDelegate waitCondition) {
int bRet;
MSG msg = new MSG();
while(waitCondition() && (bRet = GetMessage(ref msg, new HandleRef(null, IntPtr.Zero), 0, 0)) != 0) {
if(bRet == -1) {
// handle the error and possibly exit
} else {
TranslateMessage(ref msg);
DispatchMessage(ref msg);
}
Thread.Sleep(0);
}
}

There are lots of events exposed by the WebBrowser control. You might try Navigated or DocumentCompleted.
Nick

WebBrowser.Navigated is the browser event you are seeking.

Use this ,
You might just be able to use this once
br1.DocumentCompleted += br1_DocumentCompleted;
Application.Run();
Call
void br1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var br1 = sender as WebBrowser;
if (br1.Url == e.Url)
{
Console.WriteLine("Natigated to {0}", e.Url);
Application.ExitThread(); // Stops the thread
}
}
Replace br1 with your webbrowser name
Hope this helps

Related

EvaluateScriptAsync waiting in Cefsharp

I am having a problem about using EvaluateScriptAsync in Cefsharp. I want to run a javascript code with EvaluateScriptAsync and then, after this is completed I want my code continue to work, but ContinueWith isn't waiting to EvaluateScriptAsync complete. Here is my code, and don't know what's the problem:
private void WebBrowserFrameLoadEnded(object sender, FrameLoadEndEventArgs e)
{
if (e.Frame.IsMain)
{
var task = browser.EvaluateScriptAsync("(function() { function xx() { $('body').animate({scrollTop: $('body').prop(\"scrollHeight\")}, 8000, function() { var win = $(window); if ($(document).height() - win.height() == win.scrollTop()) { return true; } else { xx(); } }); }; xx(); })();");
task.ContinueWith(taskHtml =>
{
if (taskHtml.IsCompleted)
{
/* Some code working */
}
this.DialogResult = DialogResult.OK;
});
}
}
You Should use the ExecuteSciptAsyc() method of the Browser Tab Control.
1st you have to get the Current tab page Control and then call the ExecuteSciptAsyc() method from browser control.
var control = GetCurrentTabControl();
control.Browser.ExecuteScriptAsync("alert('It's Working')");

C# Application, Tick events and waiting

I'm writing an application that will automate one of our manual webform input processes
Everything is working good except for one problem.
I have a Timer setup, that becomes enabled on a certain page. The Timer tick event is checking the page every 100 milliseconds for ajax changes applied to the page. Once the ajax updates are detected, the Timer is disabled, the result is stored, and the program SHOULD continue executing code beyond that point.
The problem is the code continues to execute while the Timer is enabled.
In the logic, as soon as the appropriate page loads, I have
t2.Enabled = true;
Which immediately works as it should, looking at the page until the update is discovered
But the code immediately following the Enabled property is executing without pause, causing many issues, such as variables changing before the result is discovered.
How can I have the code following this line wait until the t2.Enabled is set back to false (which is done within the t2_Tick(object sender, EventArgs e) method
void t2_Tick(object sender, EventArgs e)
{
string postVerifyHTML = string.Empty;
try
{
postVerifyHTML = wb.Document.Body.InnerHtml;
}
// if page fails, restart
catch
{
wb.Navigate(new Uri("http://www.website.com"), "_self");
}
if (postVerifyHTML.IndexOf("indentifier html") != -1)
{
NameSearchResults[nameCounter].Visited = true;
nameCounter++;
ResultFound = true;
t2.Enabled = false;
}
t2TimerCount++;
if (t2TimerCount >= 100)
{
// TRY AGAIN
wb.Navigate(new Uri("http://www.website.com"), "_self");
}
}
protected void wb_SearchForm_DocumentCompleted(object sender, EventArgs e)
{
string pageHTML = wb.Document.Body.InnerHtml;
// Look at the page with the name result
if (pageHTML.IndexOf("Search Results: Verify") != -1)
{
//If the page has this input, a verification is available
if (pageHTML.IndexOf("txtSSN") != -1)
{
HtmlElement txtSSN = wb.Document.GetElementById("txtSSN");
txtSSN.SetAttribute("value", curSearchRecord.UniqueId.Replace("-", "").Replace(" ", ""));
HtmlElement submitBtn = wb.Document.GetElementById("ibtnVerify");
submitBtn.InvokeMember("click");
t2.Enabled = true;
// I need the code after this point to wait until the Timer is disabled
}
The Timer is running on a different thread to your UI code, which is why your execution is continuing. Why don't you simply check the Enabled state of the Timer to determine whether or not to continue the execution? Alternatively use the callback of your ajax code to fire off the continuation code.
Im not sure this is the best method to to it but you can do a do an if like so :
if (t2.Enabled=False)
{
//the code you want to run when the timer is off
}
but you have to make sure that it is in another timer (t3 in this case if you want) otherwise it wont check every tick if t2 is off to run the code while it is.
sorry if the answer is not more detailed, I lacked details in your question as well.
Good programing :)
You could try to use a ManualResetEvent as a member of your class
After you enable the Timer, you call the WaitOne method
After your disable the Timer, you call the Set method
private ManualResetevent mre = new ManualResetEvent(false);
void t2_Tick(object sender, EventArgs e)
{
string postVerifyHTML = string.Empty;
try
{
postVerifyHTML = wb.Document.Body.InnerHtml;
}
// if page fails, restart
catch
{
wb.Navigate(new Uri("http://www.website.com"), "_self");
}
if (postVerifyHTML.IndexOf("indentifier html") != -1)
{
NameSearchResults[nameCounter].Visited = true;
nameCounter++;
ResultFound = true;
t2.Enabled = false;
//Set the mre to unblock the blocked code
mre.Set();
}
t2TimerCount++;
if (t2TimerCount >= 100)
{
// TRY AGAIN
wb.Navigate(new Uri("http://www.website.com"), "_self");
}
}
protected void wb_SearchForm_DocumentCompleted(object sender, EventArgs e)
{
string pageHTML = wb.Document.Body.InnerHtml;
// Look at the page with the name result
if (pageHTML.IndexOf("Search Results: Verify") != -1)
{
//If the page has this input, a verification is available
if (pageHTML.IndexOf("txtSSN") != -1)
{
HtmlElement txtSSN = wb.Document.GetElementById("txtSSN");
txtSSN.SetAttribute("value", curSearchRecord.UniqueId.Replace("-", "").Replace(" ", ""));
HtmlElement submitBtn = wb.Document.GetElementById("ibtnVerify");
submitBtn.InvokeMember("click");
t2.Enabled = true;
//The code will block until Set() is called on mre
mre.WaitOne();
//The rest of your code here
}

Continue executing an event after validations on modal or radwindow

I have the following scenario: A user makes a click on a button inside an asp page. Because of security reasons, during the click event execution the system determines that it is necessary to apply some validations before continuing with the execution of the fired event.
Those validations are shown in a window (in this case a Telerik RadWindow). Inside this RadWindow, there is a web user control (WUC) containg validations like a Captcha, or security code, secret questions, etc. After the user writes the captcha text or the necessary validations (it implies postbacks inside the WUC), the WUC should continue with the execution of the fired event from the botton which opened the RadWindow.
How can I do this? Any idea? Is it possible?
When you call your RadWindow, make sure the set the OnClientClose event. If you are creating your RadWindow from code-behind:
RadWindow newWindow = new RadWindow();
newWindow.OnClientClose = "onRadWindowClosed";
...
If you are opening your RadWindow through javascript, you can use the add_close() method:
...
getRadWindow().add_close('onRadWindowClosed');
...
In either case, you need to create a new event handler script on your calling page for the OnClientClose event:
function onRadWindowClosed(sender, eventArgs) {
var returnValue = eventArgs.get_argument();
if (returnValue != null) {
if (returnValue == "continue") {
// Continue doing work
}
else {
// Throw an error
}
}
}
On your WUC, in the btnContinue click event:
protected void btnContinue_Click(object sender, EventArgs e)
{
Page.ClientScript.RegisterClientScriptBlock(GetType(), "closeScript", "getRadWindow().close('continue');", true);
}
This function is used on both pages:
function getRadWindow() {
var oWindow = null;
if (window.radWindow)
oWindow = window.radWindow;
else if (window.frameElement.radWindow)
oWindow = window.frameElement.radWindow;
return oWindow;
}
UPDATE TO THE EXISTING ANSWER
On your calling page, add a function to get the RadAjaxManager (assuming you already have on the page. If not, you'll need one):
function get_ajaxManager() {
return $find("<%= Telerik.Web.UI.RadAjaxManager.GetCurrent(this.Page).ClientID %>");
}
Modify your OnClosed javascript function (from the calling page):
function onRadWindowClosed(sender, eventArgs) {
var returnValue = eventArgs.get_argument();
if (returnValue != null) {
if (returnValue == "continue") {
// This call will invoke a server side event
get_ajaxManager().ajaxRequest("continue~");
}
}
}
In your code-behind, handle the server-side event that gets called:
protected void RadAjaxManager1_Request(object source, Telerik.Web.UI.AjaxRequestEventArgs e)
{
try
{
if (e.Argument.Trim().Length == 0)
{
// Show a message when debugging, otherwise return
return;
}
string argument = (e.Argument);
String[] stringArray = argument.Split('~');
switch (stringArray[0])
{
case "continue":
// Continue performing your action or call a specific method
ServerSideMethodCall();
break;
}
}
catch (Exception ex)
{
RadAjaxManager.GetCurrent(this.Page).Alert("Unable to complete operation at this time: " + ex.Message);
}
}
As previously mentioned, you'll need a RadAjaxManager on the page if you don't already have one, and you'll need to tie the AjaxRequest handler to it.
<telerik:RadAjaxManager runat="server" ID="RadAjaxManager1" OnAjaxRequest="RadAjaxManager1_Request"></telerik:RadAjaxManager>
Sorry for the long-winded answer. Let me know if that gets you what you need.

WebBrowser problem infinite loading

I have one method. You can even test it:
private readonly
public HtmlDocument Browse()
{
var _browser = new WebBrowser();
var link = "http://hotline.ua/sr/?q=allo";
var loadFinished = false;
_browser.DocumentCompleted += delegate { loadFinished = true; };
try
{
_browser.Navigate(link);
}
catch (Exception xx)
{
if (!(xx is UriFormatException))
throw;
loadFinished = false;
}
while ( ! loadFinished )
Thread.Sleep(50);
loadFinished = false;
return _browser.Document;
}
Then somewhere in code I call this method:
var doc = Browse();
Instead of getting a HtmlDocument I get to the infinite loop in :
while ( ! loadFinished )
Thread.Sleep(50);
It seems that DocumentCompleted is never fired. But in Web browser I can easily to get this page.
Anybody knows why ? And what should I do to get a HtmlDocument ?
Oh sorry. I found a solution, after I post this topic:
That the reason why this event not fired:
I should not use
Thread.Sleep(50);
Because the event fired earlier and freed resourses. So thread get killed before it hits the document completed method.
If I change for:
Application.DoEvents();
It becomes work perfectly.
Thanks for you replies also !
I don't think that your delegate is correctly placed. You are not even taking the parameteres that this delegate needs. Take a look at:
How to use WebBrowser control DocumentCompleted event in C#?
You probably have exception which is not UriFormatException so the
loadFinished is always false. Then DocumentComplete cannot be reached.
Put a break point in Catch in this line: loadFinished = false; and check what exception is thrown.

Callback function?

I need to callback Javascript function in my code, but not firing. I am providing details what I am doing?.
I have input button in the page that calling javascript function. There I am loading another ProfilePic.aspx page. ProfilePic.aspx has FileUpload, OK and cancle button
<input type=button value="Change Image" onclick="javascript:SelectUserImage()" />
Javascript functions are
<script type="text/javascript">
function SelectUserImageCallback(ret) {
var imgId = 'ctl00_PlaceHolderMain_prof_imgUser';
var clearId = 'ctl00_PlaceHolderMain_prof_hidImageURL';
if (ret) {
if (ret == '__RESET__') {
document.getElementById(imgId).src = '\u002f_layouts\u002fimages\u002fno_pic.gif';
document.getElementById('ctl00_PlaceHolderMain_prof_hidImageURL').value = '';
document.getElementById(clearId).style.display = 'none';
}
else {
document.getElementById(imgId).onload = 'imgResizeMax(\'ctl00_PlaceHolderMain_prof_imgUser\', 100);imgResizeTbl(\'ctl00_PlaceHolderMain_prof_imgUser\');';
document.getElementById(imgId).src = ret;
document.getElementById('ctl00_PlaceHolderMain_prof_hidImageURL').value = ret;
setTimeout('imgResizeMax(\'ctl00_PlaceHolderMain_prof_imgUser\', 100);imgResizeTbl(\'ctl00_PlaceHolderMain_prof_imgUser\');', 1);
setTimeout('imgResizeMax(\'ctl00_PlaceHolderMain_prof_imgUser\', 100);imgResizeTbl(\'ctl00_PlaceHolderMain_prof_imgUser\');', 100);
document.getElementById(clearId).style.display = '';
}
}
}
function SelectUserImage() {
var href = '\u002f_layouts\u002fProfilePic.aspx';
var features = 'resizable: yes; status: no; scroll: no; help: no; center: yes; dialogWidth: 460px; dialogHeight: 140px; width:460;height:240;menubar:no;directories:no;location:no;';
commonShowModalDialog(href, features, SelectUserImageCallback, null);
}
In the ProfilePic.aspx page once user click OK buttong. I am upload his pic with some logic then I am closing window with javascript
protected void btnOK_Click(Object sender, EventArgs e)
{
try
{
// My logic Here
Debug.WriteLine("Shared Pictures Save Ends: " + DateTime.Now);
Response.Write ("<script language =javascript>close();</script>");
Response.End();
}
catch (Exception exception)
{
LogMessage(exception.Message, EventLogEntryType.Error);
if (exception.Message.ToLower().Contains("blocked"))
errorDisplay.Text = "* This type of file has been blocked by the administrator, please try a different file.";
else
{
errorDisplay.Text = exception.Message;
}
}
}
My Question: I am able to close the window but, What ever I need to call callback function `SelectUserImageCallback' not firing. I need to call this method after OK button part execution done.
Are you closing the window before the callback executes? I've done that before. As an experiment, try commenting out the code that closes the window.
You may have to restructure your code so that the callback function closes the window when it's finished whatever it's doing.
Update: Sorry, I misunderstood the question. There was a lot of code and I didn't read it all. I thought the call back was in the dialog page, but it looks like it's in the main page. I'm not familiar with commonShowModalDialog(), but it looks like it may have something to do with SharePoint. Do you have any documentation on that method? I found this discussion that makes it look like there's a special way to return a value from the dialog box. It may be that your callback isn't being called because you're not closing the window the right way. (That's a total guess on my part.)
Good luck.

Categories