【问题标题】:Open multiple pages in WebBrowser and send a command to all of them在 WebBrowser 中打开多个页面并向所有页面发送命令
【发布时间】:2015-03-10 20:52:29
【问题描述】:

我有一个具有以下功能的 winform 应用程序:

  1. 有一个多行文本框,每行包含一个 URL - 大约 30 个 URL(每个 URL 不同但网页相同(只是域不同);
  2. 我有另一个文本框,我可以在其中编写一个命令和一个按钮,该按钮将该命令发送到来自网页的输入字段
  3. 我有一个 WebBrowser 控制器(我想在 一个 控制器中完成所有事情)

该网页由一个文本框和一个按钮组成,我在该文本框中插入命令后希望单击该按钮。

到目前为止我的代码:

//get path for the text file to import the URLs to my textbox to see them
private void button1_Click(object sender, EventArgs e)
{
    OpenFileDialog fbd1 = new OpenFileDialog();
    fbd1.Title = "Open Dictionary(only .txt)";
    fbd1.Filter = "TXT files|*.txt";
    fbd1.InitialDirectory = @"M:\";
    if (fbd1.ShowDialog(this) == DialogResult.OK)
        path = fbd1.FileName;
}

//import the content of .txt to my textbox
private void button2_Click(object sender, EventArgs e)
{
    textBox1.Lines = File.ReadAllLines(path);
}

//click the button from webpage
private void button3_Click(object sender, EventArgs e)
{
    this.webBrowser1.Document.GetElementById("_act").InvokeMember("click");
}

//parse the value of the textbox and press the button from the webpage
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    newValue = textBox2.Text;
    HtmlDocument doc = this.webBrowser1.Document;
    doc.GetElementById("_cmd").SetAttribute("Value", newValue);     
}

现在,我如何将我的文本框中的所有这 30 个 URL 添加到同一个 web 控制器中,以便我可以将相同的命令发送到所有网页的所有文本框,然后按下所有文本框的按钮?


//编辑1 所以,我调整了@Setsu 方法并创建了以下内容:

public IEnumerable<string> GetUrlList()
{
    string f = File.ReadAllText(path); ;
    List<string> lines = new List<string>();

    using (StreamReader r = new StreamReader(f))
    {
        string line;
        while ((line = r.ReadLine()) != null)
            lines.Add(line);
    }
    return lines;
}

现在,这是返回它应该返回的内容,以便解析每个 URL 吗?

【问题讨论】:

  • 我已经编辑了你的标题。请参阅“Should questions include “tags” in their titles?”,其中的共识是“不,他们不应该”。
  • 您不能,因为单个 WebBrowser 对象一次只能浏览一个页面。您要么必须同时拥有 30 个 Web 浏览器,要么一次完成一页。请注意,在您的代码中,您永远不会使用您的一个 WebBrowser 导航到任何页面。另请注意,您的 DocumentCompleted 事件未正确实现;您需要检查整个页面何时完全完成,因为此事件因框架而多次触发(请参阅此answer)。
  • @Setsu - 所以,我可以通过一次一页做我需要的事情来实现这一点。所以,我将有一个for(){} 循环,它将从文本框中获取每个链接,然后做我需要的,对吧?我在else{} 声明之后这样做:if (e.Url.AbsolutePath != (sender as WebBrowser).Url.AbsolutePath) return; ?
  • @Alexander 我将为您输入更详细的答案。作为旁注,您在那里的代码行可以独立存在而无需 else,因为如果条件为真,则执行将退出事件。这很好,否则您的业务逻辑将被封装在一个巨大的 else 语句中。
  • 对于您的GetUrlList 实现,您的最后一行只需为return lines;。请注意,每个网址都需要格式正确;即具有必要的协议(例如 http://)。

标签: c# winforms visual-studio


【解决方案1】:

如果您只想继续使用 1 个 WebBrowser 控件,则必须按顺序导航到每个 URL。但是请注意,WebBrowser 类的Navigate 方法是异步的,因此您不能天真地循环调用它。最好的办法是实现这个答案here 中详述的异步/等待模式。

或者,您可以拥有 30 个 WebBrowser 控件并让每个控件自行导航;这大致相当于在现代浏览器中打开了 30 个选项卡。由于每个 WebBrowser 都在做相同的工作,您可以只编写 1 个 DocumentCompleted 事件来处理单个 WebBrowser,然后将其他 WebBrowser 连接到同一事件。请注意,WebBrowser 控件有一个错误会导致它逐渐泄漏内存,解决此问题的唯一方法是重新启动应用程序。因此,我建议使用 async/await 解决方案。

更新:

这是一个简短的代码示例,说明如何使用 30 个 WebBrowsers 方式(未经测试,因为我现在无法访问 VS):

List<WebBrowser> myBrowsers = new List<WebBrowser>();

public void btnDoWork(object sender, EventArgs e)
{
    //This method starts navigation.
    //It will call a helper function that gives us a list 
    //of URLs to work with, and naively create as many
    //WebBrowsers as necessary to navigate all of them

    IEnumerable<string> urlList = GetUrlList();
    //note: be sure to sanitize the URLs in this method call

    foreach (string url in urlList)
    {
        WebBrowser browser = new WebBrowser();
        browser.DocumentCompleted += webBrowserDocumentCompleted;
        browser.Navigate(url);
        myBrowsers.Add(browser);
    }
}

private void webBrowserDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    //check that the full document is finished
    if (e.Url.AbsolutePath != (sender as WebBrowser).Url.AbsolutePath) 
        return;

    //get our browser reference
    WebBrowser browser = sender as WebBrowser;

    //get the string command from form TextBox
    string command = textBox2.Text;

    //enter the command string
    browser.Document.GetElementById("_cmd").SetAttribute("Value", command);

    //invoke click
    browser.Document.GetElementById("_act").InvokeMember("click");

    //detach the event handler from the browser
    //note: necessary to stop endlessly setting strings and clicking buttons
    browser.DocumentCompleted -= webBrowserDocumentCompleted;
    //attach second DocumentCompleted event handler to destroy browser
    browser.DocumentCompleted += webBrowserDestroyOnCompletion;
}

private void webBrowserDestroyOnCompletion(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    //check that the full document is finished
    if (e.Url.AbsolutePath != (sender as WebBrowser).Url.AbsolutePath) 
        return;

    //I just destroy the WebBrowser, but you might want to do something
    //with the newly navigated page

    WebBrowser browser = sender as WebBrowser;
    browser.Dispose();
    myBrowsers.Remove(browser);
}

【讨论】:

  • 谢谢@Setsu 的解释。我会尝试用第二种方法来做,因为它似乎更容易一些,而且我没有那么有经验。无论如何,如果您有时间,并且可以构建一个适合我的代码的 sn-p,请告诉我。
  • 我还有一个问题:如何从 webbrowser 控制器的应用程序文本框中解析字符串?我问这个是因为当我启动应用程序并且我必须输入字符串时,webbrowser 控制器已经加载。
  • @Alexander 我不太确定我明白你在问什么。您是在谈论表单应用程序上的 TextBox 控件还是网页中的 html 文本框元素?
  • 我说的是我的表单中的一个文本框,我正在其中编写命令(例如:apple)。在我在文本框中输入该文本并按下按钮(也来自我的表单)后,文本应在网页文本框中以doc.GetElementById("_cmd").SetAttribute("Value", newValue); 调用。问题是newValue。当我第一次运行我的应用程序时,文本框最初是空的,所以 newValue = null 这不是我想要的。我要newValue = textBox1.Text.@Setsu
  • 我得到:webBrowserDocumentCompleted does not exist in the current contextGetUrlList() does not exist in the current context
猜你喜欢
  • 2013-05-12
  • 2014-08-29
  • 1970-01-01
  • 2021-06-26
  • 1970-01-01
  • 2015-08-05
  • 1970-01-01
  • 2010-09-12
  • 2015-10-23
相关资源
最近更新 更多