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?
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;
}
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);
}
}
}
A
from page B
? Why can't you access page A
from page A
- James Johnson 2012-04-04 21:35
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