﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>博客园-Allen Lee's Magic</title><link>http://www.cnblogs.com/allenlooplee/</link><description>这里没有答案，顶多给你几个值得一试的猜想。</description><language>zh-cn</language><lastBuildDate>Tue, 06 Jan 2009 06:47:42 GMT</lastBuildDate><pubDate>Tue, 06 Jan 2009 06:47:42 GMT</pubDate><ttl>60</ttl><item><title>WM有约（五）：部署应用程序</title><link>http://www.cnblogs.com/allenlooplee/archive/2008/12/24/1360935.html</link><dc:creator>Allen Lee</dc:creator><author>Allen Lee</author><pubDate>Tue, 23 Dec 2008 16:00:00 GMT</pubDate><guid>http://www.cnblogs.com/allenlooplee/archive/2008/12/24/1360935.html</guid><wfw:comment>http://www.cnblogs.com/allenlooplee/comments/1360935.html</wfw:comment><comments>http://www.cnblogs.com/allenlooplee/archive/2008/12/24/1360935.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cnblogs.com/allenlooplee/comments/commentRss/1360935.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/allenlooplee/services/trackbacks/1360935.html</trackback:ping><description><![CDATA[<p><span style="font-family: Arial, Helvetica, sans-serif;">
<p style="text-align: center"><span style="color:#0070c0; font-size:20pt"><strong>WM有约（五）：部署应用程序 </strong></span></p>
<p>&nbsp;</p>
<p style="text-align: right"><span style="color:#0070c0"><strong><em>Written by Allen Lee </em></strong></span></p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>创建安装包 </strong></span></p>
<p>创建一个新的项目，用来部署Windows Mobile应用程序的项目模板是Other Project Types\Setup and Deployment下的Smart Device CAB Project： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122308_1553_WM1.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 1 </strong></span></p>
<p>输入项目名称，然后单击OK完成项目的创建。部署项目创建好后，Visual Studio将会显示File System (Setup)选项卡： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122308_1553_WM2.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 2 </strong></span></p>
<p>右键单击Setup项目，然后单击Add菜单下的Project Output： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122308_1553_WM3.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 3 </strong></span></p>
<p>Add Project Output Group对话框将会打开，同时选中Primary output、Localized resources和Content Files： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122308_1553_WM4.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 4 </strong></span></p>
<p>其中，Primary output就是我们的应用程序主体，Content Files就是Options.xml，那么Localized resources是什么呢？因为Orientation Aware Control是通过资源文件来储存窗体的布局信息（例如控件的位置和大小等）的： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122308_1553_WM5.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 5 </strong></span></p>
<p>所以我们要一并把它部署到目标设备上。 </p>
<p>接着，我希望在Windows Mobile的程序里显示这个应用程序的快捷方式，那么我得先添加一个Programs Folder： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122308_1553_WM6.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 6 </strong></span></p>
<p>选中左边的Programs Folder，右键单击右边空白处，并单击Create New Shortcut： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122308_1553_WM7.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 7 </strong></span></p>
<p>Select Item in Project对话框将会打开，选中里面Application Folder，然后单击OK： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122308_1553_WM8.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 8 </strong></span></p>
<p>对话框将会进一步显示Application Folder里面的内容，选中里面的Primary output from Alt (Active)，然后单击OK关闭对话框： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122308_1553_WM9.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 9 </strong></span></p>
<p>此时，Programs Folder右边将会出现一个快捷方式，把这个快捷方式重命名为应用程序的名字就可以了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122308_1553_WM10.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 10 </strong></span></p>
<p>最后，生成Setup项目，完成了！ </p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>安装和运行应用程序 </strong></span></p>
<p>把模拟器的共享文件夹设为Setup项目的输出文件夹： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122308_1553_WM11.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 11 </strong></span></p>
<p>这样，你就可以在模拟器里看到存储卡了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122308_1553_WM12.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 12 </strong></span></p>
<p>运行安装程序，Windows Mobile将会问你把应用程序安装在设备里还是存储卡里： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122308_1553_WM13.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 13 </strong></span></p>
<p>这里，我选择安装在设备里，单击Install继续。安装完毕后，你将会在Programs里找到这个应用程序： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122308_1553_WM14.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 14 </strong></span></p>
<p>运行应用程序，哈哈，出错了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122308_1553_WM15.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 15 </strong></span></p>
<p>还记得我们是怎样计算"下一次"的吗？ </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122308_1553_WM16.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 1 </strong></span></p>
<p>由于程序第一次运行，所以m_PinnedDates集合里什么也没有，此时调用Last方法就会抛出异常。没事，改用LastOrDefault方法就好了（当然，相应的逻辑需要调整一下，否则就会看到"下一次"是个很怪的日期！），不过这里为了简单起见，我已经准备了一份有数据的PinnedDates.txt文件了，把这个替换应用程序自动生成的空白文件，然后再运行应用程序，这次就正常了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122308_1553_WM17.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 16 </strong></span></p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122308_1553_WM18.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 17 </strong></span></p>
<p>什么？你问我什么时候把这个应用程序部署到我的手机上用？哎&hellip;&hellip; </p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>最后&hellip;&hellip; </strong></span></p>
<p>每个故事都会有结束的时候，我不知道应该为这个系列写点什么样的结语，不知道是否太久没有动笔的缘故，脑子似乎空荡荡的，今晚一直在循环播放周杰伦的《晴天》，或许，允许我用里面的几句歌词来做结语，也好掩饰一下我的江郎才尽： </p>
<p style="margin-left: 36pt"><em>从前从前有个人爱你很久<br />但偏偏风渐渐把距离吹得好远<br />好不容易又能再多爱一天<br />但故事的最后你好像还是说了拜拜</em></p>
</span></p><img src ="http://www.cnblogs.com/allenlooplee/aggbug/1360935.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/><a href="http://job.cnblogs.com/offer/302/" target="_blank">盛大招聘.Net开发工程师 </a><br/><a href="http://www.hjbook.net/product/4041/" target="_blank">经典好书：.NET框架程序设计（修订版）</a><br/>新闻：<a href="http://news.cnblogs.com/n/44341/" target="_blank">搜狐董事局主席张朝阳：今年不会买公司</a><br/>导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://job.cnblogs.com/" target="_blank">招聘</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>WM有约（四）：处理屏幕旋转</title><link>http://www.cnblogs.com/allenlooplee/archive/2008/12/21/1359421.html</link><dc:creator>Allen Lee</dc:creator><author>Allen Lee</author><pubDate>Sun, 21 Dec 2008 13:18:00 GMT</pubDate><guid>http://www.cnblogs.com/allenlooplee/archive/2008/12/21/1359421.html</guid><wfw:comment>http://www.cnblogs.com/allenlooplee/comments/1359421.html</wfw:comment><comments>http://www.cnblogs.com/allenlooplee/archive/2008/12/21/1359421.html#Feedback</comments><slash:comments>20</slash:comments><wfw:commentRss>http://www.cnblogs.com/allenlooplee/comments/commentRss/1359421.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/allenlooplee/services/trackbacks/1359421.html</trackback:ping><description><![CDATA[<p><span style="font-family: Arial, Helvetica, sans-serif;">
<p style="text-align: center"><span style="color:#0070c0; font-size:20pt"><strong>WM有约（四）：处理屏幕旋转 </strong></span></p>
<p>&nbsp;</p>
<p style="text-align: right"><span style="color:#0070c0"><strong><em>Written by Allen Lee </em></strong></span></p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>如果用户旋转屏幕&hellip;&hellip; </strong></span></p>
<p>首先，运行一下应用程序： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM1.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 1 </strong></span></p>
<p>接着，旋转一下屏幕： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM2.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 2 </strong></span></p>
<p>噢，"下一次"被挤下去了，屏幕也出现了滚动条，然而，屏幕上仍有许多空白地方未被利用，怎么办？ </p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>支持屏幕旋转的控件 </strong></span></p>
<p>以前，patterns &amp; pactices提供了一个Mobile Client Software Factory，里面有一个OrientationAware控件，可以帮我们应对这种情况。现在，Clarius Consulting提供了一个专门用来应对这个问题的<a href="http://www1.orientationaware.net/downloads.html">Orientation Aware Control组件</a>，下面我们将会探讨如何使用Community Edition来解决这个问题。 </p>
<p>安装好Orientation Aware Control组件之后，Add New Item对话框里会有一个Orientation Aware Control： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM3.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 3 </strong></span></p>
<p>选中这个项，给它一个名字，然后单击Add，你会得到一个空白的控件设计界面，把HomeForm上的MonthCalendar控件和两个Label控件复制到HomeControl上，调整它们的位置，使它们居中： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM4.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 4 </strong></span></p>
<p>接着，右键单击控件的空白处，选择Rotate： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM5.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 5 </strong></span></p>
<p>控件的设计界面将会旋转： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM6.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 6 </strong></span></p>
<p>这幅图运行后的样子实际上就是图2，现在把控件上的东西重新调整一下： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM7.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 7 </strong></span></p>
<p>需要说明的是，我把控件的Size设为和HomeForm的一样，竖着的和横着的分别对应起来。然后，把控件旋转回去，把它添加到HomeForm上，并把它的Dock属性设为Fill： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM8.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 8 </strong></span></p>
<p>哎哟，有点不堪入目啊，不知道运行起来会不会也是这样呢？好，我们来看看： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM9.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 9 </strong></span></p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM10.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 10 </strong></span></p>
<p>嗯，处理得还算不错，虽然横屏时还是出现了滚动条。注意，由于目前HomeControl只是一个空壳，所以之前实现的所有功能都不会在上面产生作用，当然就包括"下一次"没有任何显示了。 </p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>把业务逻辑的代码分离出来 </strong></span></p>
<p>目前，用户界面的代码和业务逻辑的代码纠缠在一起，HomeForm.cs里的代码更是超过四百行，这无疑会为将来的维护带来问题（虽然我不打算维护这个示范程序，但门面话还是要说一下的，哈哈），于是，接下来将会尝试把用户界面的代码分离到HomeControl里，而业务逻辑的代码将会创建一个新的DateManager类来存放。 </p>
<p>DateManager类将会负责日期的存取、文件管理和预测逻辑；而HomeControl将会负责MonthCalendar上钉住日期的更新、"下一次"的更新和获取用户选中的日期。 </p>
<p>首先是文件管理，HomeForm.cs里的InitializeFile、GetPinnedDatesFilePath、GetExcludedDatesFilePath、GetIncludedDatesFilePath和GetFilePath等方法可以直接复制到DateManager类里，其中InitializeFile将会在DateManager类的构造函数里调用，以便创建用来储存日期的文件。 </p>
<p>接着是日期的存取，HomeForm.cs里的LoadDates、SaveDates、LoadPinnedDates、SavePinnedDates、LoadIncludedDates、SaveIncludedDates、LoadExcludedDates和SaveExcludedDates等方法都可以直接复制到DateManager类里，然而，由于DateManager类是用户界面中立的，于是需要对外提供Load和Save两个方法，以便相关的窗体/控件调用： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM11.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 1 </strong></span></p>
<p>最后就是"下一次"的预测了，HomeForm.cs里的CalculateNextTime、CalculateNextSaturday、ApplyInclusion、ApplyExclusion和ApplyAdjustment等方法都可以直接复制到DateManager类里，另外，我们需要把CalculateNextTime方法变成公有方法，以便HomeControl调用。 </p>
<p>慢着！我怎么添加日期？噢，差点忘记了~~~我们知道，DateManager并不需要关心你是否在添加一个周末，它的任务只是把你给它的一组日期添加到对应的集合，于是我们可以这样实现Pin、Include和Exclude三个方法： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM12.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 2 </strong></span></p>
<p>再等等！我怎么获取要显示在MonthCalendar上的钉住日期？很简单，只需要提供一个PinnedDates属性就可以了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM13.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 3 </strong></span></p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>把用户界面的代码分离出来 </strong></span></p>
<p>我们知道，HomeControl的职责是显示钉住日期、更新"下一次"和在用户选中日期时发出通知，对于前两个，我们只需简单地提供两个属性就可以了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM14.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 4 </strong></span></p>
<p>对于后面那个职责，我们需要提供一个SelectedDatesChanged事件，它会在MonthCalendar控件的DateChanged事件的基础上加上是否为周末的判断逻辑。为了实现这个事件，我们需要创建一个SelectedDatesEventArgs类： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM15.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 5 </strong></span></p>
<p>其中，CalculateWeekend和CalculateRange两个方法是从HomeForm.cs里直接复制过来的。有了这些准备，我们就可以着手实现SelectedDatesChanged事件了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM16.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 6 </strong></span></p>
<p>还差什么呢？对了，是配置信息的设置，把HomeForm.cs里的SetupOptions方法直接复制过来，并把它变成公有方法。 </p>
<p>最后就是着手整理HomeForm.cs了。首先，应用程序启动的时候，我们需要设置HomeControl的配置信息，并调用DateManager的Load方法： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM17.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 7 </strong></span></p>
<p>当主窗体的Deactivate事件触发时和用户单击Save菜单项时，调用DateManager的Save方法： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM18.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 8 </strong></span></p>
<p>当主窗体的Activated事件触发时，更新HomeControl上的"下一次"： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM19.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 9 </strong></span></p>
<p>当用户通过OptionForm修改了配置信息时，调用HomeControl的SetupOptions方法读取配置信息： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM20.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 10 </strong></span></p>
<p>当用户在HomeControl里的MonthCalendar上做出选择，我们将会把用户选中的日期保存到一个私有变量里，然后根据SelectedDatesEventArgs的ArePast属性设置Pin、Include和Exclude三个菜单项的Enable属性： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM21.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 11 </strong></span></p>
<p>当用户单击Pin、Include和Exclude三个菜单项时，将会调用DateManager对应的方法来处理： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM22.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 12 </strong></span></p>
<p>最后，HomeForm.cs里的其他代码，包括辅助方法和原用户界面上的控件都要删除。 </p>
<p>现在，是时候运行一下应用程序了，看看改了这么多有没有改坏了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM23.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 11 </strong></span></p>
<p>噢，钉住日期没有显示出来！没问题，只需要在主窗体的Activated事件触发时和用户单击Pin菜单项时把DateManager的PinnedDates属性的值赋给HomeControl的DataSource属性就可以了。再次运行应用程序，这次就正常了（奇怪，为什么图10上会多出一个滚动条呢？难道是因为之前没有把窗体上原有的控件删除？）： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM24.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 12 </strong></span></p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/122108_1312_WM25.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 13 </strong></span></p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>你还想要什么？ </strong></span></p>
<p>原本还想试一下数据绑定的，不过现在看来也没有这个必要了。目前这个应用程序基本上可以投入应用了，所以下一步就是如何把它部署到设备上。下一集，我们将会探讨如何为这个应用程序开发安装包。 </p>
</span></p><img src ="http://www.cnblogs.com/allenlooplee/aggbug/1359421.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/><a href="http://job.cnblogs.com/offer/302/" target="_blank">盛大招聘.Net开发工程师 </a><br/><a href="http://www.hjbook.net/product/4041/" target="_blank">经典好书：.NET框架程序设计（修订版）</a><br/>新闻：<a href="http://news.cnblogs.com/n/44340/" target="_blank">《南方都市报》:网店新规或成一纸空文</a><br/>导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://job.cnblogs.com/" target="_blank">招聘</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>WM有约（三）：下一次是什么时候？</title><link>http://www.cnblogs.com/allenlooplee/archive/2008/12/17/1356431.html</link><dc:creator>Allen Lee</dc:creator><author>Allen Lee</author><pubDate>Wed, 17 Dec 2008 00:16:00 GMT</pubDate><guid>http://www.cnblogs.com/allenlooplee/archive/2008/12/17/1356431.html</guid><wfw:comment>http://www.cnblogs.com/allenlooplee/comments/1356431.html</wfw:comment><comments>http://www.cnblogs.com/allenlooplee/archive/2008/12/17/1356431.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.cnblogs.com/allenlooplee/comments/commentRss/1356431.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/allenlooplee/services/trackbacks/1356431.html</trackback:ping><description><![CDATA[<p><span style="font-family: Arial, Helvetica, sans-serif;">
<p style="text-align: center"><span style="color:#0070c0; font-size:20pt"><strong>WM有约（三）：下一次是什么时候？ </strong></span></p>
<p>&nbsp;</p>
<p style="text-align: right"><span style="color:#0070c0"><strong><em>Written by Allen Lee </em></strong></span></p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>不要留恋过去 </strong></span></p>
<p>怎样才能约束用户，不让其选择过去的日期呢？有一个很傻的办法，就是每次启动应用程序的时候，自动把MonthCalendar控件的MinDate属性的值设为今天。这样虽然禁止了用户选择过去的日期，却带来另外一些问题： </p>
<ul>
<li>当月之前的日期无法查看。 </li>
<li>和选项窗体的Min Date设置相冲。 </li>
</ul>
<p>有鉴于此，我们采用另一种办法，就是在用户选中某个日期时，判断这个日期是否已经过去，若是，则禁用Pin菜单项，若否，则启用Pin菜单项。那么，如何获知用户选中了某个日期？最简单的办法就是使用MonthCalendar控件的DateChanged事件： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM1.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 1 </strong></span></p>
<p>运行应用程序，你会发现，当我选中今天或者将来的日期时，Pin菜单项是启用的（图1），而当我选中过去的日期时，Pin菜单项则是禁用的（图2）： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM2.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 1 </strong></span></p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM3.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 2 </strong></span></p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>这（几）天不要选 </strong></span></p>
<p>在继续之前，我们有必要搞清楚，"排除某（几）天"究竟是什么意思。在这里，"排除某（几）天"并不是指禁止用户选中那（几）天，而是指那（几）天不在计划中，但我们很清楚，计划赶不上变化，或许那（几）天真正到来的时候又可以选了。 </p>
<p>和之前的"钉住日期"相比，"排除日期"除了无需在MonthCalendar控件上有所反映之外，其它部分基本上是一样的，它支持排除某天、连续的几天和某个周末，用来保存被排除的日期的文件和应用程序放在同一个文件夹里，应用程序在启动的时候会检查这个文件是否存在，如果不存在就创建一个空白的文件。从上面这些描述来看，"排除日期"和"钉住日期"在很大程度上共享着相同的代码，于是，接下来就是考虑如何重用现有的代码并实现新的功能。 </p>
<p>首先要处理的是LoadPinnedDates和SavePinnedDates两个方法（参见<a href="/allenlooplee/archive/2008/12/10/1352372.html">《WM有约（一）：你好，CF》</a>的代码5和代码6），我们提取这两个方法的代码，并创建两个新的方法： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM4.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 2 </strong></span></p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM5.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 3 </strong></span></p>
<p>这样，LoadPinnedDates和SavePinnedDates两个方法就可以简化为分别对LoadDates和SaveDates两个方法的调用了，而LoadExcludedDates和SaveExcludedDates两个方法也可以如法炮制了。在着手实现这些方法之前，我们还需要提供一个东西，那就是文件的路径，也是我们接下来需要做的事情&mdash;&mdash;改造GetFilePath方法（参见<a href="/allenlooplee/archive/2008/12/10/1352372.html">《WM有约（一）：你好，CF》</a>的代码4），改造后的GetFilePath方法将会用来映射文件路径： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM6.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 4 </strong></span></p>
<p>有了这些准备，我们就可以着手实现LoadExcludedDates和SaveExcludedDates两个方法了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM7.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 5 </strong></span></p>
<p>至于LoadPinnedDates和SavePinnedDates两个方法的新版本就留给读者自行处理了。 </p>
<p>接着就是"排除日期"的核心功能&mdash;&mdash;ExcludeWeekend和ExcludeRange两个方法了，它们与PinWeekend和PinRange两个方法（参见<a href="/allenlooplee/archive/2008/12/10/1352372.html">《WM有约（一）：你好，CF》</a>的代码1和代码2）的最大区别就是不需要把操作结果反映在MonthCalendar控件上，而它们的共同之处是都要计算具体的日期并把它们添加到对应的集合里。我们先来看看计算具体的日期这部分功能，它分为两种情况，一种是计算周末的，另一种是计算两个日期之间的，如果这两个日期相同，则视为一天，于是，我们可以创建CalculateWeekend和CalculateRange两个方法来分别负责这两种情况： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM8.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 6 </strong></span></p>
<p>有了这些准备，我们就可以着手实现ExcludeWeekend和ExcludeRange两个方法了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM9.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 7 </strong></span></p>
<p>至于PinWeekend和PinRange两个方法的新版本就留给读者自行处理了。 </p>
<p>还差什么呢？对，用户界面，没有这个，我们辛苦了这么久就白干了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM10.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 3 </strong></span></p>
<p>还有Exclude菜单项的相关代码： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM11.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 8 </strong></span></p>
<p>噢，别忘了在InitializeFile方法（参见<a href="/allenlooplee/archive/2008/12/10/1352372.html">《WM有约（一）：你好，CF》</a>的代码12）里添加检查文件是否存在的代码，以及在适当的地方添加保存数据的代码，否则&hellip;&hellip; </p>
<p>运行应用程序，选中2009年1月17日到2009年1月31日之间的日期，然后单击Exclude菜单项： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM12.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 4 </strong></span></p>
<p>通过资源管理器找到ExcludedDates.txt文件，然后用Word Mobile查看里面的内容，结果发现只有下面3天！ </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM13.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 5 </strong></span></p>
<p>问题出在哪里？原来，我选中的那几天的开始日期恰好是星期六，于是应用程序"自作聪明"地把它视为一般周末！如何解决这个问题？回到代码8，我们知道，ExcludeWeekend方法的调用需要满足两个条件，第一个是用户只选中了一天，另一个则是这天是星期六。要知道用户是否只选中了一天，我们只需要看看SelectionStart和SelectionEnd两个属性是否同一天： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM14.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 9 </strong></span></p>
<p>再次运行应用程序，这次就正常了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM15.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 6 </strong></span></p>
<p>需要提醒的是，Pin菜单项的相关代码由于应用了相同的逻辑，于是也存在相同的问题，不过解决方法是一样的，所以这里就不说了。另外，因为"排除日期"不像"钉住日期"那样会在用户界面上有所反映，所以当我们单击Exclude菜单项时，一切都在后台完成，如果用户不知情的话，感觉起来就像什么也没干一样，为了增强用户体验，最好就显示一个消息框告诉用户日期已被排除。 </p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>这（几）天应该选 </strong></span></p>
<p>由于"包含日期"和"排除日期"极其相似，再加上我们在实现"排除日期"时提取的公共代码也适用于"包含日期"，于是，我们可以用非一般的速度来实现"包含日期"的内部逻辑： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM16.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 10 </strong></span></p>
<p>至于用户界面，我们同样为它添加一个Include菜单项： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM17.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 7 </strong></span></p>
<p>而这个菜单项的相关代码如下： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM18.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 11 </strong></span></p>
<p>其它东西，例如应用程序启动的时候检查用来保存日期的文件是否存在、读取保存的日期和在适当的时候保存日期，和前面的实现大同小异，这里就不细说了。 </p>
<p>运行应用程序，选中2009年2月14日，然后单击Include菜单项： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM19.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 8 </strong></span></p>
<p>由于这天刚好是星期六，所以应用程序执行了包含周末的逻辑，这也是预期的行为： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM20.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 9 </strong></span></p>
<p>到了这里，你可能会认为"排除日期"和"包含日期"也是时候告一段落了，但事实上我们还有一个问题需要处理。试想一下，如果我对同一个日期先后执行包含和排除操作，那么应用程序是否应该分别在m_IncludedDates和m_ExcludedDates两个集合里登记这个日期？我们知道，"排除日期"和"包含日期"都是用来反映计划的调整，比起分别在两个地方登记同一个日期，执行抵消操作或许更有意义。举个例子，刚才我包含了2009年2月14日，现在我要排除这个日期，那么应用程序应该从m_IncludedDates里删除这个日期而不是向m_ExcludedDates添加这个日期。怎么样？是不是很简单？然而，这个东西实现起来一点都不容易，因为我们通常操作的是一组日期而不是单个日期，如果我们足够好运，那么要抵消的日期集合会是被抵消的日期集合的子集，如果我们不够运气，那么&hellip;&hellip;一般地，如果我们要包含一组日期，那么我们要先检查m_ExcludedDates是否包含了这些日期的部分或全部，如果是，则从m_ExcludedDates里删除相同部分，剩下的才添加到m_IncludedDates。以IncludeWeekend方法（参见代码10）为例，从最初的monthCalendar1.SelectionStart到最后的m_IncludedDates.AddRange需要经过如下四步： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM21.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 10 </strong></span></p>
<p>其中，第三步的Subtract方法是解决这个问题的关键，那么，如何实现这个方法呢？我们知道，List&lt;T&gt;本身没有提供这个方法，要想达到这样的效果就得使用C# 3.0的扩展方法了。下面来看看我的实现： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM22.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 12 </strong></span></p>
<p>对于second里的每个日期，Subtract方法试图从first里删除，并通过Remove方法的返回值判断删除操作是否成功，如果不成功，就意味着这个日期应该添加到m_IncludedDates里，于是返回这个日期。有了这些准备，我们就可以着手修改IncludeWeekend方法： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM23.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 13 </strong></span></p>
<p>另外，IncludeRange、ExcludeWeekend和ExcludeRange等方法也需要修改，不过都是大同小异，所以就不一一列举了。 </p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>下一次是什么时候？ </strong></span></p>
<p>下一次&hellip;&hellip;在MonthCalendar控件下面&hellip;&hellip; </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM24.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 11 </strong></span></p>
<p>通常，这种可预测的"下一次"都意味着计算周期的存在，对于这个应用程序，这个周期是两周，以星期六为计算基准，比如说，假设上图的5、6和7三天已被钉住，那么 下次应该被钉住的日期将会是19、20和21三天，于是"Next time:"下面的Label就应该显示"2008年12月19日"。这个计算过程的一般形式如下图所示： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM25.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 12 </strong></span></p>
<p>有了这些分析，我们就可以着手实现CalculateNextTime方法了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM26.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 14 </strong></span></p>
<p>故事到此结束了吗？当然不是，前面我们花了这么多精力来实现"排除日期"和"包含日期"，如果仅仅用来保存一些日期，那么我也未免太无聊了。 </p>
<p>首先，我们来看看"包含日期"将会如何影响"下一次"的计算，还是借用图11，假设19、20和21三天已被钉住，今天是23号，那么"Next time:"下面的Label应该显示"2009年1月2日"，但如果26、27和28三天已被包含，那么"Next time:"下面的Label就应该显示"2008年12月26日"了。简而言之，在时间轴上排在前面的"包含日期"将会取代使用默认算法计算出来的日期： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM27.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 15 </strong></span></p>
<p>接着，我们再来看看"排除日期"将会如何影响"下一次"的计算，假设2、3和4三天已被钉住，今天是6号，那么"Next time:"下面的Label应该显示"2009年1月17日"，但如果17到31之间的日期已被排除，那么"Next time:"下面的Label就应该显示"2009年2月7日"了。 </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM28.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 13 </strong></span></p>
<p>简而言之，"排除日期"会导致使用默认算法计算出来的日期逐周往后推，直到计算出来的日期没被排除为止： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM29.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 16 </strong></span></p>
<p>由此可见，完整的CalculateNextTime方法应该包含如下四步： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM30.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 14 </strong></span></p>
<p>其中，第一步和第四步是从代码14里分解出来的： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM31.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 17 </strong></span></p>
<p>有了这些准备，我们就可以着手实现完整的CalculateNextTime方法了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM32.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 18 </strong></span></p>
<p>最后，终于到最后了，我们要把计算结果显示在应用程序主窗体的"Next time:"下面，那么，我们应该在什么时候显示呢，又应该在什么时候更新呢？用Activated事件！你可能会问，为什么不用Load事件呢？这是因为当用户单击应用程序右上角的关闭按钮时，应用程序实际上只是最小化到后台，当用户通过菜单或者其它方式再次启动应用程序时，实际上只是把应用程序"还原"到前台，而在这个过程里Load事件并不会被触发。事不宜迟，让我们完成本集的最后一段代码吧： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM33.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 19 </strong></span></p>
<p>运行应用程序，终于看到下一次是什么时候了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121708_0015_WM34.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 15 </strong></span></p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>你还想要什么？ </strong></span></p>
<p>在这本集里，我们花费巨大精力实现"下一次"的计算，然而，"除了'现在'，你永远不能生活在任何其他时刻，你所能得到的只是现在的时光，未来在到来时也只不过是另一个现在"（[美]韦恩&middot;W&middot;戴尔，《你的误区》），好好把握每一个"现在"，你将会得到一个满意的轨迹。 </p>
<p>到目前为止，应用程序的用户界面都是为"垂直"屏幕设计的，有没有想过，假如用户旋转设备的屏幕，使之变成"水平"的，将会发生什么事情呢？下一集，我们将会探讨这个问题及其解决方案，我们还会尝试创建用户控件来封装MonthCalendar控件、"下一次"Label以及相关的代码，如果"时间"允许的话，我们还会看看如何在这个用户控件上实现数据绑定。 </p>
</span></p><img src ="http://www.cnblogs.com/allenlooplee/aggbug/1356431.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/><a href="http://job.cnblogs.com/offer/302/" target="_blank">盛大招聘.Net开发工程师 </a><br/><a href="http://www.hjbook.net/product/4041/" target="_blank">经典好书：.NET框架程序设计（修订版）</a><br/>新闻：<a href="http://news.cnblogs.com/n/44339/" target="_blank">腾讯加入社交网站市场争夺战</a><br/>导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://job.cnblogs.com/" target="_blank">招聘</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>WM有约（二）：配置信息</title><link>http://www.cnblogs.com/allenlooplee/archive/2008/12/14/1354492.html</link><dc:creator>Allen Lee</dc:creator><author>Allen Lee</author><pubDate>Sat, 13 Dec 2008 16:00:00 GMT</pubDate><guid>http://www.cnblogs.com/allenlooplee/archive/2008/12/14/1354492.html</guid><wfw:comment>http://www.cnblogs.com/allenlooplee/comments/1354492.html</wfw:comment><comments>http://www.cnblogs.com/allenlooplee/archive/2008/12/14/1354492.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cnblogs.com/allenlooplee/comments/commentRss/1354492.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/allenlooplee/services/trackbacks/1354492.html</trackback:ping><description><![CDATA[<p><span style="font-family: Arial, Helvetica, sans-serif;">
<p style="text-align: center"><span style="color:#0070c0; font-size:20pt"><strong>WM有约（二）：配置信息 </strong></span></p>
<p>&nbsp;</p>
<p style="text-align: right"><span style="color:#0070c0"><strong><em>Written by Allen Lee </em></strong></span></p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>添加配置文件 </strong></span></p>
<p>首先，向项目添加一个Options.xml文件，这个文件将会用来储存应用程序的配置信息： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM1.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 1 </strong></span></p>
<p>接着，把Options.xml的Copy to Output Directory属性的值改为Copy if newer： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM2.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 2 </strong></span></p>
<p>然后，就是为Options.xml添加配置信息了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM3.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 1 </strong></span></p>
<p>那么，我们如何使用这个文件呢？关于这个问题，我首先想到的是为它创建一个OptionManager类，在我的想象里，它应该是这样使用的： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM4.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 2 </strong></span></p>
<p>在继续之前，我想请你思考一个问题：你会如何实现OptionManager的Options成员呢？下面是我的做法，使用了单例模式和索引器： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM5.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 3 </strong></span></p>
<p>当然，你也可以学ConfigurationManager类那样，通过AppSettings静态属性返回一个NameValueCollection对象。接下来的问题是，如何访问Options.xml呢？由于Options.xml上面的配置信息实际上只是一组键/值对，于是你可以考虑在OptionManager的构造函数里把它们全部读到一个内部的Dictionary对象里： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM6.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 4 </strong></span></p>
<p>LoadOptions方法使用了LINQ to XML来读取Options.xml里的配置信息，并以Dictionary的形式返回： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM7.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 5 </strong></span></p>
<p>这里使用了GetXmlPath方法来获取Options.xml的路径： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM8.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 6 </strong></span></p>
<p>OptionManager提供了一个Save方法，用于保存配置信息： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM9.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 7 </strong></span></p>
<p>其中，SaveOptions方法也使用了LINQ to XML来构建并保存配置信息： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM10.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 8 </strong></span></p>
<p>最后就是OptionManager的索引器了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM11.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 9 </strong></span></p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>设计选项窗体 </strong></span></p>
<p>说到选项窗体，你会如何设计？我想象中的选项窗体大致像这样： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM12.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 3 </strong></span></p>
<p>我用了一个NumericUpDown控件来收集MaxSelectionCount的配置信息，另外用了两个DateTimePicker控件分别收集MinDate和MaxDate的配置信息。 </p>
<p>接着，我们来看看底下两个菜单项。Cancel菜单项比较简单，仅仅把窗体的DialogResult设为Cancel： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM13.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 10 </strong></span></p>
<p>而OK菜单项的任务就多一点，它要保存用户的配置信息： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM14.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 11 </strong></span></p>
<p>接着就是修改一下主窗体的菜单： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM15.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 4 </strong></span></p>
<p>Save菜单项和以前一样，而Options菜单项将会打开选项窗体： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM16.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 12 </strong></span></p>
<p>现在，我们来运行一下这个应用程序： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM17.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 5 </strong></span></p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM18.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 6 </strong></span></p>
<p>有问题！选项窗体打开的时候，上面的控件没有反映配置文件里面的信息，这是因为我没有实现这样的逻辑，同样地，这个问题也会发生在主窗体身上，这意味着无论我们如何修改配置文件，重新启动应用程序之后，主窗体将会恢复默认配置，何等严重！ </p>
<p>首先是选项窗体打开的时候需要读取配置信息到对应的控件上： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM19.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 13 </strong></span></p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM20.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 14 </strong></span></p>
<p>由于读取配置信息的代码和代码12里的那部分是相同的，所以我把它提取出来，放在SetupOptions方法里，这样，代码12就简化为： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM21.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 15 </strong></span></p>
<p>再次运行应用程序： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM22.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 7 </strong></span></p>
<p>这次正常了。不过还有一个小问题，选项窗体里保存配置信息的代码属于OK菜单项而不是选项窗体的，当用户单击窗体右上角的OK按钮时，虽然窗体的DialogResult属性返回OK，但实质上没有执行任何保存配置信息的操作，这就导致了窗体行为的不一致。解决这个问题的办法有两个，一个是去掉窗体右上角的OK按钮，另一个是把OK菜单项里保存配置信息的代码放到窗体层面上做。前一种做法很简单，只需要把选项窗体的ControlBox属性设为false就行了。至于后一种做法，在继续讨论之前，我想先考一考你，我应该选择Deactivate事件还是Closing事件呢？答案是两个都可以，因为选项窗体是通过ShowDialog方法打开的，关闭这样的窗体是真的关闭了而不是像主窗体那样最小化到后台，所以我们可以放胆使用Closing事件： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121308_1546_WM23.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 16 </strong></span></p>
<p>还有一个不是问题的问题，就是当你关闭选项窗体时，你会发现画面突然停顿，你知道这个时候OptionManager在保存配置信息，但用户并不知道，这可能会给用户留下不好的印象/感受，要让用户知道后台正在执行任务，最简单的办法就是在屏幕正中显示等待指针，当然，任务完成之后记得去掉等待指针，否则&hellip;&hellip; </p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>你还想要什么？ </strong></span></p>
<p>下一集，除了上一集剩下的两个需求之外，我们还会探讨两个新的需求： </p>
<ul>
<li>支持日期的包含，比如说2009年2月14日被指定为届时要选的日期。 </li>
<li>如果指定一个周期，应用程序将会综合排除逻辑、包含逻辑和这个周期计算下一个将要被选中的日期，并显示在主界面上。 </li>
</ul>
</span></p><img src ="http://www.cnblogs.com/allenlooplee/aggbug/1354492.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/><a href="http://job.cnblogs.com/offer/302/" target="_blank">盛大招聘.Net开发工程师 </a><br/><a href="http://www.hjbook.net/product/4041/" target="_blank">经典好书：.NET框架程序设计（修订版）</a><br/>新闻：<a href="http://news.cnblogs.com/n/44338/" target="_blank">视频网站遭版权清算风暴 80家影视机构围攻土豆</a><br/>导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://job.cnblogs.com/" target="_blank">招聘</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>WM有约（一）：你好，CF</title><link>http://www.cnblogs.com/allenlooplee/archive/2008/12/10/1352372.html</link><dc:creator>Allen Lee</dc:creator><author>Allen Lee</author><pubDate>Wed, 10 Dec 2008 15:48:00 GMT</pubDate><guid>http://www.cnblogs.com/allenlooplee/archive/2008/12/10/1352372.html</guid><wfw:comment>http://www.cnblogs.com/allenlooplee/comments/1352372.html</wfw:comment><comments>http://www.cnblogs.com/allenlooplee/archive/2008/12/10/1352372.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.cnblogs.com/allenlooplee/comments/commentRss/1352372.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/allenlooplee/services/trackbacks/1352372.html</trackback:ping><description><![CDATA[<p><span style="font-family: Arial, Helvetica, sans-serif;">
<p style="text-align: center"><span style="color:#0070c0; font-size:20pt"><strong>WM有约（一）：你好，CF </strong></span></p>
<p>&nbsp;</p>
<p style="text-align: right"><span style="color:#0070c0"><strong><em>Written by Allen Lee </em></strong></span></p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>来，和CF打个招呼！ </strong></span></p>
<p>在这个系列里，我将会使用Visual Studio 2008来创建Windows Mobile 6 Professional应用程序，如果你也想在自己的计算机上体验一下，那么请先安装Visual Studio 2008和<a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=06111A3A-A651-4745-88EF-3D48091A390B&amp;displaylang=en">Windows Mobile 6 Professional SDK</a>。 </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF1.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 1 </strong></span></p>
<p>创建一个新的项目，接着选择Visual C#\Smart Device\Smart Device Project项目模板，然后输入项目名称，最后单击OK打开Add New Smart Device Project对话框： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF2.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 2 </strong></span></p>
<p>在Add New Smart Device Project对话框里，选择Windows Mobile 6 Professional SDK作为目标平台，选择.NET Compact Framework Version 3.5作为目标平台上运行的CF的版本，选择Device Application作为项目模板，单击OK完成项目的创建： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF3.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 3 </strong></span></p>
<p>在继续之前，让我们先把目标设备设为Windows Mobile 6 Professional Emulator，把Form1的FormFactor属性设为Windows Mobile 6 Professional： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF4.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 4 </strong></span></p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF5.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 5 </strong></span></p>
<p>好了，我们可以把目光投向Form1了。现在，把一个Label拖到Form1上，接着把它的Text属性设为"Hello, CF!"，然后调整一下它的位置和对齐方式： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF6.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 6 </strong></span></p>
<p>一切就绪之后，就可以单击Start Debugging了，此时，你会看到Deploy Alt对话框，请确保你所选择的是Windows Mobile 6 Professional Emulator，然后单击Deploy： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF7.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 7 </strong></span></p>
<p>在部署Alt之前，Visual Studio 2008会检查目标设备上是否部署了.NET Compact Framework 3.5，如果没有，它会先部署.NET Compact Framework 3.5，再部署Alt。一切顺利的话，你将会看到运行在模拟器上的Alt： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF8.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 8 </strong></span></p>
<p>等一下！谁要这种东西啊？ </p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>那么，你想要的是什么？ </strong></span></p>
<p>首先，把中间那个Label删除，在Form1的正中放一个MonthCalendar。接着，给Form1添加Pin和Save两个菜单项。我想要的界面大致如下： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF9.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 9 </strong></span></p>
<p>当我选中某天并单击Pin时，MonthCalendar中的那天就会加粗；当我选中连续的几天并单击Pin时，MonthCalendar中的那几天都会加粗；而当我选中某个周六时，MonthCalendar中的那个周六及其相邻的周五和周日都会加粗。当我单击Save时，MonthCalendar中所有加粗的日期都会保存下来，每次启动应用程序时，保存的日期将会在MonthCalendar上加粗显示。 </p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>钉住日期 </strong></span></p>
<p>当选中的日期是周六时，我们可以计算它的前一天和后一天，接着把它们添加到某个用来储存选中日期的集合里，然后把这个集合里的日期关联到MonthCalendar的BoldedDates属性上： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF10.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 1 </strong></span></p>
<p>而对于另外两种情况，我们可以统一处理。当我们选中连续的几天时，MonthCalendar并非告诉我们具体是哪几天，而是通过SelectionStart和SelectionEnd两个属性告诉我们首尾两天，中间的日期需要我们自己计算。如果SelectionStart和SelectionEnd两个属性是同一天，那就意味着只选中一天。于是，我们可以这样： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF11.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 2 </strong></span></p>
<p>有了PinWeekend和PinRange两个方法，我们就可以处理Pin的Click事件了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF12.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 3 </strong></span></p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>保存选中的日期和读取保存的日期 </strong></span></p>
<p>假设用来保存日期的文件和应用程序放在同一个文件夹里，那么我们可以通过下面代码获取文件的路径： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF13.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 4 </strong></span></p>
<p>要把一组日期保存到文件里，我们可以这样： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF14.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 5 </strong></span></p>
<p>而要读取文件里的日期，我们可以这样： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF15.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 6 </strong></span></p>
<p>那么，我们在什么时候调用这两个方法呢？我们知道，Form1上有一个Save菜单项，当用户单击这个菜单项的时候，我们应该调用SavePinnedDates方法： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF16.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 7 </strong></span></p>
<p>另外，当用户关闭应用程序时，我们也应该调用SavePinnedDates方法： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF17.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 8 </strong></span></p>
<p>你可能会问："为什么不用Closing事件？"这是因为当我们单击窗体右上角的交叉时，应用程序实际上只是最小化到后台而不是关闭，此时触发的是Deactivate事件。 </p>
<p>至于日期的读取，我们可以在Form1的Load事件触发时执行： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF18.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 9 </strong></span></p>
<p>还差什么呢？嗯，或许你已经想到了，当应用程序第一次运行的时候，用来保存日期的文件并不存在，而此时应用程序却要从这个文件中读取数据，那将会引发异常。对于这个问题，我们有两个应对措施，一个是每次启动应用程序的时候检查这个文件是否存在，如果不存在就创建一个空白的文件；另一个是在应用程序的安装程序里包含一个空白的文件，安装应用程序的时候把这个文件一并安装到应用程序文件夹里。这两种方案各有利弊，这里我选择第一种方案（我这样做并没有什么特别的理由，只是想这样做而已）： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF19.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 10 </strong></span></p>
<p>这个方法将会在Form1的构造函数里调用： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF20.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 11 </strong></span></p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>模拟运行 </strong></span></p>
<p>接下来就是在模拟器里运行这个应用程序，步骤大体上和前面的"Hello, CF!"类似，你要做的只是单击Start Debugging。噢，天哪，应用程序的主窗体都还没出来就抛了个异常： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF21.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 10 </strong></span></p>
<p>原来，我没有释放InitializeFile方法里创建的文件，GC也没来得及帮我"擦屁股"，所以当LoadPinnedDates方法试图读取文件的时候就出错了。知道问题的症结就好办了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF22.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>代码 12 </strong></span></p>
<p>重新执行调试，你将会看到应用程序的主窗体： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF23.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 11 </strong></span></p>
<p>试一下钉住日期的功能： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF24.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 12 </strong></span></p>
<p>基本上可以过关，但有一个小问题，就是最多只能连续选中7天，这个问题可以通过修改MonthCalendar的MaxSelectionCount属性来解决。 </p>
<p>单击Save之后，通过资源管理器找到PinnedDates.txt文件，用Word Mobile打开后可以看到选中的日期都保存下来了： </p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/allenlooplee/121008_1548_WMCF25.png" /> </p>
<p><span style="color:#4f81bd; font-size:9pt"><strong>图 13 </strong></span></p>
<p>&nbsp;</p>
<p><span style="color:#0070c0"><strong>你还想要什么？ </strong></span></p>
<p>"我不知道，但我随时都可能会有新的想法。"我不知道是否有办法在开始的时候就能收集到我的（绝）大部分需求，但相比之下，我更喜欢边做边用边修改的做法。我还想要什么呢？现在我的脑子里能想到的是： </p>
<ul>
<li>可以配置最大连续选择范围。 </li>
<li>可以配置允许选择的日期范围，比如说不能选择2008年10月17日之前的日期。 </li>
<li>过去的日期不能选择，比如说不能选择昨天。 </li>
<li>支持日期的排除，比如说2009年1月17日到2009年1月31日不能选。 </li>
<li>（其它） </li>
</ul>
<p>下一集，我们将会探讨如何实现这些需求。</p>
</span></p><img src ="http://www.cnblogs.com/allenlooplee/aggbug/1352372.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/><a href="http://job.cnblogs.com/offer/302/" target="_blank">盛大招聘.Net开发工程师 </a><br/><a href="http://www.hjbook.net/product/4041/" target="_blank">经典好书：.NET框架程序设计（修订版）</a><br/>新闻：<a href="http://news.cnblogs.com/n/44337/" target="_blank">携程增持如家股份至8.72%</a><br/>导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://job.cnblogs.com/" target="_blank">招聘</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item></channel></rss>