which event is rised up in Internet Explorer (IE9) when the F5 key (refresh) is clicked? And how can I catch it with an Handler in my BHO?
Note:
I have created a BHO in C# for IE9. My class extend IObjectWithSite that allow me to add handlers through SetSite function.
public int SetSite(object site)
{
webBrowser = (SHDocVw.WebBrowser)site;
//events here...
}
If you are developing a browser plugin that injects Javascript, I found it useful to hook both ondocumentcomplete and ondownloadcomplete.
Ondocumentcomplete fires as soon as the DOM has been loaded and can be manipulated, but it misses refreshes.
Ondownloadcomplete waits until all resources (e.g., images) have downloaded, but catches refreshes. This delay can be quite long.
By hooking both, you get a responsive plugin most of the time, and you don't miss refreshes. Your javascript can then include a check to avoid running twice. Something like:
// Inject the code, but only once
if (typeof myplugin == 'undefined') {
myplugin = new function () {
// Your code runs here.
};
}
I found the following page to be informative:
Alternative way to detect refresh in a BHO
There is no direct method and it is hard to implement across different versions of IE. Although you can use combination of some events to achieve that. Be warned the following approaches are not fool proof.
Links:
MSDN Forum
Detecting the IE Refresh button
Refresh and DISPID_DOCUMENTCOMPLETE
Related
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 have a C# WinRT app that uses the WebView control. None of the events I have created event handlers for are firing:
Uri theUri = new Uri("http://bing.com/");
webViewVideoPlayer.NavigationStarting += webViewVideoPlayer_NavigationStarting;
webViewVideoPlayer.NavigationCompleted += webViewVideoPlayer_NavigationCompleted;
webViewVideoPlayer.ContentLoading += webViewVideoPlayer_ContentLoading;
webViewVideoPlayer.NavigationFailed += webViewVideoPlayer_NavigationFailed;
webBrowserVideoPlayer.Navigate(theUri);
I have set breakpoints on the first line in each of the event handler bodies. The web page renders fine, but none of the breakpoints are hit. What can I do to fix this?
The app is currently configured for Debug build, x86 platform.
[TO THE MODERATORS: This is not my best post obviously. I was tired and made a silly mistake. If you feel it is best to delete it, please do.]
You've got two different control names in your code. The events are attached to webViewVideoPlayer, yet later the Navigate is called on a different WebView called webBrowserVideoPlayer.
I did a test with a WebView and the events were called as expected.
I've tried the built in WPF WebBrowser (.NET ActiveX Wrapper), Awesomium.NET, and Chromium.NET.
I'm not sure if I just missed something here, but I don't know if there is any way to raise an event when the DOM changes so that my C# code can execute. I want to avoid having a timer check/compare the DOM for changes each time if I can...
So, I'm just wondering if I missed something in those controls that allows me to do what I want or if not, is there any alternatives/methods to have a DOMChanged event?
I don't believe there's any way to do that by default with those controls (though I've never used Chromium.NET).
One thing you could try doing is using JavaScript to detect DOM changes. You could have your application register a callback function and write the JavaScript itself. Here's a rough example in Awesomium syntax:
myControl.CreateObject("JSCallback");
myControl.SetObjectCallback("JSCallback", "handleDOMChange", OnDOMChange);
myControl.ExecuteJavascript(
//this is where you would write your Javascript method that would detect DOM change and have it execute this handler method
//Here's a post with some info on this (ignore cross-browser compatibility issues since it's all running within Awesomium): http://stackoverflow.com/questions/3219758/detect-changes-in-the-dom
function DOMChangeDetected ()
{
JSCallback.handleDOMChange();
}
);
private void OnDOMChange(object sender, JSCallbackEventArgs eventArgs)
{
//.NET event triggered from JS DOM change detection
}
I am using the Business Silverlight application. I have incorporated some MVVM into this and were off an running with it. We are using some telerik controls, mostly the ribbon control and the docking. We register all the telerik ribbon controls in the about.xaml.cs file, the method is DisplayUI - its here where we register the docking control then we register the ribbon after this. What happens is that when you click the ABOUT link it shows our first tab with buttons(perfect). when you click the HOME link next to the ABOUT link, we go back to the home page..but when you click the ABOUT link again it registers the controls again so we end up with two tabs that are the same.
Is there a way to check to see if this about.xaml.cs file has already been initialized? Im guessing that is has a handle on the first call in memory as I am able to see the first tabs rendering..
Thanks
here is the about code
public About()
{
InitializeComponent();
DisplayUI();
this.Title = ApplicationStrings.AboutPageTitle;
}
that display UI does all the work in registering the dockpanel and the ribbons. We'd like to not have the DisplayUI() called if this has already been rendered once.
If you do it by event handler can you unsubscribe from the event at the end of the method? Without seeing some code it's hard to work out what to change.
It's not the nicest way of doing it, but if this code needs to run once and only once then you could have a static boolean variable on the class set to false and when you call DisplayUI you check the value of this. If it's false you set it to true and run the method, and if it's true you just return.
I'm having a problem screenscraping some data from this website using the MSHTML COM component. I have a WebBrowser control on my WPF form.
The code where I retrieve the HMTL elements is in the WebBrowser LoadCompleted events. After I set the values of the data to the HTMLInputElement and call the click method on the HTMLInputButtonElement, it is refusing to submit the the request and display the next page.
I analyse the HTML for the onclick attribute on the button, it is actually calling a JavaScript function and it processes my request. Which makes me not sure if calling the JavaScript function is causing the problem? But funny enough when I take my code out of the LoadCompleted method and put it inside a button click event it actually takes me to the next page where as the LoadCompleted method didn't do. Doing that sort of thing defeats the point of trying to screenscrape the page automatically.
On another thought: when I had the code inside the LoadCompleted method, I'm thinking the HTMLInputButtonElement is not fully rendered on to the page which result in click event not firing, despite the fact when I looked at the object in run time it is actually held the submit button element there and the state is saying I completed which baffles me even more.
Here is the code I used inside the LoadCompleted method and the click method on the button:
private void browser_LoadCompleted(object sender, NavigationEventArgs e)
{
HTMLDocument dom = (HTMLDocument)browser.Document;
IHTMLElementCollection elementCollection = dom.getElementsByName("PCL_NO_FROM.PARCEL_RANGE.XTRACKING.1-1-1.");
HTMLInputElement inputBox = null;
if (elementCollection.length > 0)
{
foreach (HTMLInputElement element in elementCollection)
{
if (element.name.Equals("PCL_NO_FROM.PARCEL_RANGE.XTRACKING.1-1-1."))
{
inputBox = element;
}
}
}
inputBox.value = "Test";
elementCollection = dom.getElementsByName("SUBMIT.DUM_CONTROLS.XTRACKING.1-1.");
HTMLInputButtonElement submitButton = null;
if (elementCollection.length > 0)
{
foreach (HTMLInputButtonElement element in elementCollection)
{
if (element.name.Equals("SUBMIT.DUM_CONTROLS.XTRACKING.1-1."))
{
submitButton = element;
}
}
}
submitButton.click();
}
FYI: This is the URL of the web page I'm trying to access using MSHTML,
http://track.dhl.co.uk/tracking/wrd/run/wt_xtrack_pw.entrypoint.
There are many possibilities:
You may try to put your code at
other events, such as on Navigation
Completed, or on Download Completed.
You may need to explicitly evaluate the OnClick event after the click() function.
Using the MS WebBrowser control is
easier than using the MSHTML COM.
To make life easier, you may just use a webscraping library such as the IRobotSoft ActiveX control to automate your entire process.
Delay in OnBeforeNavigate can cause click actions to fail.
We have noticed that with some submit actions OnBeforeNavigate is called twice, especially where onClick is used. The first call is before the onClick action is performed, the second is after it is complete.
Turn off your BHO, put a breakpoint on onClick, step over the submit action return jsSubmit() and then wait a bit and you should be able to cause the same issue without your automation.
Any delay >150ms on the second call to OnBeforeNavigate causes some failure in page load/navigation to the result.
Edit:
Having tried our own automation of this DHL page we don't currently have an issue with the timing described above.