DirectUI的初步分析-转
DirectUI的初步分析(一)
最近由于项目的需要学习了一下DirectUI方面的东西,主要借鉴的是一个国外程序员写的代码(见引用一),看了后发现它更多的是探讨一种实现的可能性和思路,和实际应用还是有距离的,不过其实现还是很有意思的。在写此小结的时候又发现国内一个程序员将这个代码部分移植到WINCE下的代码(见引用二),因为平台的差异性要完全开发一个WINCE下的实际代码还是需要时间的。 由于本人GUI开发做得少,工作中有关这方面的东西主要是提供思路和方法,学习DirectUI的主要目的是为了更新知识学习思路,文章中难免出现错误。 一、核心 1、CWindowWnd: 窗口对象类(窗口实例对象父类) 2、CDialogBuilder: 创建控件类,分析脚本并用递归方式(_Parse函数)创建所有控件实例 3、CPaintManagerUI: 窗口消息及图形绘制管理器类 4、CGUIRenderEngineUI: 图形渲染引擎类,在离屏DC中生成最终显示的图形,可根据需要扩展多种图形效果显示。 5、INotifyUI: 事件通知抽象类 6、IMessageFilterUI: 消息过滤抽象类 二、控件 CControlUI: 控件管理抽象父类,父类INotifyUI 1、button CButtonUI: 按钮控件 COptionUI: 选择按钮控件 2、combox CSingleLinePickUI: CDropDownUI: 下拉控件,父类另有CContainerUI和IListOwnerUI 3、decoration CTitleShadowUI: 阴影效果 CListHeaderShadowUI CSeparatorLineUI CFadedLineUI 4、edit CSingleLineEditUI: 单行编辑框控件 CMultiLineEditUI: 多行编辑框控件 5、label CLabelPanelUI: 可设置背景色和文字色的静态标签控件 CGreyTextHeaderUI 6、list 第一种: CListUI: 列表控件,包含以下几个子控件 (1)CListHeaderItemUI: 列表头 (2)CListExpandElementUI: 列表项 第二种:用法不明 CListHeaderUI: 列表头 CListElementUI: 列表项,父类另有IListItemUI CListLabelElementUI: 列表项,父类CListElementUI CListTextElementUI: 列表项 CListFooterUI: 列表尾 7、panel CTextPanelUI: 父类CLabelPanelUI CTaskPanelUI: CNavigatorPanelUI: 导航面板,父类另有IListOwnerUI,包含CNavigatorButtonUI子控件 CSearchTitlePanelUI: CImagePanelUI: 图片显示 CWarningPanelUI: 警告提示,父类CTextPanelUI CPaddingPanelUI: 填充栏 8、tab CTabFolderUI: 父类另有CContainerUI和IListOwnerUI CTabPageUI: 父类另有CContainerUI 9、toolbar CToolbarUI: 工具栏,包含以下几个子控件 (1)CToolButtonUI: 图形按钮 (2)CToolSeparatorUI: 分隔符 (3)CToolGripperUI: gripper 10、title CToolbarTitlePanelUI: 11、statusbar CStatusbarUI: 状态栏,父类另有CContainerUI 12、anim CAnimJobUI: 动画显示类 13、ActiveX CActiveXUI: 三、容器: CContainerUI: 容器类,父类CControlUI和IContainerUI。可以认为容器是特殊的控件(见上面控件类关于父类的说明),其目的之一是具有容器特性的控件可以容纳其它控件,这样可以方便的实现控件的叠加;目的之二实际的窗口只有一个,对于叠加的控件必须要进行层次管理才能正确绘图和事件分发。另外可参见引用三 1、画布: CCanvasUI(父类CContainerUI),可绘制背景色、画线、贴图 CWindowCanvasUI: 父类CCanvasUI CControlCanvasUI: 父类CCanvasUI CWhiteCanvasUI: 父类CCanvasUI CDialogCanvasUI: 父类CCanvasUI CTabFolderCanvasUI: 父类CCanvasUI 2、布局: 管理不同层次的控件 CDialogLayoutUI: 父类CContainerUI CVerticalLayoutUI: 父类CContainerUI CHorizontalLayoutUI: 父类CContainerUI CTileLayoutUI: 父类CContainerUI 四、通用 1、script CMarkup CMarkupNode 2、language CUIUtility 3、multi-thread CriticalSection AutoCriticalSection CMutex CAutoMutex CEvent CAutoEvent CManualEvent 五、主要数据成员 1、CPaintManagerUI CControlUI* m_pRoot: 如果控件是叠加的则存放最下层的控件对象,否则存放第一个创建的控件对象 CControlUI* m_pFocus: 存放获得焦点的控件对象指针 CControlUI* m_pEventHover: 存放当前有鼠标移进移出事件的控件对象指针 CControlUI* m_pEventClick: 存放当前有点击事件的控件对象指针 CControlUI* m_pEventKey: 存放当前有按键事件的控件对象指针 CStdPtrArray m_aNotifiers: 记录所有需要事件通知的窗口,根据窗口名称调用相应的消息处理函数 CStdPtrArray m_aNameHash: 保存控件对象指针hash表(用控件名称生成hash值) CStdPtrArray m_aPostPaint: panel的fade效果 CStdPtrArray m_aMessageFilters: 保存需要进行消息过滤的控件或功能(如动画类) CStdPtrArray m_aDelayedCleanup: CStdPtrArray m_aPreMessages: 预处理消息 HWND m_hWndPaint: 控件布局窗口句柄 HDC m_hDcPaint: 控件布局窗口设备DC HDC m_hDcOffscreen: 离屏内存DC HBITMAP m_hbmpOffscreen: 离屏内存DC相关联HBITMAP 2、CControlUI CPaintManagerUI* m_pManager: 窗口消息或绘图管理器 CControlUI* m_pParent: 逻辑上的父窗口(控件)对象指针 CStdString m_sName: 控件标识 CStdString m_sText: 控件显示标题或显示脚本字符串 CStdString m_sToolTip: 控件的Tip信息 3、CContainerUI CStdPtrArray m_items: 同一层的控件对象或控件对象的子对象,例如canvas上放置的按钮、combox由edit和list两个子对象组成,其它还有tab等。具体见CDropDownUI、CTabFolderUI、CNavigatorPanelUI三个类定义 4、CDialogLayoutUI CStdValArray m_aModes: 用于存放在Layout上绝对坐标转成相对坐标(CDialogLayoutUI::RecalcArea)的控件对象(指针、大小、模式),目的是否为了让布局上的控件随布局变化而变化,能够正确绘图??? 六、控件属性 待完成 七、脚本例子 <Dialog> <WindowCanvas pos=/ "0,0,600,800/" > <DialogLayout pos=/ "0,0,600,800/" > <Button pos=/ "390, 30, 490, 58/" text=/ "OK/" name=/ "ok/" /> </DialogLayout> </WindowCanvas> </Dialog> 八、绘图及事件处理 1、绘图 STEP01. CWindowWnd::__WndProc: 主窗口程序 STEP02. pThis->HandleMessage: pThis是布局窗口对象指针,并与布局窗口绑定(SetWindowLongPtr) STEP03. m_pm.MessageHandler: m_pm为CPaintManagerUI唯一实例对象 STEP04. CPaintManagerUI::MessageHandler: 处理WM_PAINT STEP05. m_pRoot->DoPaint: m_pRoot为最下层的控件对象(在本例中为CWindowCanvasUI控件,对应脚本中的WindowCanvas) STEP06. CCanvasUI::DoPaint: 往画布上绘制背景色、边角弧形、水印等。 STEP07. CContainerUI::DoPaint: 在最下层具有容器特性的控件(CWindowCanvasUI控件)上画容器内所有控件(控件实例对象保存在m_items中) STEP08. pControl->DoPaint: pControl为控件对象实例之一,利用多态性来调用不同控件的绘图方法 STEP09. CButtonUI::DoPaint: 按钮(对应脚本中Button)绘图方法,有下面两种方法 i)文字方法: CGUIRenderEngineUI::DPaintButton ii)图片方法: CGUIRenderEngineUI::DoPaintBitmap STEP10. 新一轮消息循环 2、事件 STEP01. CWindowWnd::__WndProc: STEP02. pThis->HandleMessage: STEP03. m_pm.MessageHandler: STEP04. CPaintManagerUI::MessageHandler: 处理WM_LBUTTONDOWN STEP05. CPaintManagerUI::FindControl: 根据鼠标坐标查找相应控件对象 STEP06. m_pRoot->FindControl: STEP07. CContainerUI::FindControl: 在最下层具有容器特性的控件(CWindowCanvasUI控件)容器内查找相应控件对象 STEP08. CControlUI::FindControl: 在m_items中查找相对应的控件对象 STEP09. pControl->Event: pControl为控件对象实例之一,利用多态性来调用不同控件的事件方法 STEP10. CPaintManagerUI::MessageHandler: 处理WM_LBUTTONUP STEP11. m_pEventClick->Event: 利用多态性来调用不同控件的事件方法(m_pEventClick说明见 "主要数据成员" ) STEP12. CButtonUI::Event: 按钮(对应脚本中Button)事件方法 STEP13. CButtonUI::Activate: STEP14. m_pManager->SendNotify: 传递控件对象指针和触发事件(文本方式) STEP15. CPaintManagerUI::SendNotify: 注意以下两点实现是完成控制和业务分离的关键 i)利用重载特性调用注册的监听对象(窗口)的消息处理函数Notify(监听对象保存在m_aNotifiers中) for ( int i = 0; i < m_aNotifiers.GetSize(); ++i ) { static_cast<INotifyUI*>(m_aNotifiers[i])->Notify(Msg); } ii)布局窗口CStartPageWnd的消息处理,宏定义展开后实际就是重载的Notify函数 DIRECT_BEGIN_NOTIFYMAP(CStartPageWnd) PROCESS_BUTTON_CLICK(_T( "ok" ),OnOk) 。。。 DIRECT_END_NOTIFYMAP(CStandardPageWnd) STEP16. CStartPageWnd::OnOk: 控件消息处理函数,此处可以加入具体的事务逻辑处理 STEP17. 新一轮消息循环 3、消息定义(文本) "click" 、 "changed" 、 "link" 、 "browse" 、 "itemclick" 、 "itemselect" 、 "dropdown" 、 "itemactivate" 、 "headerdragging" 、 "headerclick" 、 "headerdragged" 、 "itemexpand" 、 "itemcollapse" 、 "windowinit" 、 "killfocus" 、 "setfocus" 、 "timer" 九、疑问 1、Edit、Combox的下拉列表部分、ScrollBar、Tooltip控件是创建的实际窗口,这个与DirectUI思路还是有差别的 2、实例中有创建一个不进行消息处理的窗口(CFrameWindowWnd),然后又创建了一个窗口(CStandardPageWnd)用于具体的控件布局。但是我用一个窗口也能实现,原作者为什么这样还不清楚 3、控件是用文本形式来做标识的,消息类型是文本形式,是否改成数值型比较好 十、引用 引用一: http: //www.viksoe.dk/code/windowless1.htm 引用二: http: //directui.googlecode.com/ 引用三: http: //www.cnblogs.com/cutepig/archive/2010/06/14/1758204.html |
DirectUI的初步分析(二)
这篇文章是<DirectUI的初步分析>后的延续,在本文中主要是针对整体的框架结合MVC模式来进行分析,不会涉及具体的代码。 (1)Subject(目标) I)目标知道它的观察者。可以有任意多个观察者观察同一个目标。 II)提供注册和删除观察者对象的接口。 目标应该就是CControlUI类,关于第一点观察者观察的是CControlUI,当多个观察者观察的同一个目标状态发生改变时也就是多视图更新;关于第二点CControlUI中并没有定义这样的接口,这个工作是由CPaintManagerUI来完成的,不过没有再另行定义虚接口类。CPaintManagerUI的功能不仅限于此,还完成了更多的工作,会在下面做详细描述。 (2)Observer(观察者) 为那些在目标发生改变时需获得通知的(观察者)对象定义一个更新接口。 INotifyUI虚接口类的Notify函数。关于这一点单纯的观察者模式是有缺点的,如果存在多个观察者对象那就要为每一个观察者对象定义一个同名方法(即虚函数Notify)这加大了开销。如果用委托那观察者对象的方法就可以不同名(将观察者对象的方法委托给目标) (3)ConcreteSubject(具体目标) I)将有关状态存入各ConcreteObserver对象。 II)当它的状态发生改变时,向它的各个观察者发出通知。 应该就是继承自CControlUI或CContainUI等的各种控件类,当有事件触发时会通知观察者对象。关于第一点如进度条拖动时通知观察者对象获取值;关于第二点如按钮按下通知观察者 (4)ConcreteObserver(具体观察者) I)维护一个指向ConcreteSubject对象的引用 II)存储有关状态,这些状态应与目标的状态保持一致 III)实现Observer的更新接口以使自身状态与目标的状态保持一致 派生自INotifyUI的类如CStandardPageWnd,它实现了Notify虚函数、根据名称或ID获得控件实例对象指针、根据控件通知保存值或控件状态等等。 (5)众所周知MVC方式是从观察者模式演变而来。从第(4)描述来看CStandardPageWnd做为观察者并没有更新视图,原因何在?视图更新的工作实际上由CPaintManagerUI来承担了,这样做的好处就是当目标状态或数据发生改变时视图能立即更新反映变化,观察者收到通知后只需关注状态存储或数据处理,这也是文档视图的分离。 (6)我们知道WINDOW操作系统是基于窗口和消息的系统,所以目标状态的改变也是基于窗口消息触发而改变。CStandardPageWnd的父类CWindowWnd完成了这一部分同平台紧密相关的工作,而CPaintManagerUI完成了消息处理及改变目标状态的工作,理论上来说如果把这两部分加上相对独立的渲染部分替换就可以实现跨平台了。是否可以认为这些是控制器完成的工作? |
DirectUI的初步分析(三)
基于(http: //directui.googlecode.com/)最新版r62进行分析,对于未使用的控件属性未做分析 属性 一、主窗口属性(XML中标签 'Window' ) size 窗口的大小 sizebox caption 标题栏的宽度高度 roundcorner 窗口的圆角矩形的半径 mininfo showdirty 是否用矩形框标示需要重画的区域 二、公共属性( 'Window' 标签和第一个容器标签间的内容) Image 图片的来源以及图片文件的名称,一般是用于html方式文字的标签{i} Font 字体配置方案,对应CLabelUI的font属性 Default 指定Button、VScrollBar、HScrollBar的几种状态图片属性 三、控件(容器)属性 CControlUI 1 float 和pos组合使用,根据所处容器窗口坐标计算其窗口坐标。如果不指定此属性则背景图片会拉伸到整个容器,文字会水平居左垂直居中显示 2 pos 同上 3 padding 控件文字显示位置缩进距离 4 bkcolor 第一种背景颜色,如果指定第二种背景颜色则背景为垂直方向的渐变色 5 bkcolor2 第二种背景颜色 6 bordercolor 边框线颜色 7 bordersize 边框线尺寸 8 bkimage 背景图片 9 width 10 height 11 minwidth 12 minheight 13 maxwidth 14 maxheight 15 name 控件标识,在同一窗口内具有唯一性 16 text 显示文字 17 tooltip tip信息 18 userdata 扩展用户数据 19 enabled 是否激活 20 mouse 是否响应鼠标消息,如果为 false 则由其最近的左兄弟或父亲结点处理 21 visible 是否可见 22 shortcut 23 relativepos 子控件(容器)相对于父控件(容器)的客户区坐标 CContainerUI -> CControlUI 1 inset 容器内的控件的可显示区域要上下左右各缩进多少;如果容器嵌套容器则用于指定子容器相对父容器的偏移 2 mousechild 3 vscrollbar 垂直滚动条的几种状态图片属性 4 hscrollbar 水平滚动条的几种状态图片属性 5 childpadding CHorizontalLayoutUI -> CContainerUI 1 sepwidth 2 sepimm CTileLayoutUI -> CContainerUI 1 columns 容器内控件按几列显示,会自动根据列数计算行数 CLabelUI -> CControlUI 1 align 文字对齐方式 2 font 文字字体属性 3 textcolor 文字颜色 4 disabledtextcolor 非激活状态下文字颜色 5 textpadding 6 showhtml html方式显示文字,可参考DrawHtmlText函数说明 7 fitallArea 状态图片是否需要填充整个区域, false 可以用来画checkbox & radio box 8 tipimage tip的背景图片 CButtonUI -> CLabelUI -> CControlUI 1 normalimage 正常状态图片 2 hotimage 高亮状态图片 3 pushedimage 按下状态图片 4 focusedimage 获得焦点状态图片 5 disabledimage 非激活状态图片 6 disabled COptionUI -> CButtonUI -> CLabelUI -> CControlUI 1 group 为 true 则表示是多个option组合使用,且所有option必须包含在容器内。 2 selected 初始状态为选中,如果指定了 group 属性则当前选中项只有一个,而且属性字符中 group 要位于selected前面。 3 selectedimage 选中状态图片 4 foreimage checkbox或radio box选中时前景小图片 5 selectedtextcolor 文字颜色 CTextUI -> CLabelUI -> CControlUI CProgressUI -> CLabelUI -> CControlUI 1 fgimage 前景进度条图片,一般是根据百分比做拉伸处理 2 hor 为 true 则水平显示 3 min 最小值 4 max 最大值 5 value 当前值 CSliderUI -> CProgressUI -> CLabelUI -> CControlUI 1 thumbimage 滑标正常状态图片 2 thumbhotimage 滑标高亮状态图片 3 thumbpushedimage 滑标按下状态图片 4 thumbsize 滑标大小 注:如果需要实现带轨道的滑动条控制,则需要指定父类CControlUI的bkimage属性;如果要实现计数,则需要指定父类CProgressUI的min/max/value属性 CEditUI -> CLabelUI 1 readonly 只读属性 2 password 密文 "*" 显示方式 3 normalimage 正常状态图片 4 hotimage 高亮状态图片 5 focusedimage 获得焦点状态图片 6 disabledimage 非激活状态图片 7 multiline 多行属性 8 maxchar 是大可输入字符数 CComboUI -> CContainerUI -> CControlUI 1 textpadding 2 normalimage 3 hotimage 4 pushedimage 5 focusedimage 6 disabledimage 7 itemfont 8 itemalign 9 itemtextpadding 10 itemtextcolor 11 itembkcolor 12 itemimage 13 itemselectedtextcolor 14 itemselectedbkcolor 15 itemselectedimage 16 itemhottextcolor 17 itemhotbkcolor 18 itemhotimage 19 itemdisabledtextcolor 20 itemdisabledbkcolor 21 itemdisabledimage 22 itemlinecolor 23 itemshowhtml CScrollbarUI -> CControlUI 1 button1normalimage 2 button1hotimage 3 button1pushedimage 4 button1disabledimage 5 button2normalimage 6 button2hotimage 7 button2pushedimage 8 button2disabledimage 9 thumbnormalimage 10 thumbhotimage 11 thumbpushedimage 12 thumbdisabledimage 13 railnormalimage 14 railhotimage 15 railpushedimage 16 raildisabledimage 17 bknormalimage 18 bkhotimage 19 bkpushedimage 20 bkdisabledimage 21 hor 22 linesize 23 range 24 value CListUI -> CVerticalLayoutUI -> CContainerUI 1 header 2 headerbkimage 3 expanding 4 multiexpanding 5 itemfont 6 itemalign 7 itemtextpadding 8 itemtextcolor 9 itembkcolor 10 itemimage 11 itemselectedtextcolor 12 itemselectedbkcolor 13 itemselectedimage 14 itemhottextcolor 15 itemhotbkcolor 16 itemhotimage 17 itemdisabledtextcolor 18 itemdisabledbkcolor 19 itemdisabledimage 20 itemlinecolor 21 itemshowhtml CListHeaderItemUI -> CControlUI 1 dragable 2 sepwidth 3 align 4 font 5 textcolor 6 showhtml 7 normalimage 8 hotimage 9 pushedimage 10 focusedimage 11 sepimage CListElementUI -> CControlUI 1 selected CListExpandElementUI -> CListTextElementUI -> CListLabelElementUI -> CListElementUI -> CControlUI 1 expander 2 hideself CListContainerElementUI -> CContainerUI 1 selected |
DirectUI的初步分析-转的更多相关文章
- Azure底层架构的初步分析
之所以要写这样的一篇博文的目的是对于大多数搞IT的人来说,一般都会对这个topic很感兴趣,因为底层架构直接关乎到一个公有云平台的performance,其实最主要的原因是我们的客户对此也非常感兴趣, ...
- 基于Spark和SparkSQL的NetFlow流量的初步分析——scala语言
基于Spark和SparkSQL的NetFlow流量的初步分析--scala语言 标签: NetFlow Spark SparkSQL 本文主要是介绍如何使用Spark做一些简单的NetFlow数据的 ...
- S2-052 RCE漏洞 初步分析
PS:初步分析,只是分析了Struts2 REST插件的部分,本来菜的抠脚不敢发,但看到各大中心发的也没比我高到哪里去,索性发出来做个记事! 漏洞描述 2017年9月5日,Apache Struts发 ...
- Netfilter之连接跟踪实现机制初步分析
Netfilter之连接跟踪实现机制初步分析 原文: http://blog.chinaunix.net/uid-22227409-id-2656910.html 什么是连接跟踪 连接跟踪(CONNT ...
- drf安装与APIView初步分析
drf安装 1. pip install djangorestframework 2. 在settings文件中注册app : INSTALLED_APPS = [..., 'rest_framewo ...
- python简易的大乐透数据获取及初步分析
该项目从网上爬取并分析彩票数据,为用户查看和初步分析往期数据提供一种简易的工具. https://github.com/unknowcry/Lottery # -*- coding: utf-8 -* ...
- linux 时钟源初步分析linux kernel 时钟框架详细介绍
初步概念: 看datasheet的关于时钟与定时器的部分, FCLK供给cpu, HCLK供给AHB总线设备(存储器控制器,中断控制器.LCD控制器.DMA.USB主机控制器等), PCLK供给APB ...
- 2018-08-27 使用JDT核心库解析JDK源码后初步分析API命名
源自术语词典API项目 · Issue #85 · program-in-chinese/overview, 打算先用早先的代码提取JDK API中的类/方法/参数名, 看看有哪些词需要翻译. 源码在 ...
- Linux学习 :Uboot, Kernel, 根文件系统初步分析
1.U-Boot启动内核的过程可以分为两个阶段: 1)第一阶段的功能 硬件设备初始化 加载U-Boot第二阶段代码到RAM空间 设置好栈 跳转到第二阶段代码入口 2)第二阶段的功能 初始化本阶段使用的 ...
随机推荐
- loadrunner运行乱码解决方法
最近进行项目压力测试,选择用loadrunner来进行测试,当在回放时间,脚本中的中文由乱码.快照,经研究,经过一下3步可以解决,分享一下 第一步:当在进行新建脚本时间,选择选项中-〉高级-〉选择“支 ...
- 几款Android开发人员必备小工具
在这里我介绍一下我常常在Android Studio里面使用的小工具吧,这些工具都能够在plugin里面搜索到. (当然了哈.我也是从网上找的.用着挺方便的,在这里总结一下) Gsonformat: ...
- Eclipse中jar包关联JDK源代码
1.点 “window”-> "Preferences" -> "Java" -> "Installed JRES"2.此 ...
- POJ2226
题意:给定一个矩阵,当中有一些地方有水,如今有一些长度随意,宽为1的木板,要求在全部板不跨越不论什么坑的前提下,用一些木板盖住这些有水的地方,问至少须要几块板子? 思路: watermark/2/te ...
- JDBC实战案例--利用jdbc实现的宠物信息管理系统
一.需求: 利用jdbc实现对宠物的信息进行管理的一套系统 宠物信息:宠物ID,宠物类别,宠物名字,宠物性别,宠物年龄,宠物入库日期 系统完成功能:实现对宠物信息的录入,修改,删除,查询. 二.解决方 ...
- ExceptionLess 搭建到本地服务器
Exceptionless 是一个开源的实时的日志收集框架,它可以应用在基于 ASP.NET,ASP.NET Core,Web Api,Web Forms,WPF,Console,MVC 等技术栈的应 ...
- 使用nginx生成缩略图
nginx中可以使用 --with-http_image_filter_module 这个模块,今天发现在github上发现国人开发的一款模块 模块同时支持 Nginx 和 tengine 本ngin ...
- xp看系统位数
运行cmd,看打开的cmd窗口标题一般c:\windows\system32代表32位操作系统,64位就是system64,详细系统信息运行命令“systeminfo”
- bootstrap table 插件 搜索
以前用过easyui datagrid 每次搜索的时候调用datagrid构造方法 重新获取数据, 发现bootstrap-table 插件不行,只需要初始化一次, 以后每次搜索时,直接调用refre ...
- 细说 ASP.NET控制HTTP缓存[转]
阅读目录 开始 正常的HTTP请求过程 缓存页的请求过程 缓存页的服务端编程 什么是304应答? 如何编程实现304应答 如何避开HTTP缓存 在上篇博客[细说 ASP.NET Cache 及其高级用 ...