此配置节的作用在于指定各种控件在不同类型的移动设备显示的适配器,以达到适应各种设备不同的展示形式。例子如下,
<mobileControls sessionStateHistorySize="6" cookielessDataDictionaryType="System.Web.Mobile.CookielessData"> <device name="XhtmlDeviceAdapters" predicateClass="System.Web.UI.MobileControls.Adapters.XhtmlAdapters.XhtmlPageAdapter" predicateMethod="DeviceQualifies" pageAdapter="System.Web.UI.MobileControls.Adapters.XhtmlAdapters.XhtmlPageAdapter"> <control name="System.Web.UI.MobileControls.Panel" adapter="System.Web.UI.MobileControls.Adapters.XhtmlAdapters.XhtmlPanelAdapter" /> <!--其他控件--> </device> <device name="HtmlDeviceAdapters" predicateClass="System.Web.UI.MobileControls.Adapters.HtmlPageAdapter" predicateMethod="DeviceQualifies" pageAdapter="System.Web.UI.MobileControls.Adapters.HtmlPageAdapter"> <control name="System.Web.UI.MobileControls.Panel" adapter="System.Web.UI.MobileControls.Adapters.HtmlPanelAdapter" /> <control name="System.Web.UI.MobileControls.Form" adapter="System.Web.UI.MobileControls.Adapters.HtmlFormAdapter" /> <control name="System.Web.UI.MobileControls.TextBox" adapter="System.Web.UI.MobileControls.Adapters.HtmlTextBoxAdapter" /> <!--其他控件--> </device> <device name="UpWmlDeviceAdapters" inheritsFrom="WmlDeviceAdapters" predicateClass="System.Web.UI.MobileControls.Adapters.UpWmlPageAdapter" predicateMethod="DeviceQualifies" pageAdapter="System.Web.UI.MobileControls.Adapters.UpWmlPageAdapter"> </device> <device name="WmlDeviceAdapters" predicateClass="System.Web.UI.MobileControls.Adapters.WmlPageAdapter" predicateMethod="DeviceQualifies" pageAdapter="System.Web.UI.MobileControls.Adapters.WmlPageAdapter"> <control name="System.Web.UI.MobileControls.Panel" adapter="System.Web.UI.MobileControls.Adapters.WmlPanelAdapter" /> <control name="System.Web.UI.MobileControls.Form" adapter="System.Web.UI.MobileControls.Adapters.WmlFormAdapter" /> <!--其他控件--> </device> <device name="ChtmlDeviceAdapters" inheritsFrom="HtmlDeviceAdapters" predicateClass="System.Web.UI.MobileControls.Adapters.ChtmlPageAdapter" predicateMethod="DeviceQualifies" pageAdapter="System.Web.UI.MobileControls.Adapters.ChtmlPageAdapter"> <control name="System.Web.UI.MobileControls.Form" adapter="System.Web.UI.MobileControls.Adapters.ChtmlFormAdapter" /> <control name="System.Web.UI.MobileControls.Calendar" adapter="System.Web.UI.MobileControls.Adapters.ChtmlCalendarAdapter" /> <!--其他控件--> </device> </mobileControls>
实际上这也是本配置节的默认配置的精简版。
各个节点和属性含义如下
<mobileControls --指定移动控件是否可以具有自定义特性。 allowCustomAttributes="true|false" --定义字典类的类型,该类维护无 Cookie 的 Forms 身份验证的密钥。 cookielessDataDictionaryType="System.Web.Mobile.CookielessData" --定义用于在服务器会话中保存应用程序视图状态的历史记录的大小。 sessionStateHistorySize="number"> <!—适配器集--> <device name="String" --指定该设备适配器集必须继承的设备适配器集。 inheritsFrom="String" " --为适配器集指定页适配器的类类型。 pageAdapter="String" --指定提供计算器谓词的类类型。 predicateClass="String" --指定提供计算器谓词的方法。 predicateMethod="String> <control --控件的完全限定名称。 name="String" --设备适配器的完全限定名称。 adapter="String" /> </device> <device...>...</device> </mobileControls>
device节点中,通过predicateClass中指定的类里面的predicateMethod指定的方法来判定当前这个设备是否适用于本适配器。这里感觉就和deviefilter识别相当类似,下面则是ChtmlPageAdapter的谓词方法
了解设备适配器选择过程
下面的过程描述如何选择页的设备适配器集:
- 按照 Web.config 文件中 <device> 节的定义,ASP.NET 循环访问设备适配器集的集合。 首先检查与所请求的页最接近的 Web.config 文件,然后依次检查在配置层次结构中处于更高位置的各个 Web.config 文件。 最后检查 Machine.config 文件。
- 使用 predicateClass 和 predicateMethod 特性来计算每个设备适配器集的关联谓词。
- 每个谓词使用所提供的 HttpContext 对象来检查目标设备的设备功能,并返回 true 或 false 来指示设备适配器集是否适用。
- 只要有任何谓词返回 true,即认为该设备适配器集是被选定的。 此时,系统使用 <device> 元素的 pageAdapter 特性来确定创建哪个类的实例并创建适配器。
- 如果 Web.config 文件中的谓词都没有返回 true,则将为父目录的 Web.config 文件中的设备适配器集重复此过程。
对于每一个控件,将调用页来提供指定控件类型的设备适配器。 ASP.NET 使用以下步骤来选择适当的控件适配器:
- 所使用的设备适配器集与为页的设备适配器选择的适配器集相同。
- 如果设备适配器集包含直接将控件类映射到设备适配器类的 <control> 元素,则将创建指定适配器类的实例。
- 如果无法直接映射设备适配器集,则将为控件创建基类的新实例。
此过程将一直重复,直至达到基类 Control 为止。 如果在任何时候找到了直接映射,则将创建指定设备适配器类的新实例。 - 如果仍未找到映射,并且设备适配器集从其他适配器集继承,则将为父适配器集重复适配器选择步骤。 此过程将沿着适配器集的层次结构向上执行,直至找到映射。
查找的结果会缓存起来,因此只需要为第一个相关控件执行一次指定的查找。 上述的行为将保留下来。
在 ASP.NET 移动控件及其关联的适配器的生命周期中发生两种类型的交互:控件和设备适配器之间的交互以及页和页适配器之间的交互。
控件和控件设备适配器
在移动控件的生命周期中,在控件及其关联的设备适配器之间发生以下交互:
- ASP.NET 先检查缓存中是否存在页的副本。 如果存在,则将缓存的页发送到客户端。 有关缓存的更多信息,请参见移动文本编写器呈现的最佳做法的"输出缓存"一节。
- 创建控件。 当首次尝试访问控件的 Adapter 属性时,将选择控件适配器并创建它的一个新实例,如适配器映射中的"了解设备适配器选择过程"一节所述。
当创建新适配器时,将设置控件设备适配器的 Control。 - 如果页进行了缓存,则该页将调用适配器以确定缓存是否需要通过任何附加头有所变化。
- 调用控件的 OnInit 方法。 MobileControl 基类的实现调用控件设备适配器的 OnInit 方法。
- 如果存在控件的私有视图状态,则将加载该状态。 如果设备适配器存储了控件的任何设备特定的状态,则 LoadPrivateViewState 方法的 MobileControl 基类实现将调用控件设备适配器的 LoadAdapterState 方法。
- 调用控件的 OnLoad 方法。 MobileControl 基类的实现调用控件设备适配器的 OnLoad 方法。
- 如果控件实现 IPostBackEventHandler 接口并接收回发事件,则调用控件的 RaisePostBackEvent 方法。 如果发送到控件的事件能够根据目标设备有所变化,则此方法的控件实现将调用控件设备适配器的 HandlePostBackEvent 方法。 HandlePostBackEvent 方法返回 true 或 false,指示设备适配器是否已处理事件。
- 如果适用,保存控件的私有视图状态。 如果适配器需要存储控件的任何设备特定的状态,则 SavePrivateViewState 方法的 MobileControl 基类实现将调用控件设备适配器的 SaveAdapterState 方法。
- 调用控件的 OnPreRender 方法。 MobileControl 基类的实现调用控件设备适配器的 OnPreRender 方法。
- 调用控件的 Render 方法。 MobileControl 基类的实现调用控件适配器的 Render 方法。
- 调用控件的 OnUnload 方法。 MobileControl 基类的实现调用控件设备适配器的 OnUnload 方法。
页和页适配器
虽然 MobilePage 类从 .NET Framework Page 类(而不是 MobileControl 类)继承,但 MobilePage 类与适配器相关的行为非常类似于 MobileControl 类的行为。
ASP.NET 中的移动页适配器(例如 ChtmlPageAdapter、HtmlPageAdapter、WmlPageAdapter 或 XhtmlPageAdapter)实现 IPageAdapter 接口(该接口本身实现 IControlAdapter 接口)。 因此,页适配器的大部分生命周期类似于控件适配器的生命周期。 移动页及其设备适配器的交互特定于 Page 类,并且在它们的生命周期中经历以下步骤:
- ASP.NET 创建一个页。 当首次尝试访问页的 Adapter 属性时,将选择页适配器并创建它的一个实例,如适配器映射中的"了解设备适配器选择过程"一节所述。 当创建适配器时,将设置页适配器的 Page 属性。
- 调用页的 OnInit 方法。 MobilePage 基类的实现调用页适配器的 OnInit 方法。
- 调用页的 OnDeviceCustomize 方法。 MobilePage 基类的实现调用页适配器的 OnDeviceCustomize 方法。
- 若要确定页的回发模式(如果适用),则 MobilePage 基类将调用页适配器的 DeterminePostBackMode 方法。 此方法可以检查和修改请求变量。 此方法还负责将任何保持的视图状态信息转换回窗体变量(如果该信息尚未在某个变量中)。
- 如果适用,加载页的私有视图状态。 如果适配器存储页的任何特定于设备的视图状态,则 MobilePage 基类调用页适配器的 LoadAdapterState 方法。
- 调用页的 OnLoad 方法。 MobilePage 基类的实现调用页适配器的 OnLoad 方法。
- 如果适用,保存页的私有视图状态。 如果适配器需要存储页的任何特定于设备的视图状态,则 MobilePage 基类调用页适配器的 SaveAdapterState 方法。
- MobilePage 基类调用页适配器的 SaveViewState 方法。 此方法负责确保作为参数传入的状态被序列化到写入的页。
- 在分页过程中,MobilePage 基类访问页适配器的 OptimumPageWeight 属性。 适配器返回适合目标设备的页权重。
- 调用页的 OnPreRender 方法。 MobilePage 基类的实现调用页适配器的 OnPreRender 方法。
- 创建一个编写器实例用于捕获页输出。 MobilePage 基类调用页适配器的 CreateTextWriter 方法,该方法必须返回特定于目标的文本编写器。
- 调用页的 Render 方法。 MobilePage 基类的实现调用页适配器的 Render 方法。 在呈现过程的某些特定时刻,适配器还负责通过页的 ClientViewState 属性访问并编写页的私有视图状态。
- 调用页的 OnUnload 方法。 MobilePage 基类的实现调用页适配器的 OnUnload 方法。
大致看了一下上述的流程和网上的源码,感觉每个移动控件的生命周期只是定立了一个基本的套路,并没有具体实现控件在某个具体生命周期内该做的事情,比如拿Calendar控件的渲染Render来说
它直接调用了基类的Render方法,而这个Render调用了OnRender
这里的Adapter只是一个接口,是从Page中获取的Adapter,具体的Adapter是从web.config中配置再筛选可得,那就看看具体某个ControlAdapter的Render方法,这里拿ChtmlCalendarAdapter为例,内容过长只截取方法头和部分代码
看了就知道这确实是实际的渲染控件的代码。虽然感觉这种方式怪怪的,可能也是适配器的作用,但是它确确实实能达到同一个控件在不同设备上呈现不同样式的。
参考
适配器映射
来自 <https://msdn.microsoft.com/zh-cn/library/w5b9y36y(v=vs.100).aspx>
适配器类型
来自 <https://msdn.microsoft.com/zh-cn/library/99tteheb(v=vs.100).aspx>
适配器与 ASP.NET 的交互
来自 <https://msdn.microsoft.com/zh-cn/library/w5thxd3s(v=vs.100).aspx>
移动文本编写器呈现的最佳做法
来自 <https://msdn.microsoft.com/zh-cn/library/aa2w58cf(v=vs.100).aspx>
分页支持
来自 <https://msdn.microsoft.com/zh-cn/library/hhfw2x5a(v=vs.100).aspx>
设备模板支持
来自 <https://msdn.microsoft.com/zh-cn/library/yt1b020d(v=vs.100).aspx>
实现模板呈现
来自 <https://msdn.microsoft.com/zh-cn/library/ah665120(v=vs.100).aspx>