订阅的单例 Class 事件未触发

Subscribed Singleton Class Events Not Firing

订阅的单例方法未触发 - 嵌套 windows 表单

为旧版应用程序添加对新移动设备的支持。

单例Class基于本文实现 Create Event on Singleton Class

public sealed class Singleton
    {
        static readonly Singleton instance=new Singleton();

        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Singleton()
        {
        }

        Singleton()
        {
        }

        public static Singleton Instance
        {
            get
            {
                return instance;
            }
        }
    }

创建时,每个表单订阅一个本地方法到条形码对象的读取方法

singleton.Instance.scanEngine.BarcodeRead += new BarcodeReadEventHandler(FormA_BarcodeReadMethod);

关闭时,每个表单取消订阅条形码对象读取方法

singleton.Instance.scanEngine.BarcodeRead -= new BarcodeReadEventHandler(FormA_BarcodeReadMethod);

场景 #1

还有 5 个从主屏幕调用的其他窗体,它们的行为与上述相同

有一个实例,其中一个带有扫描仪订阅的表单调用另一个也需要扫描仪订阅的表单

场景#2

FormB在处理触发事件时调用FormC如下

单例BarcodeReader.BarcodeScan事件为空; - 控制 returns 到 FormB - FormB 事件订阅单例 BarcodeReader.BarcodeScan 事件singleton.Instance.scanEngine.BarcodeRead += new BarcodeReadEventHandler(FormB_BarcodeReadMethod); - 单例 BarcodeReader.BarcodeScan 事件不为空; - 条形码扫描和 FormB_BarcodeReadMethod 触发。

预期的结果是订阅的 FormC 事件会触发,但实际上并没有,我不明白为什么会这样。

非常感谢任何帮助。

对评论 #1 的回复

在FormB中,决定调用FormC 取消订阅 FormB 扫描仪事件

clsApplicationController.Instance.scanEngineIntermec.BarcodeRead -= new 
     BarcodeReadEventHandler(this.frmB_Intermec_OnRead); 
this.Update(); 
Application.DoEvents();

FormC调用如下

using (formC _formC = new formC())
{
_formC.cUser = _cUser;
var _result = _formC.ShowDialog();
if (_result != DialogResult.OK) return;
}

在 FormC 构造函数中订阅了事件

clsApplicationController.Instance.scanEngine.BarcodeRead += new 
BarcodeReadEventHandler(this.frmC_OnRead); 
this.Update(); 
Application.DoEvents();

对评论 #2 的回复

已订阅但未触发的 FormC 事件

void formC_OnRead(object sender, BarcodeReadEventArgs bre) 
{ 
 try { 
      ProcessScan(bre.strDataBuffer, sender); 
     } 
 catch (Exception exp) 
 { 
   MessageBox.Show(exp.Message); 
 } 
} 

formC_OnRead 方法订阅了 FormC 构造函数中的 Instance.scanEngineIntermec.BarcodeRead 事件。 ProcessScan 是通用的,可供所有扫描仪类型使用。 在工作表格上实施相同的方法和方法

为什么取消订阅新的事件处理程序而不减去现有的处理程序:

clsApplicationController.Instance.scanEngineIntermec.BarcodeRead -= new BarcodeReadEventHandler(this.frmB_Intermec_OnRead);

通常这应该是: clsApplicationController.Instance.scanEngineIntermec.BarcodeRead -= this.frmB_Intermec_OnRead;

我不确定,但是 -=new 会取消订阅一个新的 EventHandler。那有意义吗? -=new 用于您所有的表单中?!

另见 https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/events/how-to-subscribe-to-and-unsubscribe-from-events

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 失败的代码是:

    void Intermec_OnRead(object sender, BarcodeReadEventArgs bre)
    {
        try
        {
            this.textBox1.Text = bre.strDataBuffer;
            this.textBox1.Text += "\r\n" + this.Name;
            this.textBox1.Text += "\r\n" + sender.ToString();
            this.Update();

            if (_runFormC)
            {
                //MessageBox.Show(this.textBox1.Text);
                System.Threading.Thread.Sleep(5000);

                this.barcodeScanner_unSubscribe();

                using (FormC frmC = new FormC())
                {
                    var _result = frmC.ShowDialog();
                    if (_result != DialogResult.OK) return;
                }

                this.barcodeScanner_subscribe();
                this.textBox1.Text = string.Empty;
            }
        }
        catch (Exception exp)
        {
            MessageBox.Show(exp.Message);
        }
    }

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 如果从 Button_Click 处理程序调用 formC,一切都很好。

当我使用队列、锁和线程启动 formC(如果队列中有内容)时,FormC 工作正常。 将所有 subscribe() unsubscribe() 更改为从 Activated/Deactivate 处理程序调用。

我通常喜欢对条形码处理进行一些抽象,以便整个应用程序不依赖于供应商 SDK,从而在需要时更容易移植应用程序。我将跳过该切线的详细信息,但这种方法也简化了整个应用程序中的条形码处理。

让你的条码管理器保持单例,并让他成为唯一接触条码 SDK 对象的实体。让您的表单或其他对象通过调用您的单例条码管理器来注册它们对接收条码通知的兴趣,并将它们维护在堆栈中,以便您可以将条码通知路由到最顶层的侦听器。因此,在您有一个处理条形码的表单 A 调用也处理对话框的对话框 B 的情况下,您不必进行任何微观管理。当显示窗体或对话框时,它会在加载或激活中调用 BarcodeManager.Instance.PushBarcodeListener(this)。当它关闭或停用时,您调用 BarcodeManager.Instance.PopBarcodeListener(this)。如果您始终如一地使用它,Barcodemanager 将知道当前/顶级侦听器是谁,并只通知该对象。

监听器本身是一个独立于SDK的接口,任何想要接收条码事件的对象都可以实现。我们称它为 IBarcodeListener,并将定义一种方法,该方法传入条形码就绪数据:

void HandleBarcode(BarcodeReadEventArgs evt);

(您甚至可以支持侦听器遍历,其中事件在堆栈中冒泡,直到它被处理,但现在让我们保持简单)。

在您的条形码管理器单例中:

private void PushBarcodeListener(IBarcodeListener l)
{
   m_listeners.Push(l);
   if (m_listeners.Count == 1)
   {
      // as soon as you have an interested party, add your one and only 
      // event handler to the SDK object and enable it (the exact details
      // of this are SDK dependent).
   }
}

private void PopBarcodeListener(IBarcodeListener l)
{
   if (!Object.ReferenceEquals(l, m_listeners.Peek())
      Throw new Exception("Only the active BarcodeListener can be removed");

   m_listeners.Pop();
   if (m_listeners.Count == 0)
   {
      // if no one is interested in barcodes, stop listening by disabling the SDK
      // object and removing the event handler
   }
}

当然,在唯一的 SDK 事件处理程序中,您可以像这样通知顶级侦听器:

   if (m_listeners.Count != 0)
      m_listeners.Peek().HandleBarcode(evt)

免责声明,这段代码没有经过语法检查,我只是凭记忆做的。您需要更正拼写错误。


有关此添加的上下文,请参阅评论。

这是我在 UI 线程 "async" 上用于 运行 代码的一些代码示例。请注意,如果从 UI 线程调用,它基本上 运行 一旦所有当前 windows 消息已被消息泵处理:

    public System.IAsyncResult BeginInvoke(System.Delegate method)
    {
        return base.MainForm.BeginInvoke(method);
    }

    public System.IAsyncResult BeginInvoke(System.Delegate method, params object[] args)
    {
        return base.MainForm.BeginInvoke(method, args);
    }

注意"MainForm"是对应用程序中根表单的引用,而这段代码驻留在我的"app object"中,它是整个应用程序的根对象,因此可以随处访问。