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

发布于 2024-09-16 00:45:18 字数 957 浏览 2 评论 0原文

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

Document_Complete 事件毫无价值,因为它没有考虑到一个页面可能包含多个 AJAX 请求。该事件通常会触发多次。

更新

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

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解决这个问题吗?有人可以提出一个可能的解决方案吗?

I have a win forms application that contains a Web Browser control. I need to be able to add delay between operations on the web-browser due to the asynchronous nature of navigating.

The Document_Complete event is worthless as it does not take into account that a page may contain multiple AJAX requests. The event ofter fires many times.

UPDATE

The AJAX Requests are made when the page is loaded. So the page loads and content in some DIV is fetched via an HTTP request. So the the Document_Complete event is raises when the document first loads and then when each (AJAX) HTTP request returns. No Bueno.

UPDATE2

My application attempts to read HtmlElements from the Webbrowser.Document object. Because the code executes faster than the HTTP Requests return... the document object does not contain all of the html elements.

What I need is some way to delay the call of methods in the main thread. I have tried using a timer:

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

This locks up the thread and the tick event never fires. This loops never ends.

I want to run a series of methods like this:

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

Can I solve this problem with a timer and the BackgroundWorker? Can someone suggest a possible solution?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

无边思念无边月 2024-09-23 00:45:18

您可能会考虑使用 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...");
                }
            }
        }
    }
}

You might consider going with the Monitor class in the Threading namespace. Example below.

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...");
                }
            }
        }
    }
}
回忆那么伤 2024-09-23 00:45:18

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

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

Unfortunately you have to call Application.DoEvents() in order to keep the browser functional. Beyond that, I also pay attention to the ReadyState property.

  while (BusyNavigating || (wbBrowser.ReadyState != WebBrowserReadyState.Complete))
  {
    Application.DoEvents();
  } 
你的他你的她 2024-09-23 00:45:18

Document_Complete 确实告诉您页面何时完成加载。

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

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

也许您可以明确说明您的应用程序需要等待所有 AJAX 调用完成之后执行什么操作,以及为什么等待 Document_Complete 还不够好。

Document_Complete does tell you when the page has finished loading.

AJAX simply enables it to then load something else. Certain WebApplications (like StackOveflow) have AJAX calls on a timer which means that your application would never load fully because there is always an AJAX call waiting to be made/return.

This is a problem because the callback for an AJAX function could simply kick off another AJAX function. Without parsing their entire page (creating an execution tree) there really isn't a way for you to determine when the page is truly and completely loaded.

Perhaps you could clarify explicitly what it is that you application does that needs to wait until all the AJAX calls have completed and why waiting for Document_Complete is not good enough.

骷髅 2024-09-23 00:45:18

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

我编写了一个小表单,每五秒更改一次 url。请记住,在后台线程上询问 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();
    }




}

I think one of the problems you might be having (can't tell because the code is not complete) is that you're trying to do stuff on your control when invoke is required.

I coded up a little form that changes the url every five seconds. Remember that interrogating the web browser's state on a background thread also isn't allowed.

  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();
    }




}
暮年 2024-09-23 00:45:18

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

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

这似乎可以解决问题。

 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);
        }
    }

Thanks for all the suggestions. This is the "Delay" solution I came up with last night. It feels like using a steam roller to crack a peanut but I couldn't find a more 'elegant' answer to this particular problem.

I am spinning off a new worker thread that invokes a method called "AddDelay". This method will put the worker thread to sleep for some interval of time. My main (UI) thread loops on the Thread.IsAlive condition while allowing the application to receive OS messages if the thread has not completed.

This seems to do the trick.

 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);
        }
    }
世界如花海般美丽 2024-09-23 00:45:18

难道你不能简单地使用:

   System.Threading.Thread.Sleep(1000);

这肯定有同样的效果吗?

Can't you simply use:

   System.Threading.Thread.Sleep(1000);

This should have the same effect surely?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文