破宝

我是一块破破烂烂的宝贝石头。
随笔 - 94, 评论 - 1281, 引用 - 52

导航

关于

自选精华版 RECOMMENDATIONS
留言板 GUESTBOOK

本人 blog 文章、图片及其他资源等,除另有声明外,均遵循以下原则向全球(当然包括朝鲜、古巴、利比亚等国)共享:

1。欢迎转载、复制、传播、引用,但转载、复制(包括但不仅限于作为参考资料复制到本地)、传播、引用同时必须在显著位置注明作者(破宝/percyboy)和文章原始 URL 地址等信息。但商业转载、复制、传播(尤指用于图书、光盘等媒体的部分或全部),须事先征得本人的许可。

2。文章以“现状”提供,不为由于使用本站资源而造成的任何损失而负责,仅提供力所能及的咨询和参考意见。

3。关于修改:允许您将本 blog 中的资源作为参考资料复制时的一定修改,但仍须保留作者和出处信息;其他情况下的修改(包括修改后再发布),须和本人确认许可。
 

标签

每月存档

最新留言

广告

【第1页/共10页,94条】
首页
前页
1
2009年06月15日

很早就知道 Math.Round 方法实际上并不是我们上学时学到的“四舍五入”,而是 IEEE 标准定义的“银行家舍入”算法,通俗说法是“四舍六入五成双”(1.5→2, 4.5→4)。

当需要用到“四舍五入”算法时,.NET 1.x 中是需要自己实现(比如一种思路:正数加0.5后Math.Floor,负数减0.5后Math.Ceiling)。.NET 2.0 开始,Math.Round 方法提供了一个枚举选项 MidpointRounding.AwayFromZero 可以用来实现传统意义上的“四舍五入”。即: Math.Round(4.5, MidpointRounding.AwayFromZero) = 5。

不过MSDN文档中这个MidpointRounding.AwayFromZero的描述是“当一个数字是其他两个数字的中间值时,会将其舍入为两个值中绝对值较小的值。”弄的我很长时间没反应过来。

实际上从 .NET 2.0 发布以来,网上已经有很多人都指出了这个翻译错误(原文实际是 When a number is halfway between two others, it is rounded toward the nearest number that is away from zero.),实际上应该是取绝对值较大的值

可是很遗憾,.NET 3.0, .NET 3.5 的文档中,这个翻译错误依然没有改正。

P.S.附带地,你有没有注意过:Math.Floor(2.1) = 2 而 Math.Floor(-2.1) = -3,也就是所谓“向负无穷大方向舍入”; Math.Ceiling(2.1) = 3 而 Math.Ceiling(-2.1) = -2,也就是所谓“向正无穷大方向舍入”。比如说,Ceiling 的一个使用场景:一个袋子能装 10kg,则 21kg 货物需要三个袋子,如果是 -21kg 呢?Math.Ceiling 的上述行为,是否也不符合你的预期呢?

 

posted on 2009-06-15 11:52:37 by percyboy  评论(1) 阅读(1860)

 
2009年02月20日

一直觉得 ASP.net Mobile Controls 很有用,它可以根据客户端的 UserAgent 自动判断客户端浏览器的能力,自动选择最适合的方式(WML 1.1,XHTML Basic, PC 用的 HTML, 日本 Docomo 和 Willcom 特有的 CHTML 等等)输出内容。

国内说起手机上网,一般称呼是说 WAP。其实目前市场中的很多手机早已不仅仅只支持 WML 1.1 这样的早期格式(如果你试过就知道,WML 居然无法设定字体颜色),新款手机大都开始支持 WAP 2.0 也就是支持 XHTML Basic 格式的内容,不少智能手机也开始支持直接浏览 PC 站点(当然手机屏幕小,浏览 PC 站点其实多半是“应急”,并不见得舒服)。

前一阵看到这么一条新闻说,天下网推出国内首家 WAP 2.0(XHTML)手机站点。其实,把 WAP 1.x (WML 1.1) 和 WAP 2.0 (XHTML) 分开来制作和维护,会很不方便,如果再加上 PC 版那就更多事情要做了。如果使用微软的技术,使用 ASP.net Mobile Controls 来制作手机版站点,就可以很便利的解决这一问题。

因为 ASP.net Mobile Controls 主要是基于 UserAgent 来判断客户端浏览器能力的,所以,如果使用 CMNET 这样的方式上网,是没有什么问题的,绝大多数终端都可以顺利的找到适合自己的格式(WML 1.1 或者 XHTML Basic)。可如果碰到“中国特色”的 CMWAP、UNIWAP 就要瘸腿了。

其实 CMWAP、UNIWAP 之所以“中国特色”,我的看法跟很多人不大一样。我觉得不能仅因为移动或联通人为地把手机上网分成两种方式,就说这是“中国特色”,这种情况在其他国家也有先例。比如日本 Softbank 的常用上网方式就分成三种: Yahoo!Mobile(这个相当于我们的 CMWAP、UNIWAP 方式),PC Site Browser (这个是指通过一个转换软件浏览 PC 站点的服务),PC Site Direct (这个相当于我们的 CMNET 无限制),和我们类似,这三种上网方式的价格也相差很多。

我觉得 CMWAP、UNIWAP “中国特色”是从技术角度看,因为它人为地屏蔽了客户端浏览器发出的 UserAgent!(当然它还屏蔽了很多其他的东西) 缺了 UserAgent ,强大的 ASP.net Mobile Controls 就算“瘸腿”了。

但是尽管瘸腿,ASP.net Mobile Controls 解决方案仍然具有易于制作和维护的特点(因为它制作时不需要特意区分 WML 和 XHTML Basic 等)。针对 CMWAP、UNIWAP 的特点,我推荐一个 .browser 文件:

<browsers>
  <browser id="ChineseWap" parentID="Default">
    <identification>
      <userAgent match="^\s*$" />
    </identification>
    <capabilities>
      <capability name="preferredRenderingMime"              value="text/html" />
      <capability name="preferredRenderingType"              value="xhtml-mp" />
    </capabilities>
    <controlAdapters markupTextWriterType="System.Web.UI.XhtmlTextWriter" />
  </browser>

  <browser id="ChineseWapOldModel" parentID="ChineseWap">
    <identification>
      <header name="Host" match="^wap" />
    </identification>
    <capabilities>
      <capability name="preferredImageMime"                  value="image/vnd.wap.wbmp" />
      <capability name="preferredRenderingMime"              value="text/vnd.wap.wml" />
      <capability name="preferredRenderingType"              value="wml11" />
    </capabilities>
  </browser>
</browsers>

第一个 browser 标签“ChineseWap”的意思是将 XHTML Basic 形式作为 CMWAP、UNIWAP 用户(UserAgent 为空白)的默认值。第二个 browser 标签“ChineseWapOldModel” 的意思是,如果 CMWAP、UNIWAP 用户访问的是 wap.xxxx.com/.....aspx 这种形式的 URL 时,默认输出将是 WML1.1。

优点及注意事项:

(1) 或许你已经看出来了,我的方案实际上是通过域名来区分 WAP 1.x 版和 XHTML 版的,在 UserAgent 为空的情况下,访问wap.xxxx.com 是 WAP 1.x 输出,访问主站域名是 XHTML 输出。

(2) 若 UserAgent 不为空,则无论访问哪个域名,都按 UserAgent 去自动匹配适合的形式。比如在 PC 上访问 wap.xxxx.com 返回的仍然是 HTML 而不是 WML。或者通过 CMNET 上网访问 wap.xxxx.com 时仍然会根据 UserAgent 匹配适合的版本。兼容性较好。

(3) 事前应该告诉你的用户:如果使用 CMWAP 、UNIWAP 方式浏览本站时,请根据您的手机选择适合的版本:如果您的手机仅支持 WAP 1.x 则请访问 wap.xxxx.com 域名。如果您不确定,可以先试一下主站是否能访问,访问不了再选 wap.xxxx.com 域名。

忘了说了一点:将这个 .browser 文件保存在 ~/App_Browsers 文件夹下就可以了。

posted on 2009-02-20 00:34:31 by percyboy  评论(0) 阅读(2894)

 
2008年09月24日

接上篇继续说。

如果调查一个问题:正版 Windows 和盗版 Windows 的区别是什么?我觉得会有如下的回答:

“正版的包装精良”“正版的光盘质量好,不容易坏”(这估计是普通老百姓的说法)

“正版 Windows 有微软的正版贴签”(这估计是软件店促销MM的说法)

“正版 Windows 不用担心激活的问题”(这估计是经常帮人装机的“高手”的说法)

“盗版 Windows 是不是会有错别字啊?”(这估计是深受盗版书打击的读者的说法)

“正版有彩盒包装、光盘,盗版不齐全。如果上面来检查,让他们看到这些盒子就没事儿了。”(这估计是部分老板的理解)

上面的理解其实也没啥特别的错误,不过都没答到实质。IT 行业内的人都已经很清楚了,正版 Windows 和盗版 Windows 运行起来还真是差别不大,那到底相差到哪儿了?

我的理解是,正版和盗版的差别实际上就是那一纸协议:“最终用户许可协议”,也就是常说的“授权协议”,英文的“Licence”(这个词还可以当“驾驶执照”“营业执照”等意思),从法律意义上看这是一个格式合同。它详细记载了许可的内容和范围,记载了你的权利和义务。

你买正版软件,真正花钱买的并不是那张光盘或盒子本身,而是微软给你的一份授权,或者说是你和微软之间的一份合同。判断是否是正版软件,关键不是那张光盘或盒子。你的光盘坏了,或者干脆没有光盘(比如下载的试用版,后来转正),都无所谓,关键的是你有没有那份协议、你的行为有没有超出协议书规定的许可范围。

当前的正版宣传下,有很多人不理解,我买了你的软件,我爱装几台机器就装几台机器,这有什么错?为什么政府机关或者大企业,也是一张光盘就可以同时装很多台,而我才装两三台就不可以了?实际上,虽然介质光盘及包装都是类似的,但授权范围是不同的,“合同”内容是不同的,这就是原因。

因此,我认为微软等大企业应该逐渐转向以“契约精神”“合同”“服务”“权利”“义务”“许可范围”为核心的等正版化宣传,弱化介质的作用。

我认为这么做有很多好处:

(1)很多人不能理解为什么同样是一张光盘,盗版卖4元,正版要上千块。如果转向强调“契约”“合同”“服务”“权利义务”,会比较容易理解和接受,花钱买的是“服务”,是买“执照”(就类似开店需要办营业执照)。盗版不会有售后服务,而且是“无照经营”。

(2)很多人不理解也不重视研究微软的授权体系,举几个例子:

(a) 比如 Windows Server 2003 除了软件本身的授权外,还需要 CAL (User CAL 或 Device CAL)授权。软件一般带 5 个 CAL,如果需要连接更多设备或者让更多用户使用,需要额外购买 CAL,相信很多人并不了解吧。
(b) 比如 MSDN 订阅软件里有几乎全部微软的软件 Windows, Office, Visual Studio 等等,比如这里面的 Windows Server 软件能不能作为正式服务器向外提供服务呢?应该说软件本身是没特别限制的,不过按照 MSDN 的授权协议,这么做是超出许可范围内的,也就是说违反协议、也可以称为“盗版”。(这种“盗版”跟普通人理解的“盗版”差距真大。)

只有强调“契约精神”强调“授权范围”,才能让人们重视这些授权细节,才能吸引人们去研究那复杂的授权体系,根据实际需求选择最适合的授权体系。(顺便说一句,微软的授权体系也应该简化简化,看起来很费劲。) 此处举个例子,比如说 Windows Server 2003 Enterprise Edition R2 版(注意是 R2 版)有一个细节,就是在一台安装此系统的机器上,如果使用虚拟机软件(Visual PC 或 VMware 等)安装虚拟机的话,微软许可在最多四个虚拟机里安装这同一个系统而不需要额外购买授权。这个细节对某些软件开发企业来说,还是很有用的。

希望大家读后能对“正版”“盗版”有个新的认识。

posted on 2008-09-24 08:21:42 by percyboy  评论(0) 阅读(1017)

 

最近有一条争议不小的新闻:微软(中国)在国庆节期间的促销活动,Office家庭版降价到199元。

这让我想到了我三年多前的一篇博文,梦呓:微软在中国的新定价策略——比例折算法。这个价格可以说比“比例折算法”还要实惠。不过遗憾的是,网上投票情况显示,半数以上的网民对此促销活动并不买账,认为只要比盗版价格高就绝不买正版。这也如实反映了国民对“正版”的认识程度。

很多人拒绝正版的理由是没钱。但另一方面却发现,大家对于硬件选择相当的“阔绰”“奢侈”,全部组件都要选择最高等级,CPU要市场上最快的,硬盘要转速高容量大的,要外加两三千的专业级独立显卡,再加杜比5.1的家庭影院,呵呵,无论自己的实际需求是否真的必需那么高等级的硬件,都要配到气派、爽心悦目。
(其实,这个“奢侈”或者说“不管实际需求”的毛病,甚至还表现在对“盗版软件”的选择上,Windows XP 全部要 Professional(用盗版 Home 的还真没见过),Vista 全部要 Ultimate,Office 最起码要是 Professional(很多人安装之后根本就不会用到其中的 Publisher, Access 等),Visual Studio 也是最高的 Team 版最受欢迎。)

简单想想,“反正盗版软件不花钱,我就可以把省下的钱升级更好的硬件。何乐而不为呢?”这道理也很好理解。不过,这却让“因为没钱所以拒绝正版”的理由站不住脚了,实际上你不是没钱,而只是不愿把钱“分配”给软件,这才是真正的理由吧?实际上,买电脑之前做预算时,就应该把软件的费用考虑在内,量力而行,根据实际需求、合理分配金钱投入。只有当你把软件当作像 CPU 那样必不可少的组成部分时,你才不会“挪用”本应分配给软件的“专项资金”,对吧。举个不恰当的例子,某些地方政府挪用救灾专款、教育专项资金,去搞地产开发或者其他用途,大家都咬牙切齿,想想道理其实是一样的,只要不重视,就会发生“挪用”,缺钱只是个不高明的借口。

下面简单给出我的观点:
(1)盗版率高的问题,肯定有定价的原因,把发达国家的定价直接搬到中国是不合理的。定价应该考虑到中国的实际收入水平。长远来说,这并不必然会损害产商利益,毕竟中国市场是以人多为特点的市场。如果软件正版化的理念能得到社会承认,以数量取胜同样可以取得丰厚的利润。
(2)如果定价和收入基本协调的情况下,还有人死硬拒绝,那就不是“缺钱”这样的借口可以说得过去的了。

建议所有从事软件开发的人员都应该支持正版化,并力所能及的身体力行。这不仅关系微软这些外国产商,同样也关系我们自己的生存质量。比如,那些死硬拒绝正版的网民的留言:“除非微软降到 39 元(甚至19元、4元等数字),否则坚决不买。” 像 Windows 或 Office 这样规模庞大、功能强大的软件、只值这个价钱的话,那么我们诸位或公司开发的软件、应该卖啥价格呢?是不是要比白菜还便宜呢?所以,没得说,大家都该知道怎么做。

P.S.

新闻中所谓的“Office家庭版”应该是微软专为中国市场推出的吧,包含 Word, Excel, PowerPoint, OneNote 四个组件,在其他地区并没有发现这样的组合方式,正式的版本组合有:个人版 = Word + Excel + Outlook; 标准版 = 个人版 + PowerPoint; 专业版 = 标准版 + Access + Publisher;...
个人以为此次“家庭版”将使用最广泛的 Word, Excel, PowerPoint 组合在一起还是很好的。我在日本就只能选择标准版才能用上这三个组件,而 Outlook 可以用 Windows Live Mail 等替代,并非必需品。希望这样的组合方式能够向其他地区市场推广,呵呵。

posted on 2008-09-24 06:59:42 by percyboy  评论(0) 阅读(763)

 
2008年06月16日

最近似乎不太顺利,总是一钻进 Reflector 就 N 久时间找不到问题所在,一点一点琢磨那些可疑的、没有头绪的、没有注释的 BCL (.net 的基础类库)源代码,以确认到底是我错了,还是微软错了。

这不,又发现一个疑似bug,如标题所写。

XmlDataSource 控件一般是和 TreeView 组合使用的,如果是静态的 XML 数据是不会碰到什么问题的,但一变成动态数据,就总碰到一些怪异的现象。(虽然大部分最终还是被克服了。)

想让 TreeView 显示动态数据,第一条,可以不用绑定,直接一个 TreeNode 一个 TreeNode 的添加,保证 100% 符合要求。

第二条,在画面上放上多个 XmlDataSource 控件,根据情况将 TreeView 的 DataSourceID 属性指到相应的 XmlDataSource 控件。但局限也很明显,总不能一下子放上 100个,1000个 XmlDataSource 控件吧?

第三条,XmlDataSource 只用一个,但改变它的 DataFile 或者 Data 属性,以改变数据。

改变 DataFile 属性的方案,经试验是可行的。不过相对于 Data 属性,局限就是必须有硬盘上存在的 XML 文件做源。虽然你可以选择动态生成 XML 文件,然后绑上去,但还是有些麻烦,还得考虑考虑这些临时文件的废弃处理措施。

Data 属性相对就比较合我的口味,不过,我就在这个方案上栽了跟斗:我发现无论怎么改变 Data 值,TreeView 的显示总是不变。(只有页面初始化那一次有效。)

先是怀疑是不是 TreeView 没有自动去 XmlDataSource 去取最新数据?对 BCL 的 debug 是有点麻烦的,好像是有办法下载微软公开的源代码,然后进行 debug 的,不过我这会儿没功夫去调。就用 Reflection 将我看到的那些 private 变量的值弄出来看,结果没发现 TreeView 有“偷工”的迹象。

然后把矛头掉转到 XmlDataSource,看看是不是它有问题。一钻去,首先就发现这个家伙跟其他的 DataSource 控件相比,有一点很与众不同,它默认的 Cache.Enabled = true。这个默认设置,也算可以理解,毕竟 XML 文件相对于数据库那些数据源来说,还是很稳定的,而且加载很多节点的 XML 文档也是很费劲的。

进一步的追踪发现,问题的原因应该就是出在缓存上。Data 属性变化后,缓存没有自动失效,导致了问题。

下面是我使用 Reflection 的 Hack:

    public static void XmlDataSourceCacheHack(XmlDataSource dataSource)
    {
        try
        {
            Type t = typeof(XmlDataSource);
            MethodInfo m = t.GetMethod("CreateCacheKey", 
                BindingFlags.Instance | BindingFlags.NonPublic);
            string key = (string)m.Invoke(dataSource, null);
            PropertyInfo p = t.GetProperty("Cache",
                BindingFlags.Instance | BindingFlags.NonPublic);
            object cache = p.GetValue(dataSource, null);

            Type t2 = t.Assembly.GetType("System.Web.UI.DataSourceCache");
            MethodInfo m2 = t2.GetMethod("Invalidate", 
                BindingFlags.Instance | BindingFlags.Public);
            m2.Invoke(cache, new object[] { key });
        }
        catch
        {
        }
    }

如果你有不使用 Reflection 就能解决的方法,敬请不吝赐教,那就多谢了!

如果你希望看点示例代码,请到 codeproject 下面的页面去下载:

http://www.codeproject.com/KB/webforms/XmlDataSource_Cache_Hack.aspx

补充:

当然,还有个简单的做法,就是直接禁用 XmlDataSource 的 cache 功能。不过,像我的实际情况中,切换数据源的可能性要远远低于其他 PostBack 的几率,缓存要比 reflection 更重要一些。你可以根据你的实际情况决定。

posted on 2008-06-16 20:15:48 by percyboy  评论(0) 阅读(1297)

 
2008年06月10日

痛苦了debug了一个多钟头,后来终于在网络上找到了这篇“救星”文章:

http://columns.chicken-house.net/blogs/chicken/archive/2007/04/06/system-net-mail-bug.aspx

立此存照,如果您也碰到同样问题,希望能够能比我更幸运些,更早找到问题所在。

症状是:调用 SmtpClient.Send 方法后,出现 System.FormatException,

英文消息为“An invalid character was found in header value.

中文消息是:“邮件标头中找到无效字符”。

原因是在 SmtpClient.Send 之前曾经调用过该 MailMessage 对象的 From, To, Cc 等字段的 ToString 方法。很有可能的情形是,你尝试在发信前留下日志时,“无意间”调用到了。而微软的工程师在此处出现了一些失误,最终产生了该错误消息,具体情况请参看上面链接中的文章。

P.S. 当然还会有其他原因可能导致此问题,比如微软知识库里给出的一种原因是因为收件人显示名称中包含有引号

posted on 2008-06-10 18:25:15 by percyboy  评论(0) 阅读(1629)

 
2008年04月18日

I suppose you were searching the keywords in the title before entering this page. The problem may be:

In a GridView (ASP.NET 2.0), you want to use a HyperLinkField, but you find it doesn't support UrlEncode, while you are planning to pass some variables via URLs. The bug report shows that Microsoft doesn't have any plan on adding such a property, because their policy on backward compatibility between different version of .net frameworks.

I also made a lot of searching and browsing. The popular way to solve this problem always is, to tranform your HyperLinkField into TemplateField, and do UrlEncode by yourself via HttpUtility.UrlEncode method.

Following codes give you another choice, avoiding such a tranformation, and working for your UrlEncode needs. Just have a try!

 

    public static void HyperLinkFieldUrlEncodeHack(GridView gridView)
    {
        if (gridView == null)
        {
            return;
        }
        gridView.RowDataBound += delegate(object sender, GridViewRowEventArgs e)
        {
            if (e.Row.RowType != DataControlRowType.DataRow)
            {
                return;
            }
            for (int i = 0; i < gridView.Columns.Count; i++)
            {
                DataControlField field = gridView.Columns[i];
                if (field is HyperLinkField)
                {
                    log.Debug(e.Row.RowType.ToString());
                    TableCell td = e.Row.Cells[i];
                    if (td.Controls.Count > 0 && td.Controls[0] is HyperLink)
                    {
                        HyperLink hyperLink = (HyperLink)td.Controls[0];
                        HyperLinkField hyperLinkField = (HyperLinkField)field;
                        if (!String.IsNullOrEmpty(
                            hyperLinkField.DataNavigateUrlFormatString))
                        {
                            string[] dataUrlFields = new string
                                [hyperLinkField.DataNavigateUrlFields.Length];
                            for (int j = 0; j < dataUrlFields.Length; j++)
                            {
                                object obj = DataBinder.Eval(e.Row.DataItem,
                                    hyperLinkField.DataNavigateUrlFields[j]);
                                dataUrlFields[j] = HttpUtility.UrlEncode(
                                    (obj == null ? "" : obj.ToString()));
                            }
                            hyperLink.NavigateUrl = String.Format(
                                hyperLinkField.DataNavigateUrlFormatString,
                                dataUrlFields);
                        }
                    }
                }
            }
        };
    }

 

Pleased if you find it useful.

posted on 2008-04-18 20:17:05 by percyboy  评论(0) 阅读(1555)

 

我假定你是碰到了和我相同的问题,搜索标题中这几个关键字来到这篇文章的。

简单的描述一下这个有点挠头的问题,就是对于 GridView 中的 HyperLinkField 列,MS 并没有像 BoundField 那样提供 UrlEncode/HtmlEncode 之类相关的属性设置。可实际运用中,你很可能碰到需要在 URL 中传中文参数的问题。Google 出来的网页表明,西方人也会因为一些特殊字符(比如 &)碰到同样的问题。当然相比西方人,CJK 圈子里更普遍一些。

目前看到的文章给出的方案大都是将 HyperLinkField 转化为 TemplateField 之后,手动用 HttpUtility.UrlEncode 方法处理数据绑定。代码会增多,看起来也不太雅观。而且如果你反悔或者想调整字段时,从 TemplateField 也不能自动转回 HyperLinkField。

Google 出来的网页中,有一些网友跟我同样的想法,希望 MS 能够在 .net 2.0 下个版本(也就是 .net 3.x)中提供一个这样的常用属性,并且已经有人向 MS 提交了“bug 报告”。MS 的答复应该令不少人失望:为了在版本升级中保持 backward-compatibility,MS 不会添加这样的属性。

(顺便多句嘴,backward-compatibility 这个词,国内有人译作“向前兼容”,也有人译作“向后兼容”,呵呵,很有意思的一件事,都有点道理,“以前”是指 before now,“向前看”却是 look forward。)

MS 说明 backward-compatiblity 的 blog 上,很多网友表达了跟我类似的疑惑:既然 .net 各个版本之间可以互不影响的独立工作,那就没有必要去“过分”地追求 100% backward-compatibility。当然,为了大家不至于重新学习,做到尽可能兼容就可以了,但以前写的程序,就还让它们在原来的 .net framework 上运行就好了。

而至于版本迁移,决策者准备迁移之前,就应该考虑好迁移的优点和缺点,就像 PetShop 从 .net 1.x 迁移到 2.0 改动不可谓之不大,但为了利用 2.0 最新的技术,它那样去做了,到底值不值得,那就是决策者的算盘了。

好了,上面对于多数人来说都是废话,来主要的吧:

下面这段代码给你一个新选择,避免将 HyperLinkField 转换为 TemplateField ,但同时实现了 UrlEncode。

 

    public static void HyperLinkFieldUrlEncodeHack(GridView gridView)
    {
        if (gridView == null)
        {
            return;
        }
        gridView.RowDataBound += delegate(object sender, GridViewRowEventArgs e)
        {
            if (e.Row.RowType != DataControlRowType.DataRow)
            {
                return;
            }
            for (int i = 0; i < gridView.Columns.Count; i++)
            {
                DataControlField field = gridView.Columns[i];
                if (field is HyperLinkField)
                {
                    log.Debug(e.Row.RowType.ToString());
                    TableCell td = e.Row.Cells[i];
                    if (td.Controls.Count > 0 && td.Controls[0] is HyperLink)
                    {
                        HyperLink hyperLink = (HyperLink)td.Controls[0];
                        HyperLinkField hyperLinkField = (HyperLinkField)field;
                        if (!String.IsNullOrEmpty(
                            hyperLinkField.DataNavigateUrlFormatString))
                        {
                            string[] dataUrlFields = new string
                                [hyperLinkField.DataNavigateUrlFields.Length];
                            for (int j = 0; j < dataUrlFields.Length; j++)
                            {
                                object obj = DataBinder.Eval(e.Row.DataItem,
                                    hyperLinkField.DataNavigateUrlFields[j]);
                                dataUrlFields[j] = HttpUtility.UrlEncode(
                                    (obj == null ? "" : obj.ToString()));
                            }
                            hyperLink.NavigateUrl = String.Format(
                                hyperLinkField.DataNavigateUrlFormatString, 
                                dataUrlFields);
                        }
                    }
                }
            }
        };
    }

 

我想你该知道怎么来使用这段代码吧?

posted on 2008-04-18 20:01:20 by percyboy  评论(0) 阅读(1930)

 
2008年03月31日

(第一部分)

样式和控件模板

创建界面时,我们经常重复性地为很多控件设置外观属性,比如,我们希望所有的 Label 都显示为“Bold Trebuchet 12px”的字体,如果是 HTML,我们很容易用 CSS 实现,但如果是 WinForm,则会比较麻烦。WPF 引入了 Style 标签来满足这一常见的需求。先来看一个例子:

<StackPanel>
   <StackPanel.Resources>
      <Style TargetType="{x:Type Label}">
         <Setter Property="FontFamily" Value="Trebuchet" />
         <Setter Property="FontSize" Value="12" />
         <Setter Property="FontWeight" Value="Bold" />
      </Style>
   </StackPanel.Resources>

   <Label>Here is some text.</Label>
   <Label>More text.</Label>
   <Label>The last bit of text.</Label>
</StackPanel>

Style 标签中最常用的是 Setter 语法,从示例中你看到的,Setter 标签指定了样式相关的属性名和我们赋给该属性的值。是不是很简单?上一节中,我们讨论过的数据模板,它可以根据设定的数据类型来匹配,也可以根据指定的 key 来精确匹配,另外,数据模板的书写位置决定了它的作用范围。Style 标签同样适用类似的规则。这个例子中,Style 标签处在 StackPanel 的资源中,匹配类型为 Label 的标签,则此 StackPanel 内的所有标签都将适用此 Style 标签指定的样式。当然如果需要,你也可以为其中一些 Label 单独设定样式。另外一个有用的属性是 BaseOn,它允许 Style 标签间的“继承”关系:

<StackPanel>
  <StackPanel.Resources>
    <Style x:Key="baseStyle" TargetType="{x:Type Control}">
      <Setter Property="FontFamily" Value="Trebuchet" />
      <Setter Property="FontSize" Value="12" />
      <Setter Property="FontWeight" Value="Bold" />
    </Style>
    <Style BasedOn="{StaticResource baseStyle}" TargetType="{x:Type Label}">
      <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
          <Setter Property="Foreground" Value="Red" />
        </Trigger>
      </Style.Triggers>
    </Style>
  </StackPanel.Resources>

  <Label>Here is some text.</Label>
  <Label>More text.</Label>
  <Label>The last bit of text.</Label>
</StackPanel>

此例中定义了两个 Style:一个将对所有 Control 类型有效,第二个只对所有的 Label 有效。我在此例中展示了另一个很有用的语法,Trigger。示例的效果将是,此 StackPanel 内的 Label,在鼠标悬停其上时,背景变为红色。

有了 Style 标签,你可以很容易地让你的界面代码变得风格统一、易于维护。它有很多好处。不过有时候,你或许需要更强大的。比如,你希望界面中的 Button 多一点立体感,或者卡通一些。仅使用基本的 Setter 语法,你无法做到这些,而需要使用控件模板(ControlTemplate)。先试着把控件想得抽象一些,WPF 中,比如一个 Button 控件,它只是一个抽象意义上的按钮,你在不同的软件中见过各种各样、不同外观的按钮,它们都算得上 Button,它们外观各异,有些还在鼠标滑过时变化一些效果,按下它们后会触发一些事件。WPF 中,你可以将这些按钮都用 Button 来实现,分别给它们指定不同的控件模板,它们就将显示出不同的外观效果。一些控件的模板写法简单,另一些控件的模板写法或许复杂一些。下面的例子来自 SDK:

<StackPanel>
  <StackPanel.Resources>
    <Style TargetType="{x:Type Button}">
      <Setter Property="Foreground" Value="white" />
      <Setter Property="Margin" Value="1" />
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type Button}">
            <Grid>
              <Rectangle x:Name="GelBackground"
                Opacity="1" RadiusX="9"
                RadiusY="9"
                Fill="{TemplateBinding Background}"
                StrokeThickness="0.35">
                <Rectangle.Stroke>
                  <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                    <GradientStop Color="White" Offset="0" />
                    <GradientStop Color="#666666" Offset="1" />
                  </LinearGradientBrush>
                </Rectangle.Stroke>
              </Rectangle>
              <Rectangle x:Name="GelShine"
                Margin="2,2,2,0"
                VerticalAlignment="Top"
                RadiusX="6"
                RadiusY="6"
                Opacity="1"
                Stroke="Transparent"
                Height="15px">
                <Rectangle.Fill>
                  <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                    <GradientStop Color="#ccffffff" Offset="0"/>
                    <GradientStop Color="Transparent" Offset="1"/>
                  </LinearGradientBrush>
                </Rectangle.Fill>
              </Rectangle>
              <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>
            </Grid>
            <ControlTemplate.Triggers>
              <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Fill" TargetName="GelBackground">
                  <Setter.Value>
                    <RadialGradientBrush>
                      <GradientStop Color="Lime" Offset="0" />
                      <GradientStop Color="DarkGreen" Offset="1" />
                    </RadialGradientBrush>
                  </Setter.Value>
                </Setter>
              </Trigger>
              <Trigger Property="IsPressed" Value="true">
                <Setter Property="Fill" TargetName="GelBackground">
                  <Setter.Value>
                    <RadialGradientBrush>
                      <GradientStop Color="#ffcc00" Offset="0"/>
                      <GradientStop Color="#cc9900" Offset="1"/>
                    </RadialGradientBrush>
                  </Setter.Value>
                </Setter>
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
      <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
          <Setter Property="Foreground" Value="Black"/>
        </Trigger>
        <Trigger Property="IsPressed" Value="True">
          <Setter Property="Foreground" Value="Black"/>
        </Trigger>
      </Style.Triggers>
    </Style>
  </StackPanel.Resources>

  <Button Height="35" Width="125" Background="Black">Normal</Button>
  <Button Height="35" Width="125" Background="Black">Mouse Over</Button>
</StackPanel>

图10:使用模板的 Button

此例中,ControlTemplate 标签写在 Style 标签的内部(也可以单独写,在不同的 Style 间共享),定义了 Button 的外观效果,包含了若干个叠放着的矩形,它们有着不同的渐变效果。从这个例子中,你多少看到了控件模板的威力了吧,如果你乐意,你还可以在模板中加入 3D 形状或者动画效果。类似地,控件模板也使用类型匹配和 key 匹配,当然要注意不要将类型匹配错,驴唇是装不到马嘴上的。编写控件模板或许不太容易,因此最好先从好用的示例代码开始学习。SimpleStyles 的示例就是不错的资源,Kaxaml 编辑器(一个不错的 XAML 编辑器,或许你该试试)中就提供了此链接,Blend 中也有类似的资源。更多的探讨就超出了这篇入门教程的范畴了,我们进入下一个话题。

动画和 3D

动画和 3D 同样是很大的范畴,本文中我只能简单的介绍一些,并提出一些资源供你进一步的学习。

开始学习 WPF 动画之前,你应该对 DependencyProperties 有一个深入的理解,这样的属性都支持动画。前文已经提到过,Charles Petzold 的那本书在这些话题上有深入的解释,很有助于你的理解。

初步的印象是,WPF 使用 Storyboard 标签来组织动画效果。这种规则便于用 XAML 形式来记述,也能够承载越来越多的各种复杂动画效果。典型的动画以 BeginStoryboard 标签作为它的容器,用于容纳一个或多个 Storyboard:

<BeginStoryboard> 
  <Storyboard TargetProperty="Opacity"> 
    <DoubleAnimation From="1" To="0" Duration="0:0:1" /> 
  </Storyboard> 
</BeginStoryboard>

这个例子中的动画效果将是,Opacity 不透明度属性的值从 1 变化到 0,动画效果的时长是 1 秒钟,也就是在一秒钟内逐渐“消失”掉。哪些控件可以应用这个动画效果呢?又如何指定该动画效果的触发时机呢?请看完整的写法:

<Button Height="40" Width="125"> 
  <Button.Triggers> 
    <EventTrigger RoutedEvent="Button.Click"> 
      <BeginStoryboard> 
        <Storyboard TargetProperty="Opacity"> 
          <DoubleAnimation From="1" To="0" Duration="0:0:1" /> 
        </Storyboard> 
      </BeginStoryboard> 
    </EventTrigger> 
  </Button.Triggers> 
</Button>

我们让这个 Button 在被点击的时候逐渐“消失”掉。触发器定义使用的是 Button 控件的 Click 事件这个 RoutedEvent。下面的例子中,这个动画效果作为样式定义的一部分出现,而触发方式则换了另一种方式:

<Window.Resources> 
  <Style TargetType="{x:Type RadioButton}"> 
    <Style.Triggers> 
      <Trigger Property="IsChecked" Value="True"> 
        <Trigger.EnterActions> 
          <BeginStoryboard> 
            <Storyboard TargetProperty="Opacity"> 
              <DoubleAnimation From="1" To="0" Duration="0:0:1" /> 
            </Storyboard> 
          </BeginStoryboard> 
        </Trigger.EnterActions> 
      </Trigger> 
    </Style.Triggers> 
  </Style> 
</Window.Resources> 
<RadioButton Height="40" Width="125"/>

嗯,它将在 IsChecked 属性变为 True 值时启动该动画效果,这是 Trigger.EnterActions。相对地,还有 Trigger.ExitActions,如果上例改为 ExitActions,则将在 IsChecked 属性从 True 值变为其他值时启动该动画。请注意,上面两例中的动画效果的类型为 DoubleAnimation,这里的“Double”是指被逐渐变化的属性(例子中的 Opacity)的类型为 Double 数字类型。WPF 中定义了 22 中类型的动画效果,如 ColorAnimation, VectorAnimation, PointAnimation 等。这些动画效果都提供了很多可设定的属性选项,用于动画效果的细节控制,比如 AccelerationRatio, DecelerationRatio, SpeedRatio, RepeatBehavior 等。此外,还可以使用 AnimationUsingPath(路径动画),先定义一个“路径”(比如一个正弦曲线),然后让动画的变化规则根据“路径”指示的规则来变化,比如让一个“灯泡”的亮度变化呈正弦曲线的规律,比如让一个“小球”的位置变化沿着一条抛物线的轨迹移动,等等。如果你希望更大的自定义能力,WPF 中还有 AnimationUsingKeyFrames (关键帧动画),可以定义复杂的、缺乏规则的、缺乏连贯性的变化效果。如果你对 WPF 中的动画有兴趣,我建议你读一读这两个博客,Charles Petzold (在他的文章里检索一下)和 theWPFblog,那里还有不少 3D 方面的示例。开始介绍 3D 之前,我得提示你,WPF 动画的结构并不限于平面的动画,它同样适用于 3D 物体的动画。

在这个教程即将收尾之前,我们来看一看 3D。前文提到的 Daniel Lehenbauer 写的那本“Windows Presentation Foundation Unleashed”书中,关于 3D 的那一章写的很精彩,应该是截至目前最好的 WPF 3D 介绍。Petzold 正在写的一本书,将全部用于介绍 WPF 3D。当然还有上面介绍的两个博客,还有以下的几个站点:The WPF3D Team Blog, Five Great WPF 3D Nuggets, 3D Tutorial,都是很好的资源。关于 3D,第一件事必须知道的是,所有的 3D 内容都必须写在 Viewport3D 控件内部,它好像一个窥视 3D 世界的窗口,你可以在这个 3D 世界里放上 Camera,调好角度,再放上光源,这样你就在 Viewport3D 控件里看到那个 Camera “拍”到的图像。WPF 提供了两种主要的 Camera,PerspectiveCamera 和 OrthographicCamera,关于这两者的差别,请看这里的讨论。光源你可以选择 AmbientLight 和/或 DirectionalLight。

<Viewport3D> 
   <Viewport3D.Camera> 
      <PerspectiveCamera   
         FarPlaneDistance="20" 
         LookDirection="5,-2,-3" 
         UpDirection="0,1,0" 
         NearPlaneDistance="1" 
         Position="-5,2,3" 
         FieldOfView="45" /> 
   </Viewport3D.Camera> 
   <ModelVisual3D> 
      <ModelVisual3D.Content> 
         <Model3DGroup> 
            <DirectionalLight Color="White" Direction="-3,-4,-5" /> 
            <GeometryModel3D> 
               <GeometryModel3D.Geometry> 
                  <MeshGeometry3D   
                     Positions="-1 -1 0  1 -1 0  -1 1 0  1 1 0" 
                     Normals="0 0 1  0 0 1  0 0 1  0 0 1" 
                     TextureCoordinates="0 1  1 1  0 0  1 0" 
                     TriangleIndices="0 1 2  1 3 2" /> 
               </GeometryModel3D.Geometry> 
               <GeometryModel3D.Material> 
                  <DiffuseMaterial> 
                     <DiffuseMaterial.Brush> 
                        <SolidColorBrush Color="Blue"/> 
                     </DiffuseMaterial.Brush> 
                  </DiffuseMaterial> 
               </GeometryModel3D.Material> 
            </GeometryModel3D> 
         </Model3DGroup> 
      </ModelVisual3D.Content> 
   </ModelVisual3D> 
</Viewport3D>

在这里例子里,我只放了一个平面,你可以添加自己的 3D 物体。和前面介绍的那样,这里用了 PerspectiveCamera,有光源 DirectionalLight,使用 MeshGeometry3D 定义了平面的位置,然后还给这个平面指定了 Material 材质。例子中使用了 DiffuseMaterial 材质,WPF 中还提供有 EmmisiveMaterial 和 SpecularMaterial 供你选用。如果你之前使用过其他 3D 软件,你应该了解材质的重要性。例子中的 DiffuseMaterial 使用了 Brush 来“粉刷”,如果你善于联想,你说我们是否可以把软件界面也放在某个 3D 物体的表面上?来看看这个网页吧,来看看 WPF 3D 潜在的神奇。

 

WPF 现状和未来

WPF 是一个威力十足的用户界面框架,我向 WPF 的设计团队致意,我相信它是到目前为止最棒的界面框架,无论在任何平台。它还只是一个 Version 1,它还有成长的空间。下面,我将以我的一些想法来总结本文,我希望在未来版本中看到以下的功能:

  • 对话框是常见的需求,我希望看到和 WPF 普通窗口同样风格的对话框;
  • 数据绑定是我的最爱,不过当我用 Reflector 查看其内部结构时发现,它使用反射发出功能来实现(并不是很意外),我希望 WPF 团队应该尝试更快的简单属性存取(setter/getter)和 LCG(译注:不知道此处的 LCG 具体是什么的缩写),我不确定这样做是否有可行性,也不确定是否能提高运行效率,不过我想这是值得去深入研究的;
  • 希望有更多通用的控件,如 DatePicker, Calendar, MaskedTextBox, DataGrid, PropertyGrid 等;
  • 还有,3D 的运行效率希望能够得到改善,也希望更多材质类型,对 pixel shader 的支持;
  • 如果是 XBAP 方式运行,希望有“最小化 WPF”的功能,希望能有更多的 WPF 特性被移植到 XBAP 中来。

随着 Orcas 发布的临近,我希望其中的一部分能够尽早的看到。

展望未来,Microsoft 通过 Silverlight 已经开始将 WPF 和 .NET Framework 的触角伸向其他的操作系统平台。在 MIX07 展示时,Microsoft 声称 Silverlight 1.1 将包含一个跨平台的 CLR 和 BCL(基础类库)的一个子集版本,对于 .NET 开发者来说,这是一个很好的消息,我们将有可能使用喜欢的编程语言,为 Mac 和 PC 机编写基于浏览器的 RIA 程序。Silverlight 1.0 不包含 CLR,如果你希望使用 .NET 语言来编写,那你得用 Silverlight 1.1(译注:目前已改称 2.0 版本),其中包含的 BCL 子集包含了多线程、数据绑定、Web服务等重要功能。我看好这项技术,也坚信 Microsoft 正引领我们走向正确的未来方向。

结语

我希望我的这篇简介能够帮助你了解 WPF 的全貌。这是一个全新的、丰富功能的界面框架,针对 Windows 平台,并且有一个“子集”面向浏览器。它从此前的各种界面框架中获取了很多灵感,它不露痕迹的整合了那些框架的优点,为开发者提供了强大的能量来构建新一代的用户界面。

(作者 Rob Eisenberg 的博客)

posted on 2008-03-31 14:07:20 by percyboy  评论(0) 阅读(4792)

 
2008年03月26日

本人才疏学浅,半道起家,写出来的东西多半拿不出手。加上长期将此当作自留地,任意随性,写出来的东西多半天真幼稚傻里傻气。难得有若干读者捧场,才不致沦为废墟。

此页作为我的自选精华版,推荐给各位读者朋友。因瘸子里挑将军,难免有各位看不上眼的文章被我自封为精华,也请念在自留地的招牌上,多多海涵。若有您很喜欢而被我漏掉的文章,也盼望您的推荐,不过若是玩笑式的推荐就权当笑料吧。

.NET 事件模型教程(封面)

WPF 教程(译文)(第一部分)

ASP.NET Web Form 的“连续”和“有状态”假象

多语言界面 Web 站点的几个 Tip

多语言界面 Web 站点的几个 Tip (续)

.NET 访问 Oracle 数据库相关

分页(Paging) / SQL Server / Oracle

Microsoft Smart Clients vs Macromedia Rich Internet Applications

ASP.NET 2.0 BuildProvider 导论(一)

ASP.NET 2.0 BuildProvider 导论(二)

ASP.NET 2.0 BuildProvider 导论(三)

Office 2003 里的 Document Image Writer 虚拟打印机

The virtual printer -- Microsoft Office Document Image Writer

动态生成 rtf 并指定给 RichTextBox 控件

 

在 ASP.NET 中使用计时器(Timer)

.NET 中的三个 Timer 以及网页中的“Timer”

 

立即停止关于“防止ACCESS数据库被下载的方法”的讹传

ASP.NET 中防止 Access 数据库被下载的几种选择

通过试验探索 Access 2000/XP 数据库的最佳 NTFS 权限设置

 

posted on 2008-03-26 20:48:03 by percyboy  评论(0) 阅读(1135)

 
【第1页/共10页,94条】
首页
前页
1

Powered by: Joycode.MVC引擎 0.5.1.0