C# - 如何在一段时间内阻止方法返回?

时间:2010-08-23 20:51:26

标签: c# asynchronous timer webbrowser-control

我有一个包含Web浏览器控件的win表单应用程序。由于导航的异步性,我需要能够在Web浏览器上的操作之间添加延迟。

Document_Complete事件毫无价值,因为它没有考虑页面可能包含多个AJAX请求。这次事件多次发生。

更新

加载页面时会生成AJAX请求。因此,通过HTTP请求获取页面加载和某些DIV中的内容。因此,Document_Complete事件在文档首次加载时引发,然后在每个(AJAX)HTTP请求返回时引发。没有布埃诺。

UPDATE2

我的应用程序尝试从Webbrowser.Document对象中读取HtmlElements。因为代码执行速度比HTTP请求返回的速度快...文档对象不包含所有html元素。

我需要的是一些延迟主线程中方法调用的方法。我尝试过使用计时器:

private void startTimer()
        {
            timer.Interval = 2000;
            timer.Start();
            while (!BrowserIsReady)
            {
                //Wait for timer
            }
        }

这会锁定线程并且tick事件永远不会触发。这个循环永远不会结束。

我想运行一系列这样的方法:

Navagate("http://someurl.com");
//delay
ClickALink();
//delay
Navagate("Http://somewhere.com");
//delay

我可以使用计时器和BackgroundWorker解决此问题吗?有人可以建议一个可能的解决方案吗?

6 个答案:

答案 0 :(得分:1)

您可以考虑使用Threading命名空间中的Monitor类。示例如下。

using System;
using System.Threading;

namespace MonitorWait
{
    class Program
    {
        private static readonly object somelock = new object();

        static void Main(string[] args)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(SyncCall));

            Thread.Sleep(5000);  //Give the SyncCall a chance to run...
            //Thread.Sleep(6000);  //Uncomment to see it timeout.

            lock (somelock)
            {
                Monitor.Pulse(somelock);  //Tell the SyncCall it can wake up...
            }

            Thread.Sleep(1000);  //Pause the main thread so the other thread 
                                 //prints before this one :)

            Console.WriteLine("Press the any key...");
            Console.ReadKey();
        }

        private static void SyncCall(object o)
        {
            lock (somelock)
            {
                Console.WriteLine("Waiting with a 10 second timeout...");
                bool ret = Monitor.Wait(somelock, 10000);
                if (ret)
                {
                    Console.WriteLine("Pulsed...");
                }
                else
                {
                    Console.WriteLine("Timed out...");
                }
            }
        }
    }
}

答案 1 :(得分:0)

不幸的是,您必须致电Application.DoEvents()才能保持浏览器正常运行。除此之外,我还关注ReadyState属性。

  while (BusyNavigating || (wbBrowser.ReadyState != WebBrowserReadyState.Complete))
  {
    Application.DoEvents();
  } 

答案 2 :(得分:0)

Document_Complete 告诉您页面何时加载完毕。

AJAX只是让它能够加载其他东西。某些Web应用程序(如StackOveflow)在计时器上有AJAX调用,这意味着您的应用程序永远不会完全加载,因为总是等待进行/返回的AJAX调用。

这是一个问题,因为AJAX函数的回调可以简单地启动另一个AJAX函数。如果不解析整个页面(创建执行树),确实无法确定页面何时真正完全加载。

也许您可以明确说明应用程序 需要等待所有AJAX调用完成的原因以及为什么等待Document_Complete不够好的原因。

答案 3 :(得分:0)

我认为您可能遇到的问题之一(由于代码不完整而无法判断)是您在需要调用时尝试在控件上执行操作。

我编写了一个每5秒更改一次网址的小表单。请记住,不允许在后台线程上询问Web浏览器的状态。

  public partial class Form1 : Form
{
    System.Timers.Timer t = new System.Timers.Timer();
    Stack<string> urls = new Stack<string>();

    public Form1()
    {
        InitializeComponent();
        urls.Push("http://stackoverflow.com/");
        urls.Push("http://msdn.microsoft.com/");
        urls.Push("http://maps.google.com");


        t.AutoReset = false;
        t.Interval = 5000;
        t.Elapsed += new System.Timers.ElapsedEventHandler(t_Elapsed);

    }



    void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {

         if (urls.Count > 0 )
        {
             string url = urls.Pop();
             SetUrl(url);

         }



         t.Interval = 5000;
        t.Start();

    }

    static void SynchronizedInvoke(ISynchronizeInvoke sync, Action action)
    {
        // If the invoke is not required, then invoke here and get out.
        if (!sync.InvokeRequired)
        {
            // Execute action.
            action();

            // Get out.
            return;
        }

        // Marshal to the required thread.
        sync.Invoke(action, new object[] { });
    }

    private void SetUrl(string url)
    {
        SynchronizedInvoke(webBrowser1, () => webBrowser1.Navigate(url));
    }



    private void button1_Click(object sender, EventArgs e)
    {
        t.Interval = 1;
        t.Start();
    }




}

答案 4 :(得分:0)

感谢所有建议。这是我昨晚提出的“延迟”解决方案。感觉就像使用蒸汽压路机来破解花生,但我找不到更优雅的答案来解决这个问题。

我正在关闭一个调用名为“AddDelay”的方法的新工作线程。此方法将使工作线程休眠一段时间。我的主(UI)线程在Thread.IsAlive条件上循环,同时允许应用程序在线程未完成时接收OS消息。

这似乎可以解决问题。

 private void Delay()
        {
            DelayTimer dt = new DelayTimer(1);
            Thread thread = new Thread(new ThreadStart(dt.AddDelay));
            thread.Start();
            while (thread.IsAlive)
            {
                Application.DoEvents();
            }

        }




public class DelayTimer
    {
        private int _seconds;
        public DelayTimer(int time)
        {
            _seconds = time;
        }
        public void AddDelay()
        {
           Thread.Sleep(_seconds*1000);
        }
    }

答案 5 :(得分:0)

你不能简单地使用:

   System.Threading.Thread.Sleep(1000);

这肯定会产生同样的效果吗?