Question about TreeView SelecedNodeChanged Event handling code

Dec 7, 2015 at 6:46 PM
I've written a server side control that embeds the TreeView control and uses the CSSFriendly Adapter. Initially I had trouble with wiring up the events. After reading here I figured out how to actually wire up the SelectedNodeChanged event inside my Control derived from WebControl.

My server side (code behind only) control had to have the following line added in order for the OnAdapted event to be called.

TreeView1.Attributes.Add("OnAdaptedSelectedNodeChanged", "TreeView1_SelectedNodeChanged")


Now perhaps I missed the reason for why the OnAdapted prefix on the event was necessary. Why was a new event OnAdaptedSelectedNodeChanged created?

Below is the RaiseAdaptedEvent source. This method doesn't follow the standard event mechanism and was hard to understand why my code wasn't working at first. My guess here is the developers of this section made an assumption the TreeView control would always be created in the HTML source. This would explain the checking for the event within the Control's attribute collection.
public void RaiseAdaptedEvent(string eventName, EventArgs e)
{
  string attr = "OnAdapted" + eventName;
  if ((AdaptedControl != null) && (!String.IsNullOrEmpty(AdaptedControl.Attributes[attr])))
  {
    string delegateName = AdaptedControl.Attributes[attr];
    Control methodOwner = AdaptedControl.Parent;
    MethodInfo method = methodOwner.GetType().GetMethod(delegateName);
    if (method == null)
    {
      methodOwner = AdaptedControl.Page;
      method = methodOwner.GetType().GetMethod(delegateName);
    }
    if (method != null)
    {
      object[] args = new object[2];
      args[0] = AdaptedControl;
      args[1] = e;
      method.Invoke(methodOwner, args);
    }
  }
}
Instead couldn't the adapter just invoke the actual event?

For example:
I added the following method GetEventHandler to the WebControlAdapterExtender class. This was from my Google searching on finding an event delegate (http://stackoverflow.com/questions/3783267/how-to-get-a-delegate-object-from-an-eventinfo)
/// Gets the EventHandler delegate attached to the specified event and object
/// </summary>
/// <param name="obj">object that contains the event</param>
/// <param name="eventName">name of the event, e.g. "Click"</param>
public static Delegate GetEventHandler(object obj, string eventName)
{
  Delegate retDelegate = null;
  FieldInfo fi = obj.GetType().GetField(eventName,
                                         BindingFlags.NonPublic |
                                         BindingFlags.Static |
                                         BindingFlags.Instance |
                                         BindingFlags.FlattenHierarchy |
                                         BindingFlags.IgnoreCase);
  if (fi != null)
  {
    object value = fi.GetValue(obj);
    if (value is Delegate)
      retDelegate = (Delegate)value;
    else if (value != null) // value may be just object
    {
      PropertyInfo pi = obj.GetType().GetProperty("Events",
                                     BindingFlags.NonPublic |
                                     BindingFlags.Instance);
      if (pi != null)
      {
        System.ComponentModel.EventHandlerList eventHandlers = pi.GetValue(obj, null) as System.ComponentModel.EventHandlerList;
        if (eventHandlers != null)
        {
          retDelegate = eventHandlers[value];
        }
      }
    }
  }
  return retDelegate;
}
I then modified the RaiseAdaptedEvent method to utilize the GetEventHandler method.
public void RaiseAdaptedEvent(string eventName, EventArgs e)
{
  if ((AdaptedControl != null)) // && (!String.IsNullOrEmpty(AdaptedControl.Attributes[attr])))
  {
    string delegateName = eventName; // AdaptedControl.Attributes[attr];
    Control methodOwner = AdaptedControl;

    Delegate d = GetEventHandler(methodOwner, eventName);
    if (d != null)
    {
      object[] args = new object[2];
      args[0] = AdaptedControl;
      args[1] = e;
      d.Method.Invoke(methodOwner.Parent, args);
    }
  }
}
I then modified the following line in the TreeViewAdapter.RaisePostBackEvent method

From:
Extender.RaiseAdaptedEvent("SelectedNodeChanged", new EventArgs());

To:
Extender.RaiseAdaptedEvent("SelectedNodeChangedEvent", new EventArgs());
The TreeView control didn't follow the normal event naming syntax and instead put "Event" as a suffix rather than as a prefix.

This allowed the original event to be wired up.

Since the WebControlAdapterExtender.RaiseAdaptedEvent is probably called by other adapters the code for those adapters would need to be modified as well.

Thanks for reading. :)