Link to home
Start Free TrialLog in
Avatar of i-Thomas
i-Thomas

asked on

events: problem with delegate

Hi experts:

I have created a user control that published an event called ClosePanel.

The user control is a panel with a button and other controls on it. The click onto the button triggers the ClosePanel event that is created as follows in my user control class:

       private static readonly object ClosePanelEventHandler = new object();

        public event EventHandler ClosePanel
        {
            add
            {
                this.Events.AddHandler(ClosePanelEventHandler, value);
            }
           
            remove
            {
                this.Events.RemoveHandler(ClosePanelEventHandler, value);
            }
        }


        private void OnClosePanel()
        {
            EventHandler handler = this.Events[ClosePanelEventHandler] as EventHandler;
            if (handler != null)
            {
                handler(this,EventArgs.Empty);
            }
        }


       private void uxCloseBtn_Click(object sender, System.EventArgs e)    // this triggers the event
       {
            OnClosePanel();
       }


   public class ClosePanelEventArgs : EventArgs   // Definition of the event class
    {
        private int id;
         
        private ClosePanelEventArgs()
        {
        }
     
        public ClosePanelEventArgs(int id)
        {
            this.id = id;
        }
     
        public int ID
        {
            get
            {
                return this.id;
            }
        }
    }

------

Now I try to add a custom control dynamically during runtime and I want to add the event handler to the dynamically created control:


       private void addLightControlPanel(string LightName)
        {
            int index = LightControlPanels.Count;
            LightControlPanel DynLCP = new LightControlPanel();
            DynLCP.lightControlName = LightName;
            DynLCP.BackColor = SystemColors.ControlLight;
            DynLCP.Location = new Point(10 + (index * 150),10);
            DynLCP.ClosePanel += new System.EventHandler(this.Panel_Closed);        // this line causes the problem: delegate is not matching...

            LightControlPanels.Add(LightName, DynLCP);
            this.uxDynLCPPnl.Controls.Add((LightControlPanel)LightControlPanels[LightName]);
        }

        private void Panel_Closed(object sender, ClosePanelEventArgs e)
        {
            LightControlPanel panel = (LightControlPanel) sender;

            string name = panel.lightControlName;
            Debug.WriteLine("Panel " + name + " pressed");
        }

As I am newbee I am just digging into events and delegates. I would be really happy, if you could correct my code and explain what I did wrong.

Thank you very much in advance!

Avatar of b1xml2
b1xml2
Flag of Australia image

you have to declare a delegate:


       private static readonly object ClosePanelEvent = new object();

        public event ClosePanelEventHandler ClosePanel
        {
            add
            {
                this.Events.AddHandler(ClosePanelEvent, value);
            }
           
            remove
            {
                this.Events.RemoveHandler(ClosePanelEvent, value);
            }
        }


        private void OnClosePanel(ClosePanelEventArgs args)
        {
            ClosePanelEventHandler handler = this.Events[ClosePanelEvent] as ClosePanelEventHandler;
            if (handler != null)
            {
                handler(this,args);
            }
        }


       private void uxCloseBtn_Click(object sender, System.EventArgs e)    // this triggers the event
       {
            /// just adding a value to show you how to do it
            OnClosePanel(new ClosePanelEventArgs(10));
       }


public class ClosePanelEventArgs : EventArgs   // Definition of the event class
{
      public readonly int ID;
      
      
      public ClosePanelEventArgs(int ID)      
      {
            this.ID = ID;
      }
      
      
}
   
   
    public delegate void ClosePanelEventHandler(object sender,ClosePanelEventArgs args);
and here:
private void addLightControlPanel(string LightName)
{
      int index = LightControlPanels.Count;
      LightControlPanel DynLCP = new LightControlPanel();
      DynLCP.lightControlName = LightName;
      DynLCP.BackColor = SystemColors.ControlLight;
      DynLCP.Location = new Point(10 + (index * 150),10);
      DynLCP.ClosePanel += new ClosePanelEventHandler(this.Panel_Closed);      
      
      LightControlPanels.Add(LightName, DynLCP);
      this.uxDynLCPPnl.Controls.Add((LightControlPanel)LightControlPanels[LightName]);
}
SOLUTION
Avatar of BurntSky
BurntSky

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
this is event optimisation only available in c#
Avatar of BurntSky
BurntSky

Yeah, my guess was he saw a bunch of VB.NET examples and transcribed them to C#.  Everything in C# is easier :)  Not to mention all the extra features.
Avatar of i-Thomas

ASKER

@b1xml2:

    public delegate void ClosePanelEventHandler(object sender,ClosePanelEventArgs args);

This belongs in the main class, not in the custom control, right?


@BurntSky:

Can you please explain the differences in your approach compared to that of b1xml2? It is hard for me to understand it instantly as I just started to learn about events / delegates! Thank you in advance!


You can put the delegate anywhere you want; they don't even have to be inside a class.  In fact, I usually prefer to have them outside a class so they are at the same namespace level as my EventArgs class.

b1xml2 just refined your code a bit to solve your problem.  It should work just fine for you.  However, I just wanted to show you that theres an easier way to do event handling in c#.  The concepts are about the same; you still have to have the delegate, you still declare and raise the event, you just do it all in fewer lines of code.  If there's something about my code you don't understand, feel free to ask specifics so we can answer the issue your having trouble with more thoroughly.
For sure I prefer the easier way, if this easier way does not bring disadvantages (speed, unnecessary objects created ... whatever). If this is possible only in C#: Never mind! This makes me feel good to learn C# after coding some basic stuff in VB for some weeks.

In another thread b1xml2 explained:

"By using the EventList object found within the ComponentModel and applicable to WinForms, WebForms. UserControls, you create one instance of the event per class. This optimises the event handling."

So is the "BurntSky" - method equal in this respect?

And... @BurntSky: In your example Class1 would be my custom control class and Class2 my main Form class , right?


I am just placing all these questions, because I have the impression that I have the chance to talk to two real experts and I consider good understanding of delegates and event handling as one of the core topics to learn. And good knowledge here will most likely safe me a lot of headache later...


So if you feel that you've done your work, let me know and we can open another thread with fresh points...


@b1xml2:

   OnClosePanel(new ClosePanelEventArgs(10));

This line causes an error (try to translate): "No overloading for the method OnClosePanel requires '1' arguments" ...
If you're still deciding between learning VB.NET and C#, I really have to recommend C#.  It is true that a lot of the stuff you can do in both languages, but it's the little differences that really make C# the better choice (things like operator overloading, ternary sequences, unmanaged code, etc.).  Not to mention that C# is an obvious choice if you're looking for a career.  Browse around Monster.com and you'll see that ~90% of employers seeking .NET programmers want C# experience.

I've never heard about b1xml2's claim that the eventlist optimizes performance, but that doesn't mean it's not true.  I was under the understanding that the field like declaration of an event (my way: "public event EventHandler EventName") was compiled into the property-like declaration anyway; just that C# doesn't require you to write all the extra code.

Yes, Class1 is your control (the class that exposes the event) and Class2 is your form (the class that handles the event).

Feel free to ask questions pertaining to this particular topic in this thread.  If you have other unrelated questions, please open a new thread.  Opening a new thread will also expose it to more people who may have different solutions than I have.

Also, the reason that line is throwing an error is because your definition of the OnClosePanel() method probably doesn't accept a parameter.
 OnClosePanel(new ClosePanelEventArgs(this,10));
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
For all:

I posted new thread for principal discussion of advantges / disadvantages of the suggested solutions:

https://www.experts-exchange.com/questions/21427867/discussion-different-methods-of-events-handling.html


@b1xml2:

Now I got your solution running! Very nice feeling after all trying...

One question concerning the line

"OnClosePanel(new ClosePanelEventArgs(10));"

So instead of 10 I could insert any variable that contains index of item etc., right?

How do I get this value back from the event after subscribing to it? I don't know exactly how to insert handling "e" here properly to retrieve the value:

       private void Panel_Closed(object sender, ClosePanelEventArgs e)
        {
            // Convert the sender object to a specific object type.
            LightControlPanel panel = (LightControlPanel) sender;

            // Get the name of the panel.
            string name = panel.lightControlName;
            Debug.WriteLine("Panel " + name + " pressed");
        }


Thanks for answering! After this I will split points and close this topic!
>>
So instead of 10 I could insert any variable that contains index of item etc., right?
<<
yep!!
private void Panel_Closed(object sender, ClosePanelEventArgs e)
{
      // Convert the sender object to a specific object type.
      LightControlPanel panel = (LightControlPanel) sender;
      int value = e.ID;
      
      // Get the name of the panel.
      string name = panel.lightControlName;
      Debug.WriteLine("Panel " + name + " pressed");
}
Because you can pass thru objects via classes that inherit from the EventArgs class (the 2nd parameter), all events do not therefore need to return any value.
OK, b1xml2, many many thanks for your quick helping and sharing your (obviously quite special) knowledge about proper event handling. I will practice now and try to use this as much as possible.

@BurntSky: Thank you very much, too, for bringing in another way to handle events that I will use for sure when it is not about custom controls.

My suggestion is to split points:

300 for b1xml2
200 for BurntSky

ok?

it's alright with me =)
Fine with me as well.
Ok. Accepted.

Thanks once more.

BTW:

If you think I can improve my "ee-behaviour" e.g. clearer problem description etc., please let me know!

I highly admire all the help I received from ee very much and as I will not be able to answer many questions with my current knowledge-level the minimum I can give back to ee is to make my threads as useful to others as possible!

One problem I encounter from time to time is that only after some help and discussions in the thread I know the real name of the problem, because I did not have enough knowledge to pinpoint the problem...