关于自定义控件设计时如何把属性写入aspx中的研究(上)
如何通过继承GridView来修改在设计时绑定数据源时自动生成的ASP.Net代码?
具体情况是这样的,ObjectDataSource绑定到实体类,GridView帮定到ObjectDataSource,这时候,GridView会获取实体类的构架信息,并自动生成一些列,HeaderText就是实体类的属性名,是E文的,我现在想在GridView的CreateColumns方法中进行拦截这个生成过程,硬是把E文改为对应的中文。
结果,在设计时和运行时都可以看到是中文的,但是aspx中就不是中文的。
我就想问问,怎么样,才能让它在aspx中体现中文,GridView自身是怎么样把自动生成的列写入到aspx中的。
我已经把GridView以及几个基类的源码翻了好几遍了,我肯定,我已经把CreateColumns拦截到并修改成功了,但是,它从哪里得到英文HeaderText的BoundColumn写入到aspx中的?难道自动生成列的某些过程不需要调用CreateColumns?
经过跟踪发现:在设计时,vs会生成这个控件的两个实例,具体干什么我不知道,在取消数据源绑定或者重新绑定数据源的时候,其中一个实例B被销毁,又有新的实例被创建,如此反反复复。剩下的那个实例A,只是偶尔被调用几个方法。(有一个方法,可以在A以外的实例中,取得A实例,就是this.Site.Component as GridView。)
原来,我所拦截的CreateColumns,都是B和后面的实例,根本就没有拦截到A,它从来不调用CreateColumns方法。在绑定数据源时,既然IDE写入到aspx的列头是英文,那么,我可以肯定,它读取的是A中的列信息,因为,除了A以外,别的所有实例都已经被我捕获到,并把列头改为了中文,所以,IDE不可能从实例A取列信息。
但是,我有纳闷了,A从来不调用CreateColumns方法,它哪里来的列信息?
最后只有一种可能,那就是:那些属性,是被复制过去的,或者在GridViewDesigner中创建的。
这个猜想,没有得到验证,不过,在我把调用堆栈翻过几遍以后,终于发现了写入aspx的一个可疑之处:
ControlSerializer类
从代码就可以看出来,这不就是在写aspx嘛。只是看而已,没有确定^_^
这个类,还有大量串行化的方法。
至于怎么发现的……
是这样的,我写了一个类来继承GridView,把所有可以override的方法,都override一遍,然后,重写的类里面,输出当前调用堆栈信息到一个文本文件中。然后,在ide中使用这个控件,绑定数据源,取消绑定,多试几次,就可以得到足够的日志了。
下面是绑定到一个数据源控件时,所得到的override EnsureChildControls方法调用方法栈帧,第一个EnsureChildControls是GridView的EnsureChildControls:
EnsureChildControls <-
CompositeDataBoundControl.get_Controls <-
ControlSerializer.SerializeInnerProperties <-
ControlSerializer.SerializeInnerContents <-
ControlSerializer.SerializeInnerContents <-
ControlDesigner.GetPersistInnerHtmlInternal <-
ControlDesigner.GetPersistInnerHtml <-
ControlDesigner.GetPersistenceContent <-
IdentityBehavior.OnBehaviorNotify <-
DHTMLBehavior.Microsoft.VisualStudio.Web.Interop.Trident.IElementBehavior._Notify <-
IHTMLElement.GetOuterHTML <-
IdentityBehavior.GetOuterHTML <-
IdentityBehavior.System.Web.UI.Design.IControlDesignerTag.GetOuterContent <-
ControlDesignerSite.System.Web.UI.Design.IControlDesignerTag.GetOuterContent <-
ControlDesigner.CreateClonedControl <-
ControlDesigner.CreateViewControl <-
ControlDesigner.CreateViewControlInternal <-
ControlDesigner.get_ViewControl <-
GridViewDesigner.GetDesignTimeHtml <-
GridViewDesigner.GetDesignTimeHtml <-
ControlDesigner.GetViewRendering <-
ControlDesigner.GetViewRendering <-
IdentityBehavior.GetDesignTimeHtml <-
IdentityBehavior.RenderDesignTimeHtml <-
IdentityBehavior.UpdateView <-
IdentityBehavior.System.Web.UI.Design.IControlDesignerView.Update <-
ControlDesignerSite.System.Web.UI.Design.IControlDesignerView.Update <-
ControlDesigner.UpdateDesignTimeHtml <-
ControlDesigner.OnComponentChanged <-
ComponentChangeAction.DoAction <-
BaseUndoableAction.Microsoft.VisualStudio.Web.Interop.TriDsn.IUndoAction.DoAction <-
IUndoTransaction.Rollback <-
ASPUndoCoordinator.CancelTransaction <-
ASPUndoCoordinator.OnTransactionClosed <-
DesignerTransactionCloseEventHandler.Invoke <-
DesignerHost.OnTransactionClosed <-
DesignerHostTransaction.OnCancel <-
DesignerTransaction.Cancel <-
DesignerTransaction.Dispose
这些方法是从下往上调用的。
从中可以看到几个GridViewDesigner的方法,大概意思就是,我绑定数据源控件后,设计时触发ControlDesigner.UpdateDesignTimeHtml ,然后导致一系列的方法调用。
我的研究,就到这里了,下次有空再把剩下的发上来吧。
具体情况是这样的,ObjectDataSource绑定到实体类,GridView帮定到ObjectDataSource,这时候,GridView会获取实体类的构架信息,并自动生成一些列,HeaderText就是实体类的属性名,是E文的,我现在想在GridView的CreateColumns方法中进行拦截这个生成过程,硬是把E文改为对应的中文。
结果,在设计时和运行时都可以看到是中文的,但是aspx中就不是中文的。
我就想问问,怎么样,才能让它在aspx中体现中文,GridView自身是怎么样把自动生成的列写入到aspx中的。
我已经把GridView以及几个基类的源码翻了好几遍了,我肯定,我已经把CreateColumns拦截到并修改成功了,但是,它从哪里得到英文HeaderText的BoundColumn写入到aspx中的?难道自动生成列的某些过程不需要调用CreateColumns?
经过跟踪发现:在设计时,vs会生成这个控件的两个实例,具体干什么我不知道,在取消数据源绑定或者重新绑定数据源的时候,其中一个实例B被销毁,又有新的实例被创建,如此反反复复。剩下的那个实例A,只是偶尔被调用几个方法。(有一个方法,可以在A以外的实例中,取得A实例,就是this.Site.Component as GridView。)
原来,我所拦截的CreateColumns,都是B和后面的实例,根本就没有拦截到A,它从来不调用CreateColumns方法。在绑定数据源时,既然IDE写入到aspx的列头是英文,那么,我可以肯定,它读取的是A中的列信息,因为,除了A以外,别的所有实例都已经被我捕获到,并把列头改为了中文,所以,IDE不可能从实例A取列信息。
但是,我有纳闷了,A从来不调用CreateColumns方法,它哪里来的列信息?
最后只有一种可能,那就是:那些属性,是被复制过去的,或者在GridViewDesigner中创建的。
这个猜想,没有得到验证,不过,在我把调用堆栈翻过几遍以后,终于发现了写入aspx的一个可疑之处:
ControlSerializer类
1private static void SerializeControl(Control control, IDesignerHost host, TextWriter writer, string filter)
2{
3 if (control == null)
4 {
5 throw new ArgumentNullException("control");
6 }
7 if (host == null)
8 {
9 throw new ArgumentNullException("host");
10 }
11 if (writer == null)
12 {
13 throw new ArgumentNullException("writer");
14 }
15 if (control is LiteralControl)
16 {
17 writer.Write(((LiteralControl) control).Text);
18 }
19 else if (control is DesignerDataBoundLiteralControl)
20 {
21 DataBinding binding = ((IDataBindingsAccessor) control).DataBindings["Text"];
22 if (binding != null)
23 {
24 writer.Write("<%# ");
25 writer.Write(binding.Expression);
26 writer.Write(" %>");
27 }
28 }
29 else if (control is UserControl)
30 {
31 IUserControlDesignerAccessor accessor = (IUserControlDesignerAccessor) control;
32 string tagName = accessor.TagName;
33 if (tagName.Length > 0)
34 {
35 writer.Write('<');
36 writer.Write(tagName);
37 writer.Write(" runat=\"server\"");
38 ObjectPersistData persistData = null;
39 IControlBuilderAccessor accessor2 = control;
40 if (accessor2.ControlBuilder != null)
41 {
42 persistData = accessor2.ControlBuilder.GetObjectPersistData();
43 }
44 SerializeAttributes(control, host, string.Empty, persistData, writer, filter);
45 writer.Write('>');
46 string innerText = accessor.InnerText;
47 if ((innerText != null) && (innerText.Length > 0))
48 {
49 writer.Write(accessor.InnerText);
50 }
51 writer.Write("</");
52 writer.Write(tagName);
53 writer.WriteLine('>');
54 }
55 }
56 else
57 {
58 string text3;
59 HtmlControl control2 = control as HtmlControl;
60 if (control2 != null)
61 {
62 text3 = control2.TagName;
63 }
64 else
65 {
66 text3 = GetTagName(control.GetType(), host);
67 }
68 writer.Write('<');
69 writer.Write(text3);
70 writer.Write(" runat=\"server\"");
71 ObjectPersistData objectPersistData = null;
72 IControlBuilderAccessor accessor3 = control;
73 if (accessor3.ControlBuilder != null)
74 {
75 objectPersistData = accessor3.ControlBuilder.GetObjectPersistData();
76 }
77 SerializeAttributes(control, host, string.Empty, objectPersistData, writer, filter);
78 writer.Write('>');
79 SerializeInnerContents(control, host, objectPersistData, writer, filter);
80 writer.Write("</");
81 writer.Write(text3);
82 writer.WriteLine('>');
83 }
84}
2{
3 if (control == null)
4 {
5 throw new ArgumentNullException("control");
6 }
7 if (host == null)
8 {
9 throw new ArgumentNullException("host");
10 }
11 if (writer == null)
12 {
13 throw new ArgumentNullException("writer");
14 }
15 if (control is LiteralControl)
16 {
17 writer.Write(((LiteralControl) control).Text);
18 }
19 else if (control is DesignerDataBoundLiteralControl)
20 {
21 DataBinding binding = ((IDataBindingsAccessor) control).DataBindings["Text"];
22 if (binding != null)
23 {
24 writer.Write("<%# ");
25 writer.Write(binding.Expression);
26 writer.Write(" %>");
27 }
28 }
29 else if (control is UserControl)
30 {
31 IUserControlDesignerAccessor accessor = (IUserControlDesignerAccessor) control;
32 string tagName = accessor.TagName;
33 if (tagName.Length > 0)
34 {
35 writer.Write('<');
36 writer.Write(tagName);
37 writer.Write(" runat=\"server\"");
38 ObjectPersistData persistData = null;
39 IControlBuilderAccessor accessor2 = control;
40 if (accessor2.ControlBuilder != null)
41 {
42 persistData = accessor2.ControlBuilder.GetObjectPersistData();
43 }
44 SerializeAttributes(control, host, string.Empty, persistData, writer, filter);
45 writer.Write('>');
46 string innerText = accessor.InnerText;
47 if ((innerText != null) && (innerText.Length > 0))
48 {
49 writer.Write(accessor.InnerText);
50 }
51 writer.Write("</");
52 writer.Write(tagName);
53 writer.WriteLine('>');
54 }
55 }
56 else
57 {
58 string text3;
59 HtmlControl control2 = control as HtmlControl;
60 if (control2 != null)
61 {
62 text3 = control2.TagName;
63 }
64 else
65 {
66 text3 = GetTagName(control.GetType(), host);
67 }
68 writer.Write('<');
69 writer.Write(text3);
70 writer.Write(" runat=\"server\"");
71 ObjectPersistData objectPersistData = null;
72 IControlBuilderAccessor accessor3 = control;
73 if (accessor3.ControlBuilder != null)
74 {
75 objectPersistData = accessor3.ControlBuilder.GetObjectPersistData();
76 }
77 SerializeAttributes(control, host, string.Empty, objectPersistData, writer, filter);
78 writer.Write('>');
79 SerializeInnerContents(control, host, objectPersistData, writer, filter);
80 writer.Write("</");
81 writer.Write(text3);
82 writer.WriteLine('>');
83 }
84}
从代码就可以看出来,这不就是在写aspx嘛。只是看而已,没有确定^_^
这个类,还有大量串行化的方法。
至于怎么发现的……
是这样的,我写了一个类来继承GridView,把所有可以override的方法,都override一遍,然后,重写的类里面,输出当前调用堆栈信息到一个文本文件中。然后,在ide中使用这个控件,绑定数据源,取消绑定,多试几次,就可以得到足够的日志了。
下面是绑定到一个数据源控件时,所得到的override EnsureChildControls方法调用方法栈帧,第一个EnsureChildControls是GridView的EnsureChildControls:
EnsureChildControls <-
CompositeDataBoundControl.get_Controls <-
ControlSerializer.SerializeInnerProperties <-
ControlSerializer.SerializeInnerContents <-
ControlSerializer.SerializeInnerContents <-
ControlDesigner.GetPersistInnerHtmlInternal <-
ControlDesigner.GetPersistInnerHtml <-
ControlDesigner.GetPersistenceContent <-
IdentityBehavior.OnBehaviorNotify <-
DHTMLBehavior.Microsoft.VisualStudio.Web.Interop.Trident.IElementBehavior._Notify <-
IHTMLElement.GetOuterHTML <-
IdentityBehavior.GetOuterHTML <-
IdentityBehavior.System.Web.UI.Design.IControlDesignerTag.GetOuterContent <-
ControlDesignerSite.System.Web.UI.Design.IControlDesignerTag.GetOuterContent <-
ControlDesigner.CreateClonedControl <-
ControlDesigner.CreateViewControl <-
ControlDesigner.CreateViewControlInternal <-
ControlDesigner.get_ViewControl <-
GridViewDesigner.GetDesignTimeHtml <-
GridViewDesigner.GetDesignTimeHtml <-
ControlDesigner.GetViewRendering <-
ControlDesigner.GetViewRendering <-
IdentityBehavior.GetDesignTimeHtml <-
IdentityBehavior.RenderDesignTimeHtml <-
IdentityBehavior.UpdateView <-
IdentityBehavior.System.Web.UI.Design.IControlDesignerView.Update <-
ControlDesignerSite.System.Web.UI.Design.IControlDesignerView.Update <-
ControlDesigner.UpdateDesignTimeHtml <-
ControlDesigner.OnComponentChanged <-
ComponentChangeAction.DoAction <-
BaseUndoableAction.Microsoft.VisualStudio.Web.Interop.TriDsn.IUndoAction.DoAction <-
IUndoTransaction.Rollback <-
ASPUndoCoordinator.CancelTransaction <-
ASPUndoCoordinator.OnTransactionClosed <-
DesignerTransactionCloseEventHandler.Invoke <-
DesignerHost.OnTransactionClosed <-
DesignerHostTransaction.OnCancel <-
DesignerTransaction.Cancel <-
DesignerTransaction.Dispose
这些方法是从下往上调用的。
从中可以看到几个GridViewDesigner的方法,大概意思就是,我绑定数据源控件后,设计时触发ControlDesigner.UpdateDesignTimeHtml ,然后导致一系列的方法调用。
我的研究,就到这里了,下次有空再把剩下的发上来吧。
我不相信神话,我只相信汗水!我不相信命运,我只相信双手!