I always took for granted the fact that in ASP.NET WebForms you have this .aspx markup, and partial class in two other files, and then everything is magically compiled together and you just follow page lifecycle events.
Today I wanted to get a little deeper and sort it out for myself.
So for refresher, if you create an empty ASP.NET project, you get 1 page /Default.aspx divided into 3 files:
Default.aspx contains Page directive and some markup.
1: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default" %>
2:
3: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4:
5: <html xmlns="http://www.w3.org/1999/xhtml" >
6: <head runat="server">
7: <title></title>
8: </head>
9: <body>
10: <form id="form1" runat="server">
11: <div>
12:
13: </div>
14: </form>
15: </body>
16: </html>
Default.aspx.cs contains the part of _Default partial class that is supposed to be modified by developer.
1: namespace WebApplication1
2: {
3: public partial class _Default : System.Web.UI.Page
4: {
5: protected override void FrameworkInitialize()
6: {
7: base.FrameworkInitialize();
8: }
9:
10: protected void Page_Load(object sender, EventArgs e)
11: {
12: form1.Name = "New Name";
13: }
14: }
15: }
Default.aspx.designer.cs contains another part of _Default class with server-side controls declarations.
1: namespace WebApplication1 {
2:
3:
4: public partial class _Default {
5:
6: /// <summary>
7: /// form1 control.
8: /// </summary>
9: /// <remarks>
10: /// Auto-generated field.
11: /// To modify move field declaration from designer file to code-behind file.
12: /// </remarks>
13: protected global::System.Web.UI.HtmlControls.HtmlForm form1;
14: }
15: }
Protected form1 field in partial class allows you to work with form from code-behind code. You just have to remember that form1 is null early in page lifecycle and gets created by the time Page_Load is invoked. But actual initialization code for this field is nowhere to be found.
It turns out that when ASP.NET runtime compiles .aspx page, it creates a new class default_aspx derived from _Default which overrides Page.FrameworkInitialize method. You can open relevant App_Web_{hash}.dll in ‘Temporary ASP.NET Files’ folder with Reflector and have a look. I did:
1: [CompilerGlobalScope]
2: public class default_aspx : _Default, IRequiresSessionState, IHttpHandler
3: {
4: // snip
5:
6: [DebuggerNonUserCode]
7: private HtmlHead __BuildControl__control2()
8: {
9: HtmlHead head = new HtmlHead("head");
10: HtmlTitle title = this.__BuildControl__control3();
11: IParserAccessor accessor = head;
12: accessor.AddParsedSubObject(title);
13: return head;
14: }
15:
16: [DebuggerNonUserCode]
17: private HtmlTitle __BuildControl__control3()
18: {
19: return new HtmlTitle();
20: }
21:
22: [DebuggerNonUserCode]
23: private HtmlForm __BuildControlform1()
24: {
25: HtmlForm form = new HtmlForm();
26: base.form1 = form;
27: form.ID = "form1";
28: IParserAccessor accessor = form;
29: accessor.AddParsedSubObject(new LiteralControl("\r\n <div>\r\n \r\n </div>\r\n "));
30: return form;
31: }
32:
33: [DebuggerNonUserCode]
34: private void __BuildControlTree(default_aspx __ctrl)
35: {
36: this.InitializeCulture();
37: IParserAccessor accessor = __ctrl;
38: accessor.AddParsedSubObject(new LiteralControl("\r\n\r\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\" >\r\n"));
39: HtmlHead head = this.__BuildControl__control2();
40: accessor.AddParsedSubObject(head);
41: accessor.AddParsedSubObject(new LiteralControl("\r\n<body>\r\n "));
42: HtmlForm form = this.__BuildControlform1();
43: accessor.AddParsedSubObject(form);
44: accessor.AddParsedSubObject(new LiteralControl("\r\n</body>\r\n</html>\r\n"));
45: }
46:
47: [DebuggerNonUserCode]
48: protected override void FrameworkInitialize()
49: {
50: base.FrameworkInitialize();
51: this.__BuildControlTree(this);
52: base.AddWrappedFileDependencies(__fileDependencies);
53: base.Request.ValidateInput();
54: }
55: }
56:
So what we see here is the actual creation of control tree for this page. The tree consists of some instances of LiteralControl, HtmlHead, HtmlTitle and HtmlForm. At line 26 we can see how form1 field is assigned the new form which is later added to the tree by System.Web.UI.Control.AddParsedSubObject() method.
If you are curious and not afraid of some disassembly you can trace the whole process yourself. Get .NET symbols from Microsoft symbol server and place breakpoint in FrameworkInitialize method. From there you can get to dynamically created class in the call stack and step around.
0 comments:
Post a Comment