Reflecting over the Page from an ASP.NET Server control at design time and some debugging pain

I am working on an ASP.NET server control, and one of the properties of that control will typically contain a name of a boolean property that the Page exposes. At runtime this boolean property is called/examined and some action is taken as a result, however at design time, I wanted my ASP.NET control to be able to reflect over the page it is hosted on, and present a dropdown list of properties to select from, based on what the page has exposed.

Now this task is not so easy. After much pain and frustration, as well as a little help from a friend (thanks Mitch), I was able to get it working. Currently it still feels very hacky but here it is for anyone else who wants to do something similar.

INitially, I was hoping just to be able to get the page type, then use standard reflection to obtain that. Nothing I have found thus far enables me to do this *at design time*. All I ever get for a page type is "System.Web.UI.Page" and its standard set of properties.

What I did was to use the Visual Studio Environment API (which uses Interop) to get this job done. Have a look at the code below first, then I'll run through how I use it and what its doing.

public static string GetHostPagePath()
{
  string path = null;
  // Get an instance of the currently running Visual Studio IDE.
  EnvDTE80.DTE2 dte2;
  dte2 = (EnvDTE80.DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.8.0");
  if (dte2 != null)
  {
    foreach (EnvDTE.Property pr in dte2.ActiveDocument.ProjectItem.Properties)
    {
      if (string.Compare(pr.Name, "LocalPath", StringComparison.OrdinalIgnoreCase) == 0)
        path = pr.Value.ToString();
    }
  }
  return path;
}

So what this function does is simply return me the fully qualified path of the ASPX page that the server control has been placed on. Using that, I read in the contents of that file using the System.IO.File object and look for the "inherits" attribute of the “<%@ Page" directive. I grab out the class name from the inherits attribute and then create that type in code, from which I can reflect over the properties.

Oh yes, its hacky, but I haven't found a better way yet (would love some suggestions).

Now you'd think you can use the services that are accessible via the Container property component, something like this:

IDesignerHost svc = (IDesignerHost)myControl.Site.GetService(typeof(IDesignerHost));

and then access the svc.RootClassName property to get this, but this *only* gives the classname, no namespace prefix, that is, its not fully qualified, and so you can create a type from it and reflect over it. 

So that’s how I got to reflect over the page and grab all the public Boolean properties for use in my dropdown.

One more thing, when debugging a server control, you normally set that server control’s debug options to start an external program, and point that at the DEVENV.EXE visual studio executable so that when you start debugging, a new instance of VS.NET is launched and you can debug into your server control during design time.

The properties of the EnvDTE.DTE2 object during the debug experience reflected that of the initial VS.NET instance, that is, the one instance of VS.Net that starts the second instance of VS.NET which is actually the one you want to debug, NOT the initial instance. The net effect being you get to see properties of the EnvDTE.DTE2 object for the wrong instance of VS.NET, not the one where you are testing your server control. To get around this, I didn’t start new debugging instances, I simply did lots of System.Diagnostics.Trace.WriteLine statements to output debug information. Its longer and more painful, but at least you get to see the correct information. This little nuance can get quite confusing if you are not carefull.

 

 

No Comments