简体   繁体   中英

Subscribed Singleton Class Events Not Firing

Subscribed Singleton Method Not Firing - nested windows forms

Adding support for a new mobile device to a legacy application.

Singleton Class Implemented Based On This Article 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;
            }
        }
    }

On creation, Each form subscribes a local method to the read method of the barcode object

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

On close, Each form unsubscribes from the barcode object read method

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

Scenario #1

  • FormA is called from main menu form
  • singleton BarcodeReader.BarcodeScan event is null;
  • FormA created
  • FormA event subscribes to singleton BarcodeReader.BarcodeScan event

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

  • singleton BarcodeReader.BarcodeScan event is not null;

  • Barcode scans and FormA_BarcodeReadMethod fires.
  • FormA closed, FormA event unsubscribed to singleton

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

  • singleton BarcodeReader.BarcodeScan event is null;

  • back to main menu form

There are 5 other forms which are called from the main screen and behave ok as above

There is one instance where a form with a scanner subscription calls another form which also requires a scanner subscription

Scenario #2

  • FormB is called from main menu form
  • singleton BarcodeReader.BarcodeScan event is null;
  • FormB created
  • FormB event subscribes to singleton

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

  • singleton BarcodeReader.BarcodeScan event is not null;

  • Barcode scans and FormB_BarcodeReadMethod fires.

When processing the fired event, FormB calls FormC as follows

  • FormB event unsubscribed to singleton BarcodeReader.BarcodeScan event

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

  • singleton BarcodeReader.BarcodeScan event is null;

  • FormC created
  • FormC event subscribes to singleton BarcodeReader.BarcodeScan event

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

  • singleton BarcodeReader.BarcodeScan event is not null;

  • Barcode scans and FormC_BarcodeReadMethod does not fire.
  • FormC closed, FormC event unsubscribed to singleton BarcodeReader.BarcodeScan event

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

singleton BarcodeReader.BarcodeScan event is null; - Control returns to FormB - FormB event subscribes to singleton BarcodeReader.BarcodeScan eventsingleton.Instance.scanEngine.BarcodeRead += new BarcodeReadEventHandler(FormB_BarcodeReadMethod); - singleton BarcodeReader.BarcodeScan event is not null; - Barcode scans and FormB_BarcodeReadMethod fires.

The expect result would be that the subscribed FormC event would fire but it does not and I cannot figure out why that is.

Any help much appreciated.

Response To Comment #1

In FormB, a decision is made to call FormC On FormB scanner event is unsubscribed

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

FormC is the called as follows

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

event is subscribed in FormC constructor

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

Response To Comment #2

FormC event which is subscribed to but not firing

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

the formC_OnRead method is subscribed to the Instance.scanEngineIntermec.BarcodeRead event in the FormC constructor. ProcessScan is generic for use by all scanner types. The same method and approach is implemented on the working forms

Why do you unsubscribe to a new EventHandler and not subtract the existing handler here:

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

Normally this should be: clsApplicationController.Instance.scanEngineIntermec.BarcodeRead -= this.frmB_Intermec_OnRead;

I am not sure, but -=new would unsubscribe a new EventHandler. Does that make sense? The -=new is used in all your forms?!

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

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! The failing code is:

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

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! If formC is called from a Button_Click handler, everything is fine.

When I use a queue, a lock and a thread that launches formC if there is something in the queue, FormC works OK. Changed all subscribe() unsubscribe() to be called fro Activated/Deactivate handlers.

I typically like to abstract barcode handling a bit so that the whole application is not dependent on a vendor SDK, making it easier to port the application if needed. I'll skip the details on that tangent, but this approach also simplifies barcode handling throughout the app as well.

Keep your barcode manager singleton, and have him be the only entity that touches the barcode SDK objects. Have your forms or other objects register their interest in receiving barcode notifications by calling your singleton Barcode Manager, and maintain them in a stack so you can route the barcode notifications to the topmost listener. So in the scenario where you have a form A that handles barcodes that calls a dialog B that also handles dialogs, you don't have to micro-manage anything. When a form or dialog is shown it calls BarcodeManager.Instance.PushBarcodeListener(this) in the Load or Activate. And when it closes or deactivates you call BarcodeManager.Instance.PopBarcodeListener(this) . If you use this consistently, Barcodemanager will know who the current / top listener is and notify just that object.

The listener itself is an SDK independent interface that any object that wants to receive barcode events implements. Let's call it IBarcodeListener, and will define one method, that passes in barcode ready data:

void HandleBarcode(BarcodeReadEventArgs evt);

(You could even support listener traversal where the event bubbles down the stack until it is handled, but let's keep it simple for now).

Within your barcode manager singleton:

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

And of course, in the one-and-only SDK event handler, you notify the top listener like this:

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

Disclaimer, this code is not syntax checked, I just did this from memory. You will need to fix typos.


See comments for the context of this addition.

This is an example of some code I have used to run code on the UI thread "async". Note that if called from the UI thread, it basically runs once all current windows messages have been processed by the message pump:

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

Note that "MainForm" is the reference to the root form in the application, and this code resides in my "app object", which is the root object of the whole application and is therefore accessible everywhere.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM