【问题标题】:Avoid using this.Invoke(..) on an event handler in c# compact framework避免在 c# 紧凑框架中的事件处理程序上使用 this.Invoke(..)
【发布时间】:2014-10-10 14:08:41
【问题描述】:

我正在使用一个单独的类来实例化不同的条形码对象,具体取决于机器的类型,一旦读取条形码就会引发事件。

所以为了将它传输到表单,我还引发了一个我所有表单都监听的唯一事件“myEvent”,但在表单内部我必须使用 this.Invoke((Action) mymethod(argument));能够在用户界面更新组件,只要 myEvent 的处理程序在另一个不同于表单的线程上执行。

所以我有这个:

我的类 ->barcode.readed(..) 被触发,然后从它的处理程序,我引发 myEvent,在我的表单上捕获,从我的表单上的 myEvent 处理程序,我执行 this.Invoke…

我知道这是一个有点标准的过程,但我想摆脱那个 Invoke,而是直接调用 myMethod(argument);功能。

我认为这与使我的类线程安全有关,但我不知道如何在我的情况下实现它。

事实上,如果我在表单内部使用原始制造商的barcode.readed() 事件,它不需要调用调用,只要这是线程安全的,但我不知道如何模仿,而且我真的需要将所有不同的条形码处理程序包装在一个不同的项目中,以便我的表单只使用一个返回所需条形码的“myEvent”,因此,不重复代码。

提前致谢,

罗杰·特兰切斯

【问题讨论】:

    标签: c# forms events thread-safety compact-framework


    【解决方案1】:

    如果您从控件继承类(基本上是创建一个新的自定义控件),您可以在 UI 线程上处理事件,而无需使用 Invokes,因为控件(您的条形码阅读器类)是 UI 线程的一部分。

    【讨论】:

    • 太棒了!它奏效了,谢谢:我创建了一个新的自定义控件。然后,我不得不将受保护的覆盖 void Dispose(bool disposing) 从设计器剪切到我的自定义控件类,因为如果我不这样做,我就不能在我的类中实现我的条形码阅读器的销毁。最后,自定义控件的创建会自动创建函数OnPaint:据我所知,我可以简单地保持原样,因为我的控件没有任何接口。对吗?
    • 对,如果您的“控件”不绘制任何内容,则不需要绘制代码。
    【解决方案2】:

    听起来您正在使用工作线程来读取条形码,以保持 UI 响应。并且条形码对象只是在创建它的任何线程上运行。

    您可以集中事件处理(以避免重复代码)并在工作线程上读取条形码,如下所示:

    • 将条形码对象包装在公开 MyEvent 的自定义对象/库 MyBarcodeReader 中。
    • 在 MyBarcodeReader 的构造函数中,将当前 SynchronizationContext 捕获到类字段 syncContext。如果您的表单构造 MyBarcodeReader,这将是您的 UI 的 SynchronizationContext。
    • 当您激活 MyBarcodeReader(例如 MyBarcodeReader.Execute)时,在工作线程上创建条形码对象。
    • 当您需要引发 MyEvent 时,调用 syncContext.Send(这将在工作线程上),传递一个用于引发 MyEvent 的委托。 syncContext.Send 将同步到 UI 线程(如 Control.Invoke)。下面的代码说明了这一点。

    public class MyBarcodeReader
    {
        private readonly SynchronizationContext syncContext;
    
        // Handler for barcode object's Readed event.
        private void Barcode.Readed(Object sender, Event e)
        {
            // Block the worker thread to synchronize with the thread associated
            // with SynchronizationContext.
            syncContext.Send(SyncMyEvent, (Object)e);
        }
    
        // Raises MyEvent on the thread associated with SynchronizationContext,
        // usually a UI thread.
        private void SyncMyEvent(Object o)
        {
            if (MyEvent != null)
            {
                MyEvent((Event)o);
            }
        }
    
        // Constructor.
        public MyBarcodeReader()
        {
            syncContext = SynchronizationContext.Current;
        }
    }
    

    这里的方法将阻塞工作线程(与 Control.Invoke 相同)但不会阻塞 UI 线程。如果您有一个或多个订阅 MyEvent 的表单,则它们不需要使用 Control.Invoke;他们甚至不需要知道工作线程。

    有一些关于 SynchronizationContext 的优秀在线参考资料,请参阅 CodeProjectMSDN magazine

    【讨论】:

    • 谢谢!我没有明确使用工作线程,我只是实例化包含引发事件的本机对象的类,然后从该类中引发我自己的事件,该事件在表单中捕获。我正在使用 Compact Framework 3.5,没有 SynchronizationContext 功能。我试过使用这个implementation 但它不起作用,因为示例没有实现 .Current 成员。我想我需要另一种方法...
    • 如果示例没有实现 .Current 成员,为什么不使用类实例,如syncContext = SynchronizationContext?您可以查看的另一个选项是nitoasync.codeplex.com
    【解决方案3】:

    我找到了一个解决方案here:基本上它将表单控件传递给类构造函数,然后在该类中使用 form.Invoke 从表单 ui 线程触发事件。

    类:

    using System;
    using System.Windows.Forms;
    using System.Threading;
    
    namespace ThreadTest
    {
        public class WorkerClass
        {
            private Thread thr;
    
            // UI control for update
            public Control UIControl { get; set; }
    
            public delegate void StatusUpdate(DateTime dateTime, string message);
            public event StatusUpdate OnStatusUpdate;
    
            // Starts thread
            public void Start()
            {
                thr = new Thread(new ThreadStart(MainWorker));
                thr.Start();
            }
    
            // Main thread worker
            public void MainWorker()
            {
                int i = 0;
    
                while (true)
                {
                    string message = string.Format("Value of i={0}", i++);
                    FireStatusUpdate(DateTime.Now, message);
    
                    Thread.Sleep(1000);
                }
            }
    
            // Fire thread safe event
            private void FireStatusUpdate(DateTime dateTime, string message)
            {
                // UIControl is set and OnStatusUpdate has subscriber
                if (UIControl != null && OnStatusUpdate != null)
                {
                    if (UIControl.InvokeRequired)
                    {
                        UIControl.Invoke(new StatusUpdate(FireStatusUpdate), 
                                                new object[] { dateTime, message });
                        return;
                    }
    
                    OnStatusUpdate(dateTime, message);
                }
            }  
        }
    }
    

    表格:

    using System;
    using System.Drawing;
    using System.Windows.Forms;
    
    namespace ThreadTest
    {
        public partial class Form1 : Form
        {        
            public Form1()
            {
                InitializeComponent();
    
                WorkerClass worker = new WorkerClass();
                // add event handler
                worker.OnStatusUpdate += new WorkerClass.StatusUpdate(worker_OnStatusUpdate);
                // add UI control to invoke
                worker.UIControl = this;
                worker.Start();
            }
    
            void worker_OnStatusUpdate(DateTime dateTime, string message)
            {
                label1.Text = dateTime.ToLongTimeString();
                label1.Text += " " + message;
            }
    }
    

    就我而言,我已将事件类型从 StatusUpdate 更改为

     EventHandler<MyEventArgs>
    

    ,作为 MyEventArgs 这个类:

    public class MyEventArgs : EventArgs
    {
      public string MyString { get; set; }
    }
    

    谢谢!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-30
      • 1970-01-01
      相关资源
      最近更新 更多