БлогNot. C# Windows Forms: встроенный браузер без падений

C# Windows Forms: встроенный браузер без падений

Суть проблемы состоит в том, что браузерная компонента WebBrowser позволяет работать с содержимым страницы только после полной загрузки её DOM. Многие начинающие разработчики не учитывают этого и их программы с встроенным браузером "падают" немедленно после попытки получить элемент со страницы:

WebBrowser w = new WebBrowser();
w.Url = new Uri("https://google.com");
var e = w.Document.GetElementById ("elementId");

Скорее всего, здесь w.Document равен null, потому что переход браузера на новую страницу выполняется асинхронно, а строчка три сработает сразу после второй без ожидания загрузки страницы. Можно сделать вот так:

WebBrowser w = new WebBrowser();
public Form1() {
 InitializeComponent();
 w.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler (w_DocumentCompleted);
 w.Url = new Uri("https://google.com");
}
void w_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
 var e = w.Document.GetElementById("elementId");
}

а ещё лучше применить вспомогательную функцию OpenUri (см. в коде), которую можно вызывать, если при старте программы требуется загрузка web-страницы.

Создав проект C# Windows Forms, добавим на форму компоненты WebBrowser и TextBox с именами по умолчанию. Для реализации как автоматического сбора информации со страницы, загружаемой при старте приложения, так и перехода на страницу, адрес которой введён в TextBox, нам понадобилось запрограммировать только следующие события:

  • Form1 (конструктор формы) - вызываем OpenUri и разбираем другой вспомогательной функцией PrintDom структуру документа на заданную глубину (в демо-целях выводится окно сообщения MessageBox). Также показано, как после вызова OpenUri извлечь отдельный элемент;
  • textBox1_KeyDown - по нажатию Enter в текстовом поле пытаемся минимально грамотно проверить, является строка в TextBox адресом и затем переходим на неё;
  • textBox1_Click - по клику в текстовом поле выделяем его содержимое, чтобы сразу можно было вводить новый адрес.
Вспомогательные методы:
  • OpenUri - ожидание загрузки страницы с заданным адресом. Кстати, в теле функции есть ответ на ещё один частый вопрос - "Как убрать сообщения об ошибках сценария при работе с WebBrowser?";
  • PrintDom - cканируем DOM на заданную глубину depth и собираем всю информацию в строку.

Вот полный исходник файла Form1.cs и проект для скачивания.

using System;
using System.Windows.Forms;

namespace MiniBrowser {
 public partial class Form1 : Form {
  public Form1 () {
   InitializeComponent ();
   OpenUri (textBox1.Text); //Ждём загрузки страницы!
   //Теперь можно получить элементы со страницы:
   HtmlDocument doc = webBrowser1.Document;
   if (doc != null) {
    HtmlElementCollection elemColl = doc.GetElementsByTagName ("HTML");
    String str = PrintDom (elemColl, new System.Text.StringBuilder (), 1);
    MessageBox.Show ("DOM=" + str, "Тест Google", MessageBoxButtons.OK);
   }
   //Или конкретный элемент:
   /*
   HtmlElement el = webBrowser1.Document.GetElementById ("elementId");
   if (el != null) {
    MessageBox.Show ("Элемент="+ el.ToString (), "Тест Google", MessageBoxButtons.OK);
   }
   */
  }
  private void textBox1_KeyDown (object sender, KeyEventArgs e) {
   if (e.KeyCode == Keys.Enter) {
    string url = textBox1.Text.Trim ();
    if (String.IsNullOrEmpty (url)) return;
    if (url.Equals ("about:blank")) return; //На пустое переходить не надо
    if (!url.StartsWith ("http://") &&
        !url.StartsWith ("https://")) { //Дописать протокол по умолчанию
     url = "http://" + url;
    }
    Uri test = new Uri (url);
    if (Uri.TryCreate (url, UriKind.RelativeOrAbsolute, out test)) {
     try {
      webBrowser1.Navigate (test);
     }
     catch (System.UriFormatException ex) {
      MessageBox.Show (ex.Message, "Ошибка", MessageBoxButtons.OK);
      return;
     }
    }
   }
  }
  private void textBox1_Click (object sender, EventArgs e) {
   textBox1.SelectAll ();
  }
  private string PrintDom (HtmlElementCollection elemColl, System.Text.StringBuilder returnStr, Int32 depth) {
   //Сканируем DOM на заданную глубину depth
   System.Text.StringBuilder str = new System.Text.StringBuilder ();
   foreach (HtmlElement elem in elemColl) {
    string elemName;
    elemName = elem.GetAttribute ("ID");
    if (elemName == null || elemName.Length == 0) {
     elemName = elem.GetAttribute ("name");
     if (elemName == null || elemName.Length == 0) {
      elemName = "<no name>";
     }
    }
    str.Append (' ', depth * 4);
    str.Append (elemName + ": " + elem.TagName + "(Level " + depth + ")");
    returnStr.AppendLine (str.ToString ());
    if (elem.CanHaveChildren) {
     PrintDom (elem.Children, returnStr, depth + 1);
    }
    str.Remove (0, str.Length);
   }
   return returnStr.ToString ();
  }
  private void OpenUri (string uri) {
   webBrowser1.ScriptErrorsSuppressed = true;
   //Убрать сообщения об ошибках сценария
   webBrowser1.Url = new Uri (uri);
   while (webBrowser1.ReadyState != WebBrowserReadyState.Complete) {
    Application.DoEvents ();
    //Выполняем другие события, пока страница не загрузилась 
   }
  }

 }
}

 Скачать проект C# Windows Forms "MiniBrowser" в архиве .zip, развернуть в новую папку (12 Кб)

Скриншот приложения в работе
Скриншот приложения в работе

07.11.2024, 15:02 [79 просмотров]


теги: c# программирование ошибка textprocessing браузеры интернет

К этой статье пока нет комментариев, Ваш будет первым