2009-11-25 2 views
1

Моя цель:
Пользователь должен иметь возможность получить источник 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

Основная идея была бы разделить функции на две части, помещая код, который должен быть запущен после того, как событие происходит в анонимном делегате:

Вместо

void doStuff() { 
    firstPart(); 
    waitEvent(); 
    secondPart(); 
} 

вы

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

Единственная альтернатива, которую я знаю, - это перевести всю обработку во вторую нить и использовать Monitor.Wait и Monitor.Pulse, чтобы рассказать второй поток, когда он y продолжить. Это создаст дополнительные накладные расходы и, как правило, будет медленнее. Это может быть хорошей идеей, если вам нужно подождать 10 событий и продолжить, когда придет последний (и вы не знаете порядок), но не в том простом случае, о котором вы говорите выше.

+0

Ваше первое решение не будет работать, так как код еще продолжается, хотя я зарегистрирован на мероприятии. Первый класс нуждается в ответе через вызов второго класса, и поскольку я не знаю, когда это возвращаемое значение будет готово, я не могу вернуть что-то, кроме события для вызывающего, которое готово (если я хочу вернуть ценность в то время, я опаздываю). Вызывающий хочет получить ответ, и ему все равно, ждать ли он. – CappaMontana

+0

Если вы действительно хотите заблокировать функцию, пока не получите ответ, вам нужно будет убедиться, что все ваши вызывающие абоненты не вызовут вашу функцию из диспетчера событий (например, в вашем примере button_click) напрямую. Пока выполняется обработчик кнопки, центральная очередь сообщений процесса блокируется, поэтому никакие другие события пользовательского интерфейса не могут быть созданы. не можете ли вы предоставить событие своему абоненту? – mihi

+0

Да, я думал об этом, без особого успеха. Но однажды удалив код из диспетчера в form1 и form2 (просто сделал цикл while, который проверяет наличие некоторых данных вместо события documentLoaded), все сработало нормально! Tnx! – CappaMontana

Смежные вопросы