C# Web browser control is not updating correctly - c#

I'm currently working on an app that technically interacts with an html page that uses dynamic content.
My problem is when I try to append data to the WBC the content isn't updating correctly.
namespace CheckList
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
.... code removed ....
private void button2_Click(object sender, EventArgs e)
{
if (textBox1.Text != null)
{
HtmlDocument doc = webBrowser1.Document;
HtmlElement row = doc.CreateElement("tr");
HtmlElement cell1 = doc.CreateElement("td");
HtmlElement cell2 = doc.CreateElement("td");
cell1.InnerHtml = "[X] " + textBox1.Text;
cell2.SetAttribute("class", "solved_2");
cell2.InnerHtml = "Unsolved";
row.AppendChild(cell1);
row.AppendChild(cell2);
doc.GetElementsByTagName("table")[0].AppendChild(row);
//doc.Write(doc.GetElementsByTagName("HTML")[0].OuterHtml);
webBrowser1.Document.Body.InnerHtml = doc.Body.InnerHtml;
}
}
}
}
What currently happens is, I click "add" it should add the html to the page and update and the javascript and what not should still load.
What happens is it adds the content, but the javascript doesn't work after I attempt to reload the content. The CSS stays in tact though, and the javascript isn't working after that point.
JS Source:
var showalert = true;
var file = "file:///C:/Users/Removed/Documents/Visual Studio 2010/Projects/CheckList/CheckList/bin/Release/";
initiate_instance();
function initiate_instance() {
//insert
$.get(file + "saved.html", function(data) {
//$("table#items").append("<tr><th width='70%'>Issue</th><th width='30%' class='right'>Solved</th></tr>");
$("table#items").html($("table#items").html() + data);
});
//change [X] into a link
$("table#items tr td").each(function() {
$(this).html($(this).html().replace("[X]", "<a onclick='return remove(this)' href='#'>[X]</a>"));
});
//change the css
$("table#items tr:odd").attr("class", "odd");
$("table#items tr td:eq(0)").attr("width", "70%");
$("table#items tr td:eq(1)").attr("width", "30%");
$("td.solved, td.solved_2").click(function() {
if($(this).attr("class") == "solved") {
$(this).attr("class", "solved_2");
$(this).text("Unsolved");
} else {
$(this).attr("class", "solved");
$(this).text("Solved");
}
if(showalert == true) {
alert("Remember, for these changes to keep effect please save before closing the program.");
showalert = false;
}
});
}
//delete rows
function remove(obj) {
if(showalert == true) {
alert("Remember, for these changes to keep effect please save before closing the program.");
showalert = false;
}
$(obj).parent().parent().remove();
return false;
}

TL;DR: Have you tried setting "AllowNavigation" to true?
If you need to prevent navigation, but still need to update the page, a method I've found that works requires:
Initializing the WebBrowser control's DocumentText property with empty HTML to initialize the internal objects (i.e.: Document, DomDocument, Document.Body, etc)
Allowing navigation and the revoking upon page completion (if needed)
Code:
namespace CheckList
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Initialize all the document objects
webBrowser1.DocumentText = #"<html></html>";
// Add the Document Completed event handler to turn off navigation
webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBrowser1_DocumentCompleted);
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// Load default information via LoadHtml(string html);
LoadHtml(#"<html><head></head><body>Text!<script type='text/javascript' language='javascript'>alert('Aha!');</script></body></html>");
}
private void LoadHtml(string html)
{
webBrowser1.AllowNavigation = true;
// This will trigger a Document Completed event
webBrowser1.DocumentText = html;
}
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
// Prevent further navigation
webBrowser1.AllowNavigation = false;
// Clean-up the handler if no longer needed
}
private void button2_Click(object sender, EventArgs e)
{
// Do your document building
LoadHtml(doc.Body.Parent.OuterHtml);
}
}
}
I've found doing it this way:
Prevents users from navigating until allowed
Allows execution of JavaScript (immediately before OnDocumentCompleted fires)

Related

Running a Webbrowser thread in a task

I have a program that runs and starts 2 long running tasks. One of the tasks is a web scraper in which I have to use the WebBrowser ActiveX control so that I can get the rendered page. In order to use the control I have to start a thread so that I can set the apartment state for the message loop. When I do this, the proogram works fine, or at least for the first page that is fetched. Subsequent pages/calls, the webbrowser times out and it's state seems to remain at "uninitialized". In tracing my code, I never see the "HandleDestroyed" fire for the WebClient.
What do I need to do to Properly Destroy the WebBrowser control and or my own class in order for it to be reused again.
public static string AXFetch(string url, string ua)
{
TestBrowser TB = new TestBrowser();
Thread th = new Thread(() => TB.MakeLiRequest(url,ua));
th.SetApartmentState(ApartmentState.STA);
th.Start();
th.Join(new TimeSpan(0, 0, 90)); //90 second timeout
SiteData = TB.DocumentText;
TB = null;
return SiteData;
}
class TestBrowser
{
public string DocumentText = "";
private bool DocCompleted = false;
public TestBrowser()
{
}
private void reset_fetch_status()
{
this.DocCompleted = false;
this.DocumentText = "";
}
public void MakeLiRequest(string url, string UA)
{
reset_fetch_status();
using (WebBrowser wb = new WebBrowser())
{
wb.Visible = false;
wb.AllowNavigation = true;
wb.ScriptErrorsSuppressed = true;
wb.DocumentCompleted += this.wb_DocumentCompleted;
wb.Navigate(url, "_self", null, "User-Agent: " + UA + "\r\n");
WaitForPage();
wb.Url = null;
wb.DocumentCompleted -= this.wb_DocumentCompleted;
}
}
private void HandleDestroyed(Object sender, EventArgs e)
{
//This never seems to fire, I don't knwo why
Logging.DoLog("You are in the Control.HandleDestroyed event.");
}
private bool WaitForPage()
{
int timer = 0;
while (this.DocCompleted == false)
{
Application.DoEvents();
System.Threading.Thread.Sleep(100);
++timer;
if (timer == (PageTimeOut * 10))
{
Console.WriteLine("WebBrowser Timeout has been reached");
Application.Exit();
return false;
}
}
return true;
}
private void wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
WebBrowser wb = (WebBrowser)sender;
if (wb.ReadyState == WebBrowserReadyState.Complete)
{
this.DocumentText = wb.DocumentText;
this.DocCompleted = true;
}
}
}
On handle destroyed will only be called by the parent form.
If you were to try to access from the webbrowser control you would get this error:
Error 1 Cannot access protected member
'System.Windows.Forms.Control.OnHandleDestroyed(System.EventArgs)' via a
qualifier of type 'System.Windows.Forms.WebBrowser'; the qualifier must be of type 'stackoverflowpost47036339.Form1' (or derived from it)
Also you are not hooking it up. But since you haven't given your web browser any parent form, It can't be called. This is how you would hook it up to the parent form.
form1.HandleDestroyed += Form1_HandleDestroyed;
}
void Form1_HandleDestroyed(object sender, EventArgs e)
{
}

NET webbrowser - get HTML element ID by clicking on image

I have a C# winform project with a webbrowser control. I'm loading an HTML page with images into the webbrowser. Each image has a different ID:
<img src="F:\Temp\file12948.jpg" id="12948" width="180px">
Is there a way to pass the ID into a variable when clicking on the image so I can use the ID in my code? The path to the image can also be used as I can extract the number from there.
I have already searched here there and everywhere for a solution but can't find anything related.
You can dynamically attach to image's onClick event.
public class TestForm : Form
{
WebBrowser _WebBrowser = null;
public TestForm()
{
_WebBrowser = new WebBrowser();
_WebBrowser.ScriptErrorsSuppressed = true;
_WebBrowser.Dock = DockStyle.Fill;
this.Controls.Add(_WebBrowser);
WebBrowserDocumentCompletedEventHandler Completed = null;
Completed = (s, e) =>
{
//add onclick event dynamically
foreach (var img in _WebBrowser.Document.GetElementsByTagName("img").OfType<HtmlElement>())
{
img.AttachEventHandler("onclick", (_, __) => OnClick(img));
}
_WebBrowser.DocumentCompleted -= Completed;
};
_WebBrowser.DocumentCompleted += Completed;
var imgurl = "https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png";
//_WebBrowser.Navigate("http://edition.cnn.com/2017/09/09/us/hurricane-irma-cuba-florida/index.html");
_WebBrowser.DocumentText = $"<html> <img src='{imgurl}' id=123 /> </html>";
}
void OnClick(HtmlElement img)
{
MessageBox.Show(img.GetAttribute("id"));
}
}
On simple way would be to use browser navigation. When clicking you can navigate to a special URL, then you handle the Navigating event and if the url is the special url you cancel the navigation and handle the data.
public MainWindow()
{
InitializeComponent();
br.NavigateToString(#"<img src=""F:\Temp\file12948.jpg"" id=""12948"" width=""180px"" >");
br.Navigating += this.Br_Navigating;
}
private void Br_Navigating(object sender, NavigatingCancelEventArgs e)
{
if(e.Uri.Host == "messages")
{
MessageBox.Show(e.Uri.Query);
e.Cancel = true;
}
}
This works if you have some control over the HTML. You could also set the URL from JS if you don't want to add the anchor.
Edit
The above version is for a WPF application. The winforms version is as follows:
public Form1()
{
InitializeComponent();
webBrowser1.DocumentText = #"<img src=""F:\Temp\file12948.jpg"" id=""12948"" width=""180px"" >";
webBrowser1.Navigating += this.webBrowser1_Navigating;
}
private void webBrowser1_Navigating(object sender, WebBrowserNavigatingEventArgs e)
{
if (e.Url.Host == "messages")
{
MessageBox.Show(e.Url.Query);
e.Cancel = true;
}
}

c# webbrowser navigate loop freezing form

I'm trying to display a website in a form and automatically reload the website to see if a certain text is present. I'm doing this in a C# form and try to call a while with an button. This is the code I made:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnStartLoop_Click(object sender, EventArgs e)
{
lblStatusLoop.Text = "Status loop: Started";
lblStatusLoop.ForeColor = System.Drawing.Color.Green;
int i = 0;
while (i < 10)
{
Browser.Navigate("www.google.com");
Browser.DocumentCompleted += Browser_DocumentCompleted;
Thread.Sleep(5000);
i++;
}
}
void Browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
string ZoekText = txtSearchbox.Text.ToString();
if (Browser.DocumentText.Contains(ZoekText))
{
lblSearchResult.Text = "Text found";
}
else
{
lblSearchResult.Text = "Text not found";
}
}
}
As soon as I push the button the appliction freezes and I have to force quit it. I've done some research on google and suggestions were to wait for the document to complete loading but it has no effect.
I don't have much programming experience and do not have someone to fall back on so I hope someone here can point me into right direction.

Is it possible to refresh a separate WebBrowser control on text change without losing focus on textbox?

I'm designing an article editor for my company and I'd like to be able to show a live preview of the article in a separate WebBrowser window/control. The WebBrowser control needs to refresh the page every time the user changes anything in one of the fields for the article.
Previously, I had the WebBrowser control on the same form, but for space reasons, I had to break it out onto a separate form and access it using a button on the editor form. However, since I moved that control into a separate form, the WebBrowser gains focus on every refresh, meaning I can type one character and then I have to click back to the textbox I was typing in.
My question: Is there a way to refresh that preview page in the background without it stealing the focus so that I can update the preview to reflect what the user is typing without interrupting the user while typing?
Here are the methods for showing and refreshing the preview, respectively:
private void buttonShowPreview_Click(object sender, EventArgs e)
{
if (buttonShowPreview.Tag == null)
{
Form browserForm = new Form();
browserForm.FormClosing += new FormClosingEventHandler(delegate(Object form, FormClosingEventArgs args)
{
if (args.CloseReason == CloseReason.UserClosing)
{
args.Cancel = true;
browserForm.Hide();
previewShowing = false;
}
});
browserForm.Size = new System.Drawing.Size(1024, 768);
browserForm.DesktopLocation = new System.Drawing.Point(0, 0);
browserForm.Text = "Article Preview";
preview = new WebBrowser();
browserForm.Controls.Add(preview);
preview.Dock = DockStyle.Fill;
preview.Navigate("about:blank");
buttonShowPreview.Tag = browserForm;
}
Form previewForm = buttonShowPreview.Tag as Form;
previewForm.Show();
previewShowing = true;
RefreshPreview();
}
private void RefreshPreview(string jumpToAnchor)
{
if (preview != null)
{
preview.Document.OpenNew(true);
preview.Document.Write(structuredContent.GetStructuredContentHTML(content, jumpToAnchor, false));
preview.Refresh();
}
}
Based on the answer by Robberechts here, try disabling the parent Form, updating your WebBrowser, then re-enabling the parent Form again in the DocumentCompleted() event:
private void buttonShowPreview_Click(object sender, EventArgs e)
{
if (buttonShowPreview.Tag == null)
{
Form browserForm = new Form();
browserForm.FormClosing += new FormClosingEventHandler(delegate(Object form, FormClosingEventArgs args)
{
if (args.CloseReason == CloseReason.UserClosing)
{
args.Cancel = true;
browserForm.Hide();
}
});
preview = new WebBrowser();
preview.DocumentCompleted += preview_DocumentCompleted; // handle the DocumentCompleted() event
browserForm.Controls.Add(preview);
preview.Dock = DockStyle.Fill;
preview.Navigate("about:blank");
buttonShowPreview.Tag = browserForm;
}
Form previewForm = buttonShowPreview.Tag as Form;
previewForm.Size = new System.Drawing.Size(1024, 768);
previewForm.DesktopLocation = new System.Drawing.Point(0, 0);
previewForm.Text = "Article Preview";
RefreshPreview();
previewForm.Show();
}
private void RefreshPreview(string jumpToAnchor)
{
if (preview != null && preview.Parent != null)
{
preview.Parent.Enabled = false; // disable parent form
preview.Document.OpenNew(true);
preview.Document.Write(structuredContent.GetStructuredContentHTML(content, jumpToAnchor, false));
preview.Refresh();
}
}
private void preview_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
WebBrowser wb = sender as WebBrowser;
if (wb.Parent != null)
{
wb.Parent.Enabled = true; // re-enable parent form
}
}

WebForm's WebBrowser consecutive navigation

VS 10, WinFormApp
I have a list of url's. I am trying to navigate to the URL's one by one. After completing the load of the document of a page, then the integrated webbrowser will navigate to the next URL.
Here is some code:
private void btnHit_Click(object sender, EventArgs e)
{
webBrowser1.Navigate(lstUrls[counter].url);
}
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (e.Url != webBrowser1.Url)
{
lblStatus.Text = "Page is loading....";
lblStatus.ForeColor = Color.Red;
return;
}
else
{
funcMethod();
}
}
public void funcMethod()
{
lblStatus.Text = "Page is loaded";
lblStatus.ForeColor = Color.Green;
try
{
webBrowser1.Document.Focus();
webBrowser1.Navigate(lstUrls[++counter].url);
}
catch { }
}
The problem is, when I debug for each element line by line, it works for all(100+) items in the List of URL's. but when I just press the btnHit, then sometimes it loads 2/3/4/5/.. number of urls. and stop loading further. In that time, If I hit the btnHit again, the process starts and same thing happens as before.
What am I missing?

Categories