0

I am trying to write a method that will dynamically load a given page's control collection so I can iterate through it and create a list of certain types of controls that reside on the page. The idea is that for any page that inherits from a particular page class, I want to compile a list of editable fields with certain attributes. This all happens outside of these pages (on a different page that is allowing users to manage these other pages).

I've tried both of the following scenarios:

BasePage page = (BasePage)System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath(string.Format("~/Pages/{0}/Manage.aspx", type.ToString()), typeof(BasePage));

IHttpHandler handler = PageParser.GetCompiledPageInstance(string.Format("~/Pages/{0}/Manage.aspx", type.ToString()), Context.Server.MapPath(string.Format("~/Pages/{0}/Manage.aspx", type.ToString())), HttpContext.Current);
BasePage page = handler as BasePage;

In both situations, the page variable is initiated but the controls collection is blank, presumably because both of these methods are only loading the codebehind and not the markup. How can I dynamically load the page's control collection?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Shane McGarry
  • 513
  • 1
  • 6
  • 19
  • Why can't you put your fields in a user control and access that from Page A and Page B? You shouldn't be able to instantiate a page's controls collection until it has been through the full request lifecycle which is not the same as just instantiating an instance of that page - you might find it easier to subclass your fields via UserControls (which you can load on the fly using Page.LoadControl) – dash Apr 04 '12 at 22:11

3 Answers3

0

I wouldn't mess with the page lifecycle. Let ASP.NET manage the control hierarchy on its own, and use some recursive logic to inspect the controls on the page:

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        FindPanelControls(Page);
    }
}

private void FindPanelControls(Control ctrl)
{
    if (ctrl.HasControls())
    {
        foreach (var childCtrl in ctrl.Controls.OfType<WebControl>())
        {
            //check for certain attributes
            if (childCtrl is Panel)
                Response.Write(childCtrl.ID);

            //recursively call the function for this control
            FindPanelControls(childCtrl);
        }
    }
}
James Johnson
  • 45,496
  • 8
  • 73
  • 110
  • The problem is that I'm sitting on Page B and I just need to get an instance of Page A's control collection so I can see what fields it contains. This is all so I can store information about said fields in the database without having to keep another table that just defines the fields on the page (which is overkill since its already in the markup). – Shane McGarry Apr 04 '12 at 21:34
  • Why do you need to access page `A` from page `B`? Why can't you access page `A` from page `A`? – James Johnson Apr 04 '12 at 21:35
  • Because I need to manage a list of fields and my options are either a) use a "relections type" method to get a list of fields on another page that I can use to load into a drop down for user selection or b) create a table in the database of all the fields that we want to let users manage. Option b is bad because as we create new pages or modify existing ones we not only have to add fields to the markup (as per usual web development) but we also need to add said field to the database so it can be managed outside the page. This is to create a rules engine to determine if a field is required. – Shane McGarry Apr 04 '12 at 21:44
0

You're over-complicating it; just loop recursively through the Page.Controls collection and check for the type of control you're interested in. Take a look at these earlier questions:

ASP.Net / C#, Loop through certain controls on a page?

Loop through all controls on asp.net webpage

Community
  • 1
  • 1
IrishChieftain
  • 15,108
  • 7
  • 50
  • 91
0

I have come across exactly the same requirement of needing to evaluate the controls of a Page A from Page B at runtime.

There is one additional step needed to run the Page through its life cycle. We simply need to call the Page.ProcessRequest() method. Page.ProcessRequest Method (MSDN). The method is hidden from Intellisense.

After this, you will be able to access the controls in the page:

Page page = (Page)BuildManager.CreateInstanceFromVirtualPath("~/MyPage.aspx", typeof(Page));
page.ProcessRequest(HttpContext.Current);

If your page has a master page, you will need to dig down through the master page's place holders.

Be sure to note Microsoft's remarks though:

"You should not call this method."

"This API supports the .NET Framework infrastructure and is not intended to be used directly from your code."

I do not yet know if there are any adverse impacts of doing this.


EDIT: Executing a page within a page can result in a myriad of problems, so I went in a slightly different direction.

Using the HTML Agility Pack, we can load a *.aspx file and recursively traverse its nodes quite easily. If we wanted to find all asp buttons on a page, for example:

private static void FindAllButtonControls(HtmlNodeCollection htmlNodeCollection, List<HtmlNode> controlNodes)
{
    foreach (HtmlNode childNode in htmlNodeCollection)
    {
        if (childNode.Name.Equals("asp:button"))
        {
            controlNodes.Add(childNode);
        }
        else
        {
            FindAllButtonControls(childNode.ChildNodes, controlNodes);
        }
    }
}

public static List<HtmlNode> FindButtonControlsAtVirtualPath(String path)
{
    HtmlAgilityPack.HtmlDocument aspx = new HtmlAgilityPack.HtmlDocument();

    aspx.OptionFixNestedTags = true;
    aspx.Load(HttpContext.Current.Server.MapPath(path));

    List<HtmlNode> controlNodes = new List<HtmlNode>();
    FindAllButtonControls(aspx.DocumentNode.ChildNodes, controlNodes);

    return controlNodes;
}
Timo
  • 226
  • 3
  • 6