Okay, the goal is to attempt to detect when a web page has been updated, and display the updated page in a WebBrowser control. For instance, say that I know Amazon will make the Playstation 4 available today, but I don't know when. I put the address of the Amazon PS4 page in my form, and the program periodically launches a WebBrowser to that page and saves the HTML to a list of string. Next time around when it launches the browser, it will check the results against my list. If the list already contains this particular string, the WebBrowser is closed and we wait again using Thread.Sleep.
private void Launch()
{
while (blnWorking)
{
if (intWindowCount < intMaxCount)
{
ShowBrowserUri(GetBrowserUri());
}
if (intWait > 0)
Thread.Sleep(intWait);
}
}
delegate void ShowBrowserUriDelegate(Uri uri);
private void ShowBrowserUri(Uri uri)
{
if (InvokeRequired)
{
ShowBrowserUriDelegate del = new ShowBrowserUriDelegate(ShowBrowserUri);
Invoke(del, uri);
}
else
{
CookieContainer cc = GetUriCookieContainer(uri);
CookieCollection cc1 = new CookieCollection();
if (cc != null)
{
cc1 = cc.GetCookies(uri);
}
THBrowser thb = new THBrowser(uri, cc1, this); //THBrowser is a form with a WebBrowser control
//THBrowser calls the Navigate method of its WebBrowser control
intWindowCount++;
thb.Show();
thb.SendToBack();
}
}
The problem is, my program seems to be randomly opening the website in Firefox (my default browser) instead of using the THBrowser/WebBrowser. I've stepped through, and it seems to occur every time I step out of the Thread.Sleep.
It also may be relevant that OnDocumentComplete for my WebBrowser compares the current HTML to my List of HTML strings and closes the control if the page contents are already contained in my list.
I don't have the most recent version in front of me, but that part goes something like...
void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (e.Url.AbsolutePath != (sender as WebBrowser).Url.AbsolutePath)
return;
if (ResultsList.Contains(this.webBrowser1.Document.Body.InnerHtml)
this.Close();
else
ResultsList.Add(this.webBrowser1.Document.Body.InnerHtml);
}
I think this may be relevant because the THBrowser opens briefly, disappears, then a new tab is immediately opened in Firefox.
I'm pretty sure the Thread.Sleep has something to do with it. Although, I had seen this problem occasionally before introducing the Thread.Sleep, it has multiplied 100 fold since.
Maybe Thread.Sleep isn't the best way to space out my intervals. Maybe I'm just going about this completely wrong. Any suggestions are appreciated.
First, it's highly unlikely that Thread.Sleep has anything to do with the page sometimes loading in Firefox rather than in the WebBrowser control.
Thread.Sleep is not typically the best way to do things periodically. You'd probably be better off with a timer. If you're using Windows Forms, that's System.Windows.Forms.Timer. Set the Interval to whatever delay you want, and have the Tick event update the browser.
I'm not sure why you're creating a new WebBrowser control every time. Seems you'd be better off creating it just once and then calling Refresh method.
Finally, do you really need a WebBrowser? You might be better off just using WebClient.DownloadString to download the HTML.
Related
I have a list view that is populated with the location of some html files on the local disk. When an the list is double clicked it takes the first selected item and loads that file location to the web browser control. I have fiddled around with async/await to try and get the UI free while the file loads to the web browser but it still hangs the UI.
private async void ListView1_DoubleClick(object sender, EventArgs e)
{
await LoadPage(listView1.SelectedItems[0].Text);
}
private async Task LoadPage(string uri)
{
Uri myURI = new Uri(uri);
await Task.Factory.StartNew(()=>webBrowser1.Navigate(myURI),CancellationToken.None,TaskCreationOptions.LongRunning,TaskScheduler.Default);
}
Code works, the UI is still hanging though.
Task.Factory.StartNew with TaskScheduler.Default runs your code in a thread-pool thread. This is not supposed to happen for code that manipulates UI elements, like the WebBrowser control. You could add the line bellow before Application.Run, to protect yourself from such mistakes.
Control.CheckForIllegalCrossThreadCalls = true;
Related link: Why are we allowed to modify the form title from a thread pool thread?
I don't think you can do much about the WebBrowser control slowing down your UI. Maybe hiding it initially, ans showing in again on DocumentCompleted?
I have a main form that opens a new form that contains a web browser control that navigates to the entered url when it is loaded. I've tried different things I've found and this is my latest code: I have a function (GoToURL) that is triggered at the Shown event of the form which is here:
public delegate void Launch();
private void launchBrowser()
{
webBrowser1.Navigate(GlobalData.URL);
}
private void GoToURL(object sender, EventArgs e)
{
webBrowser1.Invoke(new Launch(launchBrowser));
}
I have nothing in the Document Completed function:
void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
}
The web browser control loads the url just fine and I can scroll around in it, but on some links or buttons the control goes white and nothing happens. It won't proceed with the desired function. A more specific example is I am logging into a site and once I click the submit button it just hangs there. A "loading" image is presented and it just sits there. I know the Document Completed function above is triggered when this happens, too.
I apologize for my inexperience with C# and this is the first time using the web browser control (forms not wpf), so I am at a loss of what to try. I suspect its a threading issue, but that's as far as I got.
You are experiencing issues within the browser itself. Please launch IE and test the same there. I suspect you'll see the same issues.
Your experience is tied to the version of Internet Explorer you have in your system, and javascript errors have been frequents in the earlier versions of IE. Upgrade to IE11 in any case.
If that's not an option, try using a browser control with a different rendering engine. I'm too lazy to find and list the options, but I've used a few and they work well.
to avoid JavaScript Errors please follow this
public frmMain()
{
//Java Script Error popup block
webBrowser1.ScriptErrorsSuppressed = true;
}
Please Check this on loading Uri using
webBrowser1.Url = new Uri(Url.Trim().ToString());
Url - your desired Url
I have a web browser frame docked in a Silverlight application, and sometimes full-window XAML/Silverlight UI elements can pop up over it. The old problem, which I've more-or-less fixed, is that the web frame's contents didn't seem to play nice with the Silverlight content, always wanting to be rendered on top. This isn't really an issue now since I have an event that fires whenever an application-obscuring popup appears, but now there's a new problem.
My code that launches pop-ups looks like this:
public void OnModuleShown()
{
if (ModuleShown != null)
ModuleShown(this, new ModuleShownEventArgs());
}
public void ShowModule (string uri, string headerTitle, string message, string transition = "DefaultTransition")
{
// Security and destination validation
GetInfoFromURI(uri, out contentKey, out dataKey, out securityToken);
OnModuleShown();
ShowLoadingSpinner();
_loadModule(contentKey, dataKey, securityToken, headerTitle, message, transition);
}
My code that handles the event looks like this:
private void Shell_ModuleShown(object sender, ModuleShownEventArgs e)
{
if (browserFrame.Visibility == Visibility.Visible)
{
browserFrame.Visibility = Visibility.Collapsed;
return;
}
}
The new problem is that even though I call the event before I start loading and displaying the new module, and even though all the event is doing is changing a web frame's visibility, the module tends to appear first if the loading time is short. Since the module's appearance is animated, it looks even worse since the web frame seems to be waiting for the module to finish its animation before it vanishes.
Questions
Is there some kind of threading method I can use to address this? I really don't want to use Thread.Sleep but it's the only one I know of that would fix this without large program changes I can't make. Even better would be if there was a way to get this web frame to play along with Z-indexes or something similar.
I am using Visual Studio 2013, and my project's .NET Framework version is 4.0.
I'm working in .NET, C# to be specific, creating a Win Forms UserControl, which contains a WebBrowser control. The WebBrowser control hosts a page, which in turn uses a third-party javascript component. The problem I'm having is with invoking a javascript function to initialize the third-party javascript component and block the UI in the Windows Forms application until the component has been initialized, which the component notifies you of through an internal javascript event that it has.
Part of the problem is that the only way to change any configuration parameter of the third-party javascript component is to re-initialize it with the new configuration. So for example, if you want to make it read-only you have to re-initialize it with the read-only parameter.
I've got everything working in terms of being able to call the Document.InvokeScript and then in the web page call the UserControl method using window.external but the problem I'm having is how to block the UserControl code that makes the call to initialize the javascript component so that it waits and doesn't return control to the user until the initialization of the javascript component has been completed.
The reason I need it to work this way is because if I have a "Read-Only" checkbox on the form that changes the the ReadOnly property of the UserControl to control whether the javascript component shows the data as read-only and the user clicks that checkbox really quickly you will either get a javascript error or the checkbox will get out of sync with the actual read-only state of the javascript component. This seems to happen because the control hasn't re-initialized yet after it's configuration has changed and you're already trying to change it again.
I've spent hours and hours trying work out a way to make it work using everything from AutoResetEvent to Application.DoEvents and so on, but don't seem to be able to get it working.
The closest I've found is Invoke a script in WebBrowser, and wait for it to finish running (synchronized) but that uses features introduced in VS2012 (and I'm using VS2010) and I don't think it would work anyway as it's a bit different in that you're not waiting for a javascript event to fire.
Any help would be greatly appreciated.
The problem in the first place is the requirement to "block" the UI thread until some event has been fired. It's usually possible to re-factor the application to use asynchronous event handlers (with or without async/await), to yield execution control back to the message loop and avoid any blocking.
Now let's say, for some reason you cannot re-factor your code. In this case, you'd need a secondary modal message loop. You'd also need to disable the main UI while you're waiting for the event, to avoid nasty re-entrancy scenarios. The waiting itself should to be user-friendly (e.g., use the wait cursor or progress animation) and non-busy (avoid burning CPU cycles on a tight loop with DoEvents).
One way to do this is to use a modal dialog with a user-friendly message, which gets automatically dismissed when the desired JavaScript event/callback has occured. Here's a complete example:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WbTest
{
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IScripting))]
public partial class MainForm : Form, IScripting
{
WebBrowser _webBrowser;
Action _onScriptInitialized;
public MainForm()
{
InitializeComponent();
_webBrowser = new WebBrowser();
_webBrowser.Dock = DockStyle.Fill;
_webBrowser.ObjectForScripting = this;
this.Controls.Add(_webBrowser);
this.Shown += MainForm_Shown;
}
void MainForm_Shown(object sender, EventArgs e)
{
var dialog = new Form
{
Width = 100,
Height = 50,
StartPosition = FormStartPosition.CenterParent,
ShowIcon = false,
ShowInTaskbar = false,
ControlBox = false,
FormBorderStyle = FormBorderStyle.FixedSingle
};
dialog.Controls.Add(new Label { Text = "Please wait..." });
dialog.Load += (_, __) => _webBrowser.DocumentText =
"<script>setTimeout(function() { window.external.OnScriptInitialized}, 2000)</script>";
var canClose = false;
dialog.FormClosing += (_, args) =>
args.Cancel = !canClose;
_onScriptInitialized = () => { canClose = true; dialog.Close(); };
Application.UseWaitCursor = true;
try
{
dialog.ShowDialog();
}
finally
{
Application.UseWaitCursor = false;
}
MessageBox.Show("Initialized!");
}
// IScripting
public void OnScriptInitialized()
{
_onScriptInitialized();
}
}
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IScripting
{
void OnScriptInitialized();
}
}
Which looks like this:
Another option (a less user-friendly one) is to use something like WaitOneAndPump from here. You'd still need to take care about disabling the main UI and showing some kind of waiting feedback to the user.
Updated to address the comment. Is your WebBrowser actually a part of the UI and visible to the user? Should the user be able to interact with it? If so, you cannot use a secondary thread to execute JavaScript. You need to do it on the main thread and keep pumping messages, but WaitOne doesn't pump most of Windows messages (it only pumps a small fraction of them, related to COM). You might be able to use WaitOneAndPump which I mentioned above. You'd still need to disable the UI while waiting, to avoid re-entrancy.
Anyhow, that'd still be a kludge. You really shouldn't be blocking the execution just to keep the linear code flow. If you can't use async/await, you can always implement a simple state machine class and use callbacks to continue from where it was left. That's how it used to be before async/await.
I am creating a new Web Browser control in my windows forms project to print some formatted HTML. The user does not need to see the control since it is simply printing a specially formatted version of the page that they are on.
This is the process of printing I am currently using.
internal void PrintQuestion(SessionPart SessionPart)
{
WebBrowser wbForPrinting = new WebBrowser(); //<-- Undisposed local
wbForPrinting.Parent = this;
wbForPrinting.DocumentCompleted += wbForPrinting_DocumentCompleted;
wbForPrinting.DocumentText = string.Format(DOCUMENT_HTML, SessionPart.Session.Course.Product.Name, HtmlFormatter.GetPrintableQuestion(SessionPart));
}
void wbForPrinting_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
// Print the document now that it is fully loaded.
((WebBrowser)sender).ShowPrintPreviewDialog();
}
I can not dispose of the WebBrowser right after the show print preview dialog because that would destroy the object I am attempting to print. There does not seem to be an event or way of determining that the user is done with the WebBrowser (either by finishing the print, or canceling the preview). This is the last piece of this project and my time is running out, I do not want to leave an undisposed local.
have you tried this
void wbForPrinting_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
// Print the document now that it is fully loaded.
((WebBrowser)sender).ShowPrintPreviewDialog();
// Dispose the WebBrowser now that the task is complete.
((WebBrowser)sender).Dispose();
}
Printing with Web Browser Control and Disposing afterwards