I'd like to intercept parsing of each control in the entire sub-tree of my own User Control.
Currently I've overridden the protected method Control.AddParsedSubObject in my user control. It's limited to interception of parsing on the immediate child controls in the declarative syntax because each of those controls has its own AddParsedSubObject
method to further parse its own child controls.
From the User Control I can't get into the childrens' children to intercept those parsing calls.
In the following declarative example of my user control I can access the tv object from inside the User Control's AddParsedSubObject override.
<asp:TreeView runat="server" id="tv" />
However I cannot access the tv object in the following example (or the other children of the first Panel) because that parsing is handled by the Panel instead or its children.
<asp:Panel runat="server">
<asp:TreeView runat="server" id="tv" />
<asp:Panel runat="server">
<asp:TextBox runat="server" />
</asp:Panel>
</asp:Panel>
My code-behind in the user control looks like this
// User control interception of its parsed children
protected override void AddParsedSubObject(object obj) {
// Do some custom work with the control object.
if (obj is Control && ((Control)obj).ID == "tv") {
TreeView tv = (TreeView)obj;
DoSomethingWithParsedObject(tv);
}
// Let ASP.NET continue and put the control in the page hierarchy
base.AddParsedSubObject(obj);
}
Looking for ideas about how I can intercept parsing of each control in the entire sub-tree of my user control. For example, I want to write out custom information at each parse step.
If you are using .Net 4.5, there is a good way to achieve it. Asp.Net uses ControlBuilder to build temporary cs files from aspx layouts. Before .Net 4.5 you can intercept it only by reflection and switching some internal static variables in ControlBuilder class.
But in .Net 4.5 they added new class ControlBuilderInterceptor. Then you can write such code:
namespace TestInterceptApp
{
public class BuilderInterceptor : ControlBuilderInterceptor
{
public override void OnProcessGeneratedCode(ControlBuilder controlBuilder, CodeCompileUnit codeCompileUnit, CodeTypeDeclaration baseType,
CodeTypeDeclaration derivedType, CodeMemberMethod buildMethod,
CodeMemberMethod dataBindingMethod, IDictionary additionalState)
{
base.OnProcessGeneratedCode(controlBuilder, codeCompileUnit, baseType, derivedType, buildMethod, dataBindingMethod, additionalState);
buildMethod.Statements.Insert(
buildMethod.Statements.Count - 1,
new CodeSnippetStatement("TestInterceptApp.ControlInterceptor.Intercept(@__ctrl);"));
}
}
}
Then you need to modify compilation section in web.config to register this class to Asp.Net:
<system.web>
<compilation debug="true" targetFramework="4.5" controlBuilderInterceptorType="TestInterceptApp.BuilderInterceptor"/>
</system.web>
And then you can write this interceptor class like this:
namespace TestInterceptApp
{
public static class ControlInterceptor
{
public static void Intercept(TreeView control)
{
// Do some custom work with the control object.
if (control.ID == "tv")
{
control.Nodes.Add(new TreeNode("Test"));
}
}
public static void Intercept(object obj)
{
// just ignore all others controls
}
}
}
Basically when ControlBuilder creates new cs files, it will insert this line
"TestInterceptApp.ControlInterceptor.Intercept(@__ctrl);"
to the end of each control generation block. You can define one method in ControlInterceptor class with object parameter, or you can make a couple of needed overloads with specific parameters. And plus one base method with object parameter. When CLR will execute it, if control is TreeView, then it will send it to correct method. For all other controls like Literal, Button, HtmlHead, Page, etc CLR will use method with object signature.
My sample will just add new TestNode to each TreeView in current application that has ID="tv". And code generated by ControlBuilder is such:
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
private global::System.Web.UI.WebControls.TreeView @__BuildControltv() {
global::System.Web.UI.WebControls.TreeView @__ctrl;
#line 32 "c:\users\someuser\documents\visual studio 11\Projects\TestInterceptApp\TestInterceptApp\Default.aspx"
@__ctrl = new global::System.Web.UI.WebControls.TreeView();
#line default
#line hidden
this.tv = @__ctrl;
@__ctrl.TemplateControl = this;
@__ctrl.ApplyStyleSheetSkin(this);
#line 32 "c:\users\someuser\documents\visual studio 11\Projects\TestInterceptApp\TestInterceptApp\Default.aspx"
@__ctrl.ID = "tv";
#line default
#line hidden
#line 32 "c:\users\someuser\documents\visual studio 11\Projects\TestInterceptApp\TestInterceptApp\Default.aspx"
this.@__BuildControl__control3(@__ctrl.Nodes);
#line default
#line hidden
// here is that line that we added via ControlBuilderInterceptor
TestInterceptApp.ControlInterceptor.Intercept(@__ctrl);
this.@__PageInspector_SetTraceData(new object[] {
@__ctrl,
null,
1838,
368,
false});
return @__ctrl;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With