c# – How to get the LoadCompleted event of the WebBrowser control without inserting the given control into XAML?

Question:

I don't need to use the WebBrowser in XAML, I just need it to be able to trigger the Click event on one of the tags of the loaded HTML page.

I can't trigger the Click event on the tag I want before the page has loaded. In order to receive a page load notification, I need to subscribe to the LoadCompleted event. But from what I can see, the LoadCompleted event is not fired until the WebBrowser is added to the XAML.

Because I want to load many HTML pages in parallel, the prospect of constantly adding a WebBrowser control to the XAML markup before one of the many pages is loaded, and removing it from that markup after the desired page is loaded is not very convenient.

My goal is to access the Click event call on the loaded HTML page without fiddling with XAML.

Is it possible to access the LoadCompleted event of the WebBrowser control without binding it to XAML. Is it possible? Maybe there is some other way?

Code reflecting the essence of the problem (WebBrowser was not added to XAML)

public partial class SettingsWindow : Window
{
    private WebBrowser browser = new WebBrowser();

    public SettingsWindow()
    {
        InitializeComponent();

        this.browser.LoadCompleted += Browser_LoadCompleted;
        this.browser.Source         = new Uri(@"https://www.google.com.ua/");
        //this.browser.Navigate(path); <-- пробовал загружать страницу и этим способом.
    }

    private void Browser_LoadCompleted(object sender, NavigationEventArgs e)
    {
        // Этот обработчик никогда не вызывается если объект WebBrowser не 
        // добавлен в XAML или не добавлен программным путем в качестве 
        // дочернего компонента к одному из существующих контролов окна.
    }        
}

Answer:

Indeed, the LoadCompleted event for the WPF WebBrowser is not fired until it is added to some rendered window or control, this is a known issue. The reason for this is how the WebBrowser initializes its internal Internet Explorer ActiveX control, which is responsible for loading the page. This can be understood by looking at its source code at the Reference Source.

For the DocumentCompleted event of an internal ActiveX object to work correctly, the ActiveXHost class must be set to the ActiveXState.InPlaceActive state. By default, it is in ActiveXState.Running (see property initialization ), transition to the InPlaceActive state occurs when the BuildWindowCore method is called, which is called only when the element's parent window is shown. This behavior differs from the similar element in WinForms, which is immediately promoted to InPlaceActive upon initialization, see here ) .

Solution 1 – Using WinForms WebBrowser

The WebBrowser element in WinForms is free from this problem (the analogous DocumentCompleted event is always raised), and is functionally as good as the WPF element. You can just add a reference to System.Windows.Forms and use it.

Solution 2 – Using an off-screen window

The call to BuildWindowCore requires a visible window, but it's okay to make the coordinates negative and put the window off the screen:

Window wnd = new Window();
wnd.Content = browser;
wnd.ShowInTaskbar = false;
wnd.Top = -1000;
wnd.Left = -1000;
wnd.Width = 0;
wnd.Height = 0;
wnd.Show(); 

Solution 3 – Dirty hack using reflection

Just call the internal method TransitionUpTo , passing the constant InPlaceActive (4) as an argument:

using System.Reflection;
const int InPlaceActive = 4;

//...

var method = browser.GetType().GetMethod("TransitionUpTo",BindingFlags.NonPublic | BindingFlags.Instance);
method.Invoke(browser, new object[] {InPlaceActive});
Scroll to Top