等待函数内的事件

时间:2009-11-25 16:16:31

标签: c# events event-handling

我的目标:
用户应该能够在单击按钮时获取页面的html源。此事件使用geckoWebBrowser组件打开一个新表单,并导航到给定的URL。 完成此操作后,将触发documentCompleted事件。 然后我就可以开始加载DOM了。

问题:
虽然页面的加载需要时间,但我必须等到第二个表单类中加载DOM(或者只是div的值)。这正是问题所在!每个等待或循环都会卡住第二种形式(geckoBrowser),因此我无法获得该值。

这是第一种形式的代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace mozillaFirefox2
{
public partial class Form1 : Form
{

    string source = "";

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        //BackgroundWorker bw = new BackgroundWorker();
        //bw.DoWork += new DoWorkEventHandler(bw_DoWork);

        Browser br = new Browser();
        //object parameters = br;
        //bw.RunWorkerAsync(parameters);
        br.navigate("http://www.google.com/#hl=en&q=superman&aq=f&aqi=&oq=&fp=1&cad=b");

        Thread th = new Thread(delegate() { this.dw(br); });            
        th.Start();
        th.Join(2000);

        richTextBox1.AppendText(br.GetSource + "\n");            
    }

    private void dw(Browser br)
    {
        while (br.workDone == false)
        {
            //donothing
        }
        source = br.GetSource;
    }

    //void bw_DoWork(object sender, DoWorkEventArgs e)
    //{
    //    Browser br = (Browser)e.Argument;
    //    while (br.workDone == false)
    //    {
    //        //donothing
    //    }
    //    richTextBox1.AppendText(br.GetSource + "\n");
    //}
}
}

这是第二个:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace mozillaFirefox2
{
//Declare a delegate who points to a function / signature
public delegate void GotDataEventHandler(object sender, EventArgs e); 


class Browser : Form
{
    public event GotDataEventHandler GotData;

    private System.ComponentModel.IContainer components = null;

    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        this.geckoWebBrowser1 = new Skybound.Gecko.GeckoWebBrowser();
        this.SuspendLayout();

        // 
        // geckoWebBrowser1
        // 
        this.geckoWebBrowser1.Location = new System.Drawing.Point(14, 4);
        this.geckoWebBrowser1.Name = "geckoWebBrowser1";
        this.geckoWebBrowser1.Size = new System.Drawing.Size(261, 67);
        this.geckoWebBrowser1.TabIndex = 2;
        this.geckoWebBrowser1.DocumentCompleted += new EventHandler(geckoWebBrowser1_DocumentCompleted);
        //Never forget this. Otherwise this error is raised "Cannot call Navigate() before the window handle is created"
        this.geckoWebBrowser1.CreateControl();
        // 
        // Form1
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(788, 577);
        this.Controls.Add(this.geckoWebBrowser1);
        this.Name = "Form1";
        this.Text = "Form1";
        this.ResumeLayout(false);            

    }

    #endregion

    public Skybound.Gecko.GeckoWebBrowser geckoWebBrowser1;

    public string source = "";

    public bool workDone = false;

    public bool navCall = false;

    [STAThread]
    public Browser()
    {
        Skybound.Gecko.Xpcom.Initialize(@"C:\Program Files\Mozilla-Gecko ActiveX WebBrowserControl\xulrunner-1.9.1.2.en-US.win32\xulrunner");
        Skybound.Gecko.GeckoPreferences.User["general.useragent.override"] = "Mozilla/5.0 (Windows; U; Windows NT 6.0; nl; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729)";
        this.InitializeComponent();                        
        this.Show();
    }


    public delegate void NavigationHandler(string url);
    public void navigate(string url)
    {
        if (this.InvokeRequired)
        {
            object[] parameters = { url };
            this.geckoWebBrowser1.Invoke(new NavigationHandler(navigate), parameters);
        }
        else
        {
            navCall = true;
            this.geckoWebBrowser1.Navigate(url);
        }
    }

    public string GetSource
    {
        get { return source; }
    }

    public string getSource()
    {
        return source;
    }

    public void geckoWebBrowser1_DocumentCompleted(object sender, EventArgs e)
    {
        if(navCall == true){
            source = this.geckoWebBrowser1.Document.GetElementById("res").InnerHtml.ToString();
            workDone = true;
            navCall = false;

            //
            GotDataEventHandler gd;
            lock (this)
            {
                gd = GotData;
            }
            if (gd != null)gd(this, EventArgs.Empty);

        }
    }
}
}

现在我应该可以等到我在button1_Click函数中得到一个答案,或者在这个函数中捕获事件或者在第二个表单上捕获一个函数,这样我就可以返回它(代码将自行等待)。不知道该怎么做。

1 个答案:

答案 0 :(得分:1)

基本思路是将函数拆分为两部分,将事件发生后应该运行的代码放在匿名委托中:

而不是

void doStuff() { 
    firstPart();
    waitEvent();
    secondPart();
}
你做了

void doStuff() { 
    firstPart();
    some.Event += delegate {
        secondPart();
    }
}

我知道的唯一选择是将整个处理放入第二个线程并使用Monitor.Wait和Monitor.Pulse告诉第二个线程何时可以继续。但这会产生更多的开销,但通常会更慢。如果您需要等待10个事件并在最后一个事件到达时继续(并且您不知道订单),这可能是一个好主意,但不是在上面概述的那个简单情况下。

相关问题