如何从使用定时器回UI线程的对象引发事件 [英] How to raise an event from an object that uses a timer back to the UI thread

查看:131
本文介绍了如何从使用定时器回UI线程的对象引发事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用一个计时器偶尔轮询的资源,然后使每当民意调查显示,说明一些事件的对象。我已经看过其他几个例子,但似乎无法找到马歇尔事件回到UI线程的方法不会对UI线程上的事件处理额外的代码。所以我的问题是:

I have an object that uses a timer to occasionally poll for a resource and then raises an event whenever the poll finds something of note. I have looked at several other examples but can't seem to find a method to marshall the event back to the UI thread without extra code on the event handler on the UI thread. So my question is:

有什么办法来隐藏我的对象的用户来说,这额外的努力。

Is there any way to hide this extra effort from the users of my object?

为了讨论的目的,我将包括一个简单的例子:

For the purpose of discussion I will include a trivial example:

想象一下,我有1 RichTextBox的一种形式:

Imagine I have a form with 1 richtextbox:

private void Form1_Load(object sender, EventArgs e)
{
    var listener = new PollingListener();
    listener.Polled += new EventHandler<EventArgs>(listener_Polled);
}

void listener_Polled(object sender, EventArgs e)
{
    richTextBox1.Text += "Polled " + DateTime.Now.Second.ToString();
}



我也有这个对象:

Also I have this object:

public class PollingListener
{
    System.Timers.Timer timer = new System.Timers.Timer(1000);
    public event EventHandler<EventArgs> Polled;
    public PollingListener()
    {
        timer.Elapsed +=new System.Timers.ElapsedEventHandler(PollNow);
        timer.Start();
    }

    void PollNow(object sender, EventArgs e)
    {
        var temp = Polled;
        if (temp != null) Polled(this, new EventArgs());

    }
}



它产生异常

If I run this, as expected it yields the exception

跨线程操作无效:控制'richTextBox1访问
从除线程的线程上它是在

"Cross-thread operation not valid: Control 'richTextBox1' accessed from a thread other than the thread it was created on"

这对我来说很有意义,我可以以不同包裹事件处理方法像这样:

This makes sense to me, and I can wrap the event handler method differently as so:

void listener_Polled(object sender, EventArgs e)
{
    this.BeginInvoke(new Action(() => { UpdateText() }));
}
void UpdateText()
{
    richTextBox1.Text += "Polled " + DateTime.Now.Second.ToString();
}



但现在我的对象的用户必须因为这是任何情况下做到这一点从在我的控制计时器事件引发。那么,有没有什么我可以添加到我的PollingListener类,没有改变它的方法签名额外的引用,将允许我的目标用户是无视在后台UI线程封送处理事件的传递?

But now the user of my object has to do this for any event that is raised from the timer event in my control. So, is there anything I can add to my PollingListener class that doesn't change the signature of it's methods to pass in extra references that would allow the user of my object to be oblivious of the marshaling event in the background to the UI thread?

感谢您的任何意见。

推荐答案

添加评论后:

您需要拍摄一些潜在的细节,你可以利用才能够实现这一目标。

You would need to pickup some latent detail that you can exploit to be able to accomplish that goal.

这想到的是在构造时创建自己的表格/ WPF计时器,然后用这个和部分同步隐藏协调跨线程的细节一件事。我们可以从你的样品,你的轮询的建设应该在消费者的线程的上下文中总是发生推断。

One thing that comes to mind is creating your own Forms/WPF timer at construction time and then use this and some synchronization to hide the details of coordination across threads. We can infer from your sample that construction of your poller should always happen in context of your consumer's thread.

这是实现你想要什么,而破解杂交的方式,但它可以完成的契税,因为你的人头听众的建设从消费者的线程(其中有一个窗口消息泵燃油表格/ WPF计时器的调度)发生,该类运作的其余部分可以从任何发生螺纹为形式的计时器的Tick将从原来的线程心跳。的如其他意见和答复所指出的,这将是最好重新评估和修复您的轮询操作和消费者之间的工作关系

This is a rather hack-ish way to accomplish what you want, but it can accomplish the deed because the construction of your poll-listener happens from the consumer's thread (which has a windows message pump to fuel the dispatches of Forms/WPF timers), and the rest of the operation of the class could occur from any thread as the forms Timer's tick will heartbeat from the original thread. As other comments and answers have noted, it would be best to reassess and fix the operating relationship between your polling operations and the consumer.

下面是使用类,PollingListener2的更新版本的ManualResetEvent 和隐蔽 System.Windows.Forms.Timer 来轮渡跨线程的投票通知。清理代码省略为简洁起见。需要显式清理使用的IDisposable 会在这个类的量产版推荐。

Here is an updated version of the class, PollingListener2 that uses a ManualResetEvent and a concealed System.Windows.Forms.Timer to ferry the polling notice across threads. Cleanup code is omitted for the sake of brevity. Requiring the use of IDisposable for explicit cleanup would be recommended in a production version of this class.

ManualResetEvent的@ MSDN

public class PollingListener2
{
    System.Timers.Timer timer = new System.Timers.Timer(1000);
    public event EventHandler<EventArgs> Polled;

    System.Windows.Forms.Timer formsTimer;
    public System.Threading.ManualResetEvent pollNotice;

    public PollingListener2()
    {
        pollNotice = new System.Threading.ManualResetEvent(false);
        formsTimer = new System.Windows.Forms.Timer();
        formsTimer.Interval = 100;
        formsTimer.Tick += new EventHandler(formsTimer_Tick);
        formsTimer.Start();
        timer.Elapsed += new System.Timers.ElapsedEventHandler(PollNow);
        timer.Start();
    }

    void formsTimer_Tick(object sender, EventArgs e)
    {
        if (pollNotice.WaitOne(0))
        {
            pollNotice.Reset();
            var temp = Polled;
            if (temp != null)
            {
                Polled(this, new EventArgs());
            }
        }
    }

    void PollNow(object sender, EventArgs e)
    {
        pollNotice.Set();
    }
}

这在遥远的Win32一些先例,过去在这里的一些人会使用隐藏的窗口等,以维持其他线程一只脚,而无需消费者作出自己的代码(有时无的变化是必须的)任何显著的变化。

This has some precedent in the distant Win32 past where some people would use hidden windows and the like to maintain one foot in the other thread without requiring the consumer to make any significant changes to their code (sometimes no changes are necessary).

原文:

您可以在助手类类型的<$添加一个成员变量C $ C>控制或表格和使用,作为范围为的BeginInvoke() / 的invoke()电话。

You could add a member variable on your helper class of type Control or Form and use that as the scope for a BeginInvoke() / Invoke() call on your event dispatch.

下面是你的样品类的副本,修改的行为以这种方式。

Here's a copy of your sample class, modified to behave in this manner.

public class PollingListener
{
    System.Timers.Timer timer = new System.Timers.Timer(1000);
    public event EventHandler<EventArgs> Polled;

    public PollingListener(System.Windows.Forms.Control consumer)
    {
        timer.Elapsed += new System.Timers.ElapsedEventHandler(PollNow);
        timer.Start();
        consumerContext = consumer;
    }

    System.Windows.Forms.Control consumerContext;

    void PollNow(object sender, EventArgs e)
    {
        var temp = Polled;
        if ((temp != null) && (null != consumerContext))
        {
            consumerContext.BeginInvoke(new Action(() =>
                {
                    Polled(this, new EventArgs());
                }));
        }
    }
}

下面是一个示例,演示此在行动。 。在调试模式下运行,这一点,看看你的输出,以确认它按预期工作。

Here's a sample that shows this in action. Run this in debug mode and look at your output to verify that it is working as expected.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        listener = new PollingListener(this);
    }

    PollingListener listener;

    private void Form1_Load(object sender, EventArgs e)
    {
        listener.Polled += new EventHandler<EventArgs>(listener_Poll);
    }

    void listener_Poll(object sender, EventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("ding.");
    }
}

这篇关于如何从使用定时器回UI线程的对象引发事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆