Страницы

Поиск по вопросам

вторник, 12 марта 2019 г.

Как получить событие LoadCompleted контрола WebBrowser, не вставляя данный контрол в XAML?

У меня нет потребности использовать WebBrowser в XAML, мне он нужен всего лишь для того что бы иметь возможность вызвать событие Click на одном из тегов загруженной HTML страницы.
Я не могу вызывать событие Click на нужном мне теге, до того как загрузиться страница. Для того что бы получить уведомление о загрузке страницы мне нужно подписаться на событие LoadCompleted. Но, судя из того что я вижу событие LoadCompleted не генерируется пока WebBrowser не будет добавлен в XAML.
По причине того что я хочу осуществлять загрузку множества HTML страниц параллельно, перспектива постоянно добавлять WebBrowser контрол в разметку XAML до загрузки одной из множества страниц и удаление его из этой разметки после загрузки нужной страницы выглядит не очень удобно.
Моя цель получить доступ к вызову события Click на загруженной HTML странице без возни с ХАМL.
Можно ли получить доступ к событию LoadCompleted контрола WebBrowser без его привязки к XAML. Возможно ли это? Может быть есть какой то другой путь?
Код отражающий суть проблемы (WebBrowser в 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 или не добавлен программным путем в качестве // дочернего компонента к одному из существующих контролов окна. } }


Ответ

Действительно, событие LoadCompleted для WPF WebBrowser не вызывается до тех пор, пока он не добавлен в какое-либо отображаемое окно или элемент управления, это известная проблема. Причина ее в том, как WebBrowser осуществляет инициализацию своего внутреннего ActiveX-элемента Internet Explorer, который и занимается загрузкой страницы. Это можно понять, посмотрев в его исходный код на Reference Source.
Для корректной работы события DocumentCompleted внутреннего ActiveX-объекта класс ActiveXHost должен быть переведен в состояние ActiveXState.InPlaceActive. По умолчанию он находится в ActiveXState.Running (см. инициализацию свойства), переход в состояние InPlaceActive происходит при вызове метода BuildWindowCore, который вызывается только при отображении родительского окна элемента. Это поведение отличается от аналогичного элемента в WinForms, который сразу переводится в InPlaceActive при инициализации, см. здесь)
Решение 1 - Использование WinForms WebBrowser
Элемент WebBrowser в WinForms избавлен от этой проблемы (аналогичное событие DocumentCompleted всегда вызывается), и функционально ни в чем не уступает элементу из WPF. Можно просто добавить ссылку на System.Windows.Forms и использовать его.
Решение 2 - Использование окна за пределами экрана
Для вызова BuildWindowCore требуется видимое окно, однако никто не запрещает сделать координаты отрицательными и поместить окно за пределы экрана:
Window wnd = new Window(); wnd.Content = browser; wnd.ShowInTaskbar = false; wnd.Top = -1000; wnd.Left = -1000; wnd.Width = 0; wnd.Height = 0; wnd.Show();
Решение 3 - Грязный хак с использованием отражения
Просто вызвать внутренний метод TransitionUpTo, передав в качества аргумента константу InPlaceActive (4):
using System.Reflection; const int InPlaceActive = 4;
//...
var method = browser.GetType().GetMethod("TransitionUpTo",BindingFlags.NonPublic | BindingFlags.Instance); method.Invoke(browser, new object[] {InPlaceActive});

Комментариев нет:

Отправить комментарий