How to list all events a Winforms control has published

Viewed 511
    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.

1 Answers

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!

Related