3
    private List<EventInfo> GetAllTheEventsTheControlCanPublish(Control control)
    {
        return control.GetType().GetEvents().ToList();
    }

The question is: how to get a list of ONLY the events a control is has published? I have the simple winform application:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;

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

            _listOfSelectableValues = new List<int>() { 3, 5, 8, 11, 15 };
            foreach (int currentValue in _listOfSelectableValues)
            {
                 comboBox1.Items.Add(currentValue);
            }

            comboBox1.SelectedIndexChanged += new EventHandler(comboBox1_SelectedIndexChanged);
            comboBox1.Click += new EventHandler(comboBox1_Click);            

        _allTheEventsTheControlCanPublish = GetAllTheEventsTheControlCanPublish(comboBox1);
    }

    private List<int> _listOfSelectableValues;
    private List<EventInfo> _allTheEventsTheControlCanPublish;

    private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
    {

    }

    private void comboBox1_Click(object sender, EventArgs e)
    {

    }

    private List<EventInfo> GetAllTheEventsTheControlCanPublish(Control control)
    {
        return control.GetType().GetEvents().ToList();
    }
}

}

I would like to have a method like:

    private List<EventInfo> GetPublishedEvents(Control control)
    {
        return ...;
    }

that will return a list of 2 items in this case:

  1. SelectedIndexChanged
  2. Click

Even though dr.null gave a way around, my question is still valid:
Given a control, is there a way to get a list of objects of type EventInfo of only the events this control has actually published?

To ask the question in a different form:
The method GetPublishedEvents(Control control) returns a list of objects of type EventInfo of ALL the events the specific control can publish.
Is there a field or a property of the object EventInfo whose value changes if the respective control has actually published this specific event?
I could then loop through all the objects in the list allTheEventsTheControlCanPublish and inspect the value of this field or property and thus find out which events are actually published.

user2102327
  • 59
  • 2
  • 6
  • 19
  • 1
    [Some useful info here](https://stackoverflow.com/questions/2697247/how-to-determine-if-an-event-is-already-subscribed) and why it is so difficult to get the list you want. Can you explain what are you trying to do? Perhaps there are other ways. – Steve Jul 03 '21 at 13:07
  • @Steve I am not trying to find out if I subscribe twice. Before I remove the control, I have to unsubscribe it from all the events it is subscribed to. – user2102327 Jul 03 '21 at 13:13
  • 2
    _Before I remove the control, I have to unsubscribe it from all the events it is subscribed to._ Are you sure? Why? – TaW Jul 03 '21 at 13:59
  • 1
    @TaW According to https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/how-to-add-to-or-remove-from-a-collection-of-controls-at-run-time?view=netframeworkdesktop-4.8: To remove controls from a collection programmatically Remove the event handler from the event. In Visual Basic, use the RemoveHandler Statement keyword; in C#, use the -= operator. Use the Remove method to delete the desired control from the panel's Controls collection. Call the Dispose method to release all the resources used by the control. – user2102327 Jul 03 '21 at 16:33
  • As for your reply to TaW, please read [Is it bad to not unregister event handlers?](https://stackoverflow.com/questions/1061727/is-it-bad-to-not-unregister-event-handlers) and the duplicate [Should I unsubscribe from events?](https://stackoverflow.com/questions/4172809/should-i-unsubscribe-from-events) then see if that will be necessary in your case. – dr.null Jul 03 '21 at 21:49
  • @dr.null it's much better to be proactive in removing event handlers than to abandon them and hope that everything gets cleaned up. Unless one can be certain that no unexpected references to the publisher can exist, such an approach is likely to 'mostly' work, but cause occasional memory leaks (from Is it bad to not unregister event handlers?). – user2102327 Jul 04 '21 at 03:15
  • 1
    Then just `-=` what you `+=` yourself. As simple as that. – dr.null Jul 04 '21 at 04:41
  • @dr.null That means that I have to maintain a list of delegates for each control. Is that correct? – user2102327 Jul 04 '21 at 04:50
  • In the special case of Control (and other .NET classes), they use an EventHandlerList (https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.eventhandlerlist). In this not so rare special case, you can hack it using reflection. For example this code removes all handler subscriptions on any control instance: https://pastebin.com/raw/c0drPuPT (use at your own risk) – Simon Mourier Jul 05 '21 at 14:54
  • @Simon Mourier That does not compile. I asked for a detailed and clear answer for beginners. Sorry, but my level of understanding is way lower. – user2102327 Jul 05 '21 at 16:18
  • 2
    This is not an answer, but a comment just to see if you can understand before I answer and you downvote because maybe you don't. That compiles and runs perfectly. You can use Visual Studio's help to determine what includes you need. State your .NET version and C# compiler version if it is a requirement. – Simon Mourier Jul 05 '21 at 16:23
  • @SimonMourier that works on NetFramework but not in NetCore. In Core the strings are all _ prefixed ("_key", "_head", "_handler") This should be expected though giving the high level of assumptions on an internal feature. Anyway I think you could post the answer with the inevitable warnings. – Steve Jul 05 '21 at 16:45
  • @Simon Mourier Where is EventHandlerList handlerList defined? More importantly, how does it get its items? If I am not completely mistaken, this is the list I was asking how to get its elements. It does not compile though. Error 1 The name 'nameof' does not exist in the current context. Error 8 The name 'handlerList' does not exist in the current context. using System.ComponentModel; is present. – user2102327 Jul 05 '21 at 16:58
  • add _using System.ComponentModel;_ – Steve Jul 05 '21 at 17:00
  • @Simon Mourier I work with Visual Studio 2010 and .Net Framework 4. As about the compiler version, I do not know how to check. – user2102327 Jul 05 '21 at 17:07
  • I might be naive here. Wouldn't be a first. But can't you just use reflection to iterate over all `event` declarations in the class and set them to `null`? – Kit Jul 05 '21 at 19:31
  • @Kit The point here is to learn how to get a list of events a given control is actually subscribed to, if it is possible. Second, I do not know how to use reflection to iterate through all the event declarations in the class. – user2102327 Jul 06 '21 at 02:07
  • 1
    Your question says; "the events a control is subscribed to", but it also sounds like you're trying to ask; "the events of a control that something is subscribed to". Since controls generally trigger events, rather than subscribing to the events of other objects. – Jeremy Lakeman Jul 06 '21 at 04:04
  • @Jeremy Lakeman I really am not sure how to express this correctly. In my question there is the control comboBox1 and there are 2 events: SelectedIndexChanged and Click, of this control. What I am looking for is a method that returns these two events, out of the 87 events the ComboBox can possibly have (subscribe to or whatever the correct wording is). – user2102327 Jul 06 '21 at 04:47
  • 1
    The other part of your question is "which events to I need to -= when disposing"? The lifetime of event handlers is not the controls responsibility, it's the callers responsibility to `-=` for each `+=` they have executed, even though it is the control that stores that list. Get that wrong, and you might cause a memory leak. – Jeremy Lakeman Jul 06 '21 at 04:57
  • Sorry, but I don't have Visual Studio 2010 to be able to adapt my code. These are very old versions, you should upgrade IMHO. – Simon Mourier Jul 06 '21 at 06:08
  • 1
    Take a look [here](https://stackoverflow.com/questions/15630943/how-to-check-which-event-are-assigned) – Maciej Los Jul 09 '21 at 07:56

1 Answers1

0

Most of this stuff is hidden from the programmer on purpose, so if you are just doing this for code correctness and ensuring no memory leaks, you are probably trying too hard.

C# is a managed language so normally adding dispose, garbage collect, or trying to mange memory works against you as you will get in the way of the stream lined process that C# has for dealing with this, and you add code that can be broken by future updates to C#.

And with that disclaimer, you can break into the protected 'Events' variable of Component from the inheritance chain MyComboBox -> ComboBox -> ListControl -> Control -> Component. This variable is protected which means that it can only be accessed by itself, and children. So if we make a child of ComboBox : 'myComboBox', we can access it there.

public class myCombobox : ComboBox
{
  public EventHandlerList GetPublishedEvents()
  {
    return Events;
  }
}

Events will contain only the published EventHandlers, but the key for each is a secret so that they cannot be messed with. Not being deterred by this I wrote a function to just dispose all Events of the control.

public class myCombobox : ComboBox
{
  public void removeAllActiveEventHandlers()
  {
    Events.Dispose();
  }
}

Results of -= events and disposing of events after creating and destroying 1000 comboBoxes made no difference from just letting C# handle it after destroying the object.

Furthermore, running in Debug mode, Under Diagnostic Tools, taking a snapshot of memory usage, looking at objects, then EventHandler Count shows C# destroys all 2000 of the events in all situations.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;

namespace GettingAllEventsOfAControl
{
  public partial class Form1 : Form
  {
    public class myCombobox : ComboBox
    {
      public EventHandlerList GetPublishedEvents()
      {
        return Events;
      }

      public void removeAllActiveEventHandlers()
      {
        Events.Dispose();
      }
    }

    public Form1()
    {
      InitializeComponent();
    }
        
    public void generateComboBox(string name)
    {
      // 
      // comboBox1
      // 
      myCombobox comboBox1 = new myCombobox();

      comboBox1.FormattingEnabled = true;
      comboBox1.Location = new System.Drawing.Point(442, 121);
      comboBox1.Name = "comboBox2";
      comboBox1.Size = new System.Drawing.Size(187, 21);
      comboBox1.TabIndex = 0;
      Controls.Add(comboBox1);


      _listOfSelectableValues = new List<int>() { 3, 5, 8, 11, 15 };
      foreach (int currentValue in _listOfSelectableValues)
      {
        comboBox1.Items.Add(currentValue);
      }

      comboBox1.SelectedIndexChanged += new EventHandler(comboBox1_SelectedIndexChanged);
      comboBox1.Click += new EventHandler(comboBox1_Click);

      _allTheEventsTheControlCanPublish = GetAllTheEventsTheControlCanPublish(comboBox1);
      var allEvents = comboBox1.GetPublishedEvents();

      // Removed via -=
      comboBox1.SelectedIndexChanged -= new EventHandler(comboBox1_SelectedIndexChanged);
      comboBox1.Click -= new EventHandler(comboBox1_Click);

      // Removed via dispose()
      comboBox1.removeAllActiveEventHandlers();      

      Controls.Remove(comboBox1);

      comboBox1.Dispose(); // <- also does nothing
    }

    private List<int> _listOfSelectableValues;
    private List<EventInfo> _allTheEventsTheControlCanPublish;

    private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) { }
    private void comboBox1_Click(object sender, EventArgs e) { }

    private List<EventInfo> GetAllTheEventsTheControlCanPublish(Control control)
    {
      return control.GetType().GetEvents().ToList();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      for (int i = 0; i < 1000; i++)
      {
        generateComboBox("comboBox" + i);
      }

      // V Used for testing only.  I would never use these two lines in my real code.
      System.GC.Collect();
      GC.WaitForPendingFinalizers();

      MessageBox.Show("Done", "Hi Done");
    }
  }
}

Cheers!

Daniel Lord
  • 754
  • 5
  • 18
  • Thank you for your answer. Even though its name is promising, the method EventHandlerList GetPublishedEvents() returns a collection of items of type EventHandlerList of all the built in events of the myComboBox class and not of only the 2 published events SelectedIndexChanged and Click, so your reply does not answer my question. – user2102327 Jul 09 '21 at 13:12