相信使用过Java或者是.NET的人对动态数组类Array(或者是类似的类)都不会陌生——因为这个类好处多多,可以无限扩展其长度大小范围,以便容纳更多的内容。其实在java中也有一个Array对象可以使用,它几乎也是万能的:

【一】当动态数组使用:

这是最最基本的Array使用方法,Array对象有三个“构造函数”:

new Array();
new Array(size);
new Array(element0, element1, ..., elementn);

第一个指定一个空的Array,第二个指定一个长度的Array,最后一个直接填充数据初始化长度和若干内容的Array。无论哪一种,一旦不够用了都可以往里边直接添加数据。添加数据的方法是:

ArrayInstance[索引号,从0开始]=内容;

当然您也可以通过for的方式循环遍历(注意:javascript根本没有int这种显式声明的类型,一律是弱类型的var

for(var i=起始索引;i<arrayInstance.length;++i)
{
     alert(arrayInstance[i]);
}

另外你还可以使用foreach方式(注意,javascript中没有foreach,还是用for的方式,有些类似java的for(变量in集合))

  var arr = new Array(1, 2, 3);
        for (var i in arr) {
            alert(i);
        }

以上做法输出会有惊人的发现——竟然是0,1,2而不是1,2,3!Why?注意这里和C#或者java最大的不同在于——C#或者java是把foreach理解成IEnumerator接口对象,访问封装的内部数据;但是js中这样做导致的结果是你只能访问索引号而不是索引的内容,因此你正确的做法是:

  var arr = new Array(1, 2, 3);
        for (var i in arr) {
            alert(arr[i]);
        }

除此之外,您还可以这样初始化数组:

var arr=[1,2,3,……,内容];

 

【当字典使用】

下标不一定非要是数字(默认0开始),Array允许你直接自定义下标,这样一来使用过Dictionary的人往往乐开了花:

var arr = new Array();
arr["a"] = "a";
arr["b"] = "b";

但是注意,此时使用arr.length检测的结果是0!Why?因为这些并不是js的真正下标而是属性而已,属性不算在arr的长度之列,只有0,1,2……这样的下标才算

遍历这些属性方法同遍历索引一样(使用for版本的foreach)

之前说过当字典使用的时候属性并非下标,那么属性和下标区别是什么呢?一般地,js语法非常松散,属性并不需要“定义”后才“使用”,允许对已有的“方法类”(对象,并不是真正意义上的类)边动态添加边操作——

 function MyFun() {
            this.A = "a";
        }
        var mf = new MyFun();
        mf.B = "B";
        mf["C"] = "C";
        alert(mf.B);
        alert(mf.C);
        alert(mf.A);

注意看粗体部分,就明白了,其实这是一个js的语法范畴的问题,Array作为一个对象也可以这样操作,任意一个js对象都可以这样操作,但是请记住,该对象必须通过new出来这样操作方可,直接调用(比如MyFun.B)是不可以的

另外,这种“字典型”操作方式还可以和json媲美,因此字典型的初始化还允许:

  var a = { "A": "A","B":"B" };

var 变量名 = {"键1":"值1","键2":"值2",……,"键n":"值n"};

访问如同for版的foreach,而且允许动态添加键值对。

posted @ 2012-05-28 15:06 Serviceboy 阅读(14) 评论(0) 编辑

window是javascript的一个非常重要的核心对象。对于可以打开多个页面的浏览器(比如IE7等以上版本,或是FireFox,Opera……)window只针对当前的客户端窗体有效。下面介绍以下window的一些重要属性以及应用:

【屏蔽右键】

某些网站不允许复制,怎么办呢?办法就是屏蔽鼠标右键。代码可以这样写(注意:event.button属性必须且只能和mouse相关的事件——比如onmouseup/onmousedown/onmouseover使用)。

<body onmousedown="disableRightClick()">
    <p>
        点击此区域将无法弹出右键
    </p>
</body>

脚本:

function disableRightClick() {
    //右键在IE为2
    if (event.button == 2) {
        alert("禁止使用右键!");

          return false;

    }
}

以上方法在IE中有效,Chrome无效(会弹出警告框和右键菜单)。要禁用几乎全部浏览器的鼠标右键,可以使用document.oncontentmenu属性

<head>
    <title></title>
    <script src="MyJavaScript.js" type="text/javascript"></script>
    <script>
        document.oncontextmenu = function () { alert("禁止右键!"); return false;};
    </script>
</head>
<body>
</body>

注意:由于oncontentmenu是属性,所以在绑定一个匿名事件的时候必须有返回值(return false)不能省略!

这里顺便说一声:任何Html标签的onXXX事件可以绑定一个匿名事件,同样也可以绑定一个已定义的js函数。该函数根据实际情况可以选择有或者没有返回值

【Html文本框只能输入数字】

对于限制某些内容在文本框内的输入,可以使用window.event.keyCode配合(限于onkeypress/onkeydown/onkeyup)中

例如以下文本框只允许输入数字:

<script type="text/javascript">
        function AutoCheck() {
            var code = window.event.keyCode
            if (!(code >= 48 && code <= 57)) {
                return false;
         }
        }
    </script>

对应Html:

<input type="text" id="myText" onkeypress="return AutoCheck(event);" />

注意input标签中的return不能省略,因为该函数有返回值(如果省略会导致该事件继续执行,因为事件没有被取消)。

不过当把js语句中的return false改成window.event.returnValue=false;的时候是可以省略input标签中的return的,究其原因是因为event.returnValue赋布尔类型的值会决定该事件是否被执行(具体来说,我个人认为应该是阻止事件结果的产生,并不是取消事件!)。

进一步证明,现在我写这样的代码:

<script type="text/javascript">
        function AutoCheck(event) {
            event.returnValue = false;
            alert("OK");
         }
</script>

对应Html中:

<input type="text" id="myText" onkeypress="AutoCheck(event);" />

注意:当按下任意键的时候,该事件仍然被触发(弹出OK对话框,说明该事件并没有被取消,而是事件导致的结果——本来应该在文本框中输入的内容字符不能被输入了!)
而如果使用return false取代event.returnValue的话则不仅框不能弹出,而且什么也输入不了(因为return false也会取消事件的结果,并且因为return直接向事件属性返回一个布尔值,最终后继事件代码无法执行;相对而言,event.returnValue只是取消当前事件执行的结果的发生,并不影响后继代码的执行)。

posted @ 2012-05-28 14:26 Serviceboy 阅读(70) 评论(0) 编辑

某个客户询问我关于SelectSingleNode和SelectNodes的XPath操作,问题如下:

【xml】

<root>
  <dsobject classname="Rendition">
    <contentelements>
      <contentelement>Tansion</contentelement>
    </contentelements>
  </dsobject>

  <dsobject classname="Rendition">
    <contentelements>
      <contentelement>Herry</contentelement>
    </contentelements>
  </dsobject>

  <dsobject classname="Rendition">
    <contentelements>
      <contentelement>Jack</contentelement>
    </contentelements>
  </dsobject>
</root>

现在他要求“筛选出所有名称为dsobject,classname等于Rendition”,输出其contentelement的数据。

他这样做:

[C#]

foreach (XmlNode item in document.SelectNodes("//dsobject[@classname='Rendition']"))
{
  XmlNode oneNode = item.SelectSingleNode("//contentelement");
  string inner = oneNode.InnerText;
}

[VB.NET]

For Each item As XmlNode In document.SelectNodes("//dsobject[@classname='Rendition']")
	Dim oneNode As XmlNode = item.SelectSingleNode("//contentelement")
	Dim inner As String = oneNode.InnerText
Next

粗看先是从xml文档全部选出classname属性值为Rendition的全部dsobject(外循环),内部再从每个dsobject节点内部筛选出contentelement节点并输出包含的数值。但是实际上输出的结果只是三个“Tansion”。

究其原因,是因为XPath语法的特殊性:凡是以“//”开头的,都是无条件以整个xml的根节点开始遍历的。因此item.SelectSingleNode不要误以为是接着上面的dsobject节点往下检索,而是从头开始的!因此遍历的时候总是遍历到第一个dsobject中的contentelement的内部数值。

同样地,凡是以“/”开头的,都是无条件人为指定从根节点开始的绝对路径搜索,与XmlNode上下文毫无关系(比如要查询第一个contentelement,用绝对路径甚至可以这样写):

[C#]

Console.WriteLine(document.SelectSingleNode("/root/dsobject/contentelements/contentelement").InnerText);
Console.WriteLine(document.DocumentElement.SelectSingleNode("/root/dsobject/contentelements/contentelement").InnerText);

[VB.NET]

Console.WriteLine(document.SelectSingleNode("/root/dsobject/contentelements/contentelement").InnerText)
Console.WriteLine(document.DocumentElement.SelectSingleNode("/root/dsobject/contentelements/contentelement").InnerText)

显然,以/开头无论XmlNode是什么,总是被忽略且从指定的root=>dsobject=>contentelements=>contentment这样的顺序开始检索(绝对路径)。

至于和上下文相关的,就是直接写名字;比如:

[C#]

Console.WriteLine(document.SelectSingleNode("root/dsobject/contentelements/contentelement").InnerText);
Console.WriteLine(document.DocumentElement.SelectSingleNode("dsobject/contentelements/contentelement").InnerText);

[VB.NET]

Console.WriteLine(document.SelectSingleNode("root/dsobject/contentelements/contentelement").InnerText)
Console.WriteLine(document.DocumentElement.SelectSingleNode("dsobject/contentelements/contentelement").InnerText

显然,document的根节点是“#document”(特殊节点,xml中不存在),那么相对它而言下面的节点是root/dsobject/contentelments/contentelement。不过document.DocumentElement本身就是root,因此第二句代码中root就不能再写了,不然会发生找不到节点(因为不存在root=>root=>……这样的节点)。

SelectNodes也符合这样的特点,这里就不再叙述了。

posted @ 2012-05-13 20:11 Serviceboy 阅读(34) 评论(0) 编辑

我们知道,在C#中匿名方法支持多行(有、无)返回值处理程序,不过在VisualBasic中似乎并不这样支持多行的函数或者是过程。怎么办呢?我们来看一个例子(拿List(Of T)做例子)。

我们知道List(Of T)其中带有Find和FindAll函数——该两个函数需要一个函数Predicate(Of T) As Boolean作为例子,那么我们直接可以尝试这样做(示例性代码):

Public Function MyFunc(t As T) As Boolean
………………
End Function

List(Of T).Find/FindAll(MyFunc)
………………

其实我们可以封一下:

Public MustInherit Class BasePredicate(Of T As IComparable)
    Public Shared Widening Operator CType(ByVal subclass As BasePredicate(Of T)) As Predicate(Of T)
        Return New Predicate(Of T)(AddressOf subclass.GeneralDo)
    End Operator

    Public MustOverride Function GeneralDo(ByVal t As T) As Boolean
End Class

该类使用了CType的Widening方法(自动把类转化成一个Predicate,而Predicate的需要参数恰巧是子类必须实现的抽象类GeneralDo,比如子类这样实现:

Public Class IntFindAll
    Inherits BasePredicate(Of Integer)

    Public Overrides Function GeneralDo(t As Integer) As Boolean
        Return t Mod 2 = 0
    End Function
End Class

那么子类可以这样调用:

Public Class Program
    Shared Sub Main(args As String())
        Dim numbers As New List(Of Integer) From {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
        For Each i As Integer In numbers.FindAll(New IntFindAll())
            Console.WriteLine(i)
        Next
    End Sub
End Class

我想,这个可以弥补一些因为在VS2008中VB缺失了C#应有的功能而导致VB某些程序员“心里不平衡”吧,啊哈哈……。

posted @ 2012-05-13 11:37 Serviceboy 阅读(117) 评论(0) 编辑

.NET4中加入了并行机制——所谓并行就是同时开辟若干个线程来进行计算。这些线程由于都是互相独立的,所以在进行一些分布式(比如各自不同的工作)的时候是非常简单,不过要把这些处理结果汇总起来却不是那么容易——下面来看一个非常简单的例子(求1~1000的和)。

如果你尝试使用以下的代码计算,恐怕令你大跌眼镜!

[C#]

int sum = 0;
Parallel.For(0, 1000,i => {sum+=i;});

[VB.NET]

Dim sum As Integer = 0
Parallel.[For](0, 1000, Function(i) 
sum += i)

究其原因就是.NET会默认开辟一些线程同时进行“sum+=i”的计算。那么由于sum被这些线程同时使用,往往是一个线程还没有处理完毕,另外一个线程又介入了,自然无法得到正确结果了。

解决这个问题的办法有许多:

【一】分解法:

所谓分解法,就是针对“同一个变量”被不同线程“共享”这一诟病而提出的。——也就是说,把1~1000求和分成若干块进行处理(等于给每一个线程分配了不同的内存)。最后把分布计算的结果进行累计汇总即可。结果如下:

[C#]

int[] numbers = Enumerable.Range(1, 1000).ToArray();
int[] values=new int[4];
int sum = 0;
Parallel.For(0, 4, i => { values[i] = new Program().GetTotal(i * 250, 250, numbers); });
sum = values.Sum();
Console.WriteLine(sum);

[VB.NET]

Dim numbers As Integer() = Enumerable.Range(1, 1000).ToArray()
Dim values As Integer() = New Integer(3) {}
Dim sum As Integer = 0
Parallel.[For](0, 4, Function(i) 
values(i) = New Program().GetTotal(i * 250, 250, numbers))
sum = values.Sum()
Console.WriteLine(sum)

【二】使用静态变量

静态变量本身就具备“同步”的能力(这种例子在简单工厂中也可以窥见)。所以这里显然可以使用,代码如下:

[C#]

public class Program
        {
            static int sum = 0;
            static void Main(string[] args)
            {
                Parallel.For(1, 1001, i => {sum+=i;});
                Console.WriteLine(sum);
            }
        }

[VB.NET]

Public Class Program
    Shared sum As Integer = 0
    Private Shared Sub Main(args As String())
        Parallel.[For](1, 1001, Sub(i) 
        sum += i)
        Console.WriteLine(sum)
    End Sub
End Class

【三】使用volatile变量(一般编译器为了优化性能,往往把常用的变量复制一份到寄存器中,然后直接对寄存器进行操作,等到操作完毕之后再写回原来的内存中。多线程会导致寄存器中的变量不同步,所以结果也不对。而volatile就是告诉编译器直接从内存中读取那个数字进行操作,而不是拷贝到寄存器之后处理):

[C#]

public class Program
        {
            volatile int sum = 0;
            public void ShowResult()
            {
                Parallel.For(1, 1001, i => { sum+=i; });
                Console.WriteLine(sum);
            }

            static void Main(string[] args)
            {
                Program p = new Program();
                p.ShowResult();
            }
        }

[VB.NET,由于VB.NET没有此特性,所以使用Thread.VolitaleRead每次读取最新的数值后进行累加]

Public Class Program
    Dim sum As Integer = 0
    Public Sub ShowResult()
        Parallel.For(1, 1001, Sub(i)
                                  Thread.VolatileRead(sum)  '总是读取最新数值
                                  sum = sum + i
                                  Thread.Sleep(2)
                              End Sub)
        Console.WriteLine(sum)
    End Sub

    Public Shared Sub Main(args As String())
        Dim p As New Program()
        p.ShowResult()
    End Sub
End Class

【四】使用lock(锁住一个变量,然后直到该线程操作完毕自动释放变量,另外一个线程进来操作……如此反复而已):

[C#]

 public class Program
        {
            int sum = 0;
            public void ShowResult()
            {
                object obj = new object();
                Parallel.For(1, 1001, i => { lock (obj) { sum += i; Thread.Sleep(10); } });
                Console.WriteLine(sum);
            }

            static void Main(string[] args)
            {
                Program p = new Program();
                p.ShowResult();
            }
        }

[VB.NET]

Public Class Program
    Private sum As Integer = 0
    Private obj As New Object
    Public Sub ShowResult()
        Dim obj As New Object()
        Parallel.For(1, 1001, Sub(i)
                                  SyncLock obj
                                      sum = sum + i
                                  End SyncLock
                              End Sub)
                                  Console.WriteLine(sum)
    End Sub
End Class

Module M
    Sub Main()
        Dim p As New Program
        p.ShowResult()
    End Sub
End Module

【五】使用InternLock函数:

[C#]

 public class Program
        {
            int sum = 0;
            public void ShowResult()
            {
                Parallel.For(1, 1001, i => { Interlocked.Add(ref sum, i); });
                Console.WriteLine(sum);
            }

            static void Main(string[] args)
            {
                Program p = new Program();
                p.ShowResult();
            }
        }

[VB.NET]

Public Class Program
    Private sum As Integer = 0
    Public Sub ShowResult()
        Parallel.[For](1, 1001, Sub(i)
                                    Interlocked.Add(sum, i)
                                End Sub)
        Console.WriteLine(sum)
    End Sub

    Shared Sub Main(args As String())
        Dim p As New Program()
        p.ShowResult()
    End Sub
End Class
posted @ 2012-05-12 16:48 Serviceboy 阅读(135) 评论(0) 编辑

上次写了点关于验证码制作的代码,算是学习了Drawing中的一些东西,心里很快活,也乐意为大伙儿做些善事,于是便萌发了把验证码封装成简单的控件发布出去的念头——起初是用ashx(一般处理程序,俗称Httphander)的做法,OK。在WebForm和MVC两种情况下毫无问题——代码很简单,假设把ashx放在程序根目录,那么你就在页面中对应弄上一个img即可:

<img src="/Image.req"/>

这样生成img标签自动也会请求这个handler,生成图片(不要忘记配置handler就可以了)。

然后我想到要做一个dll,因为总觉得把ashx发布出去不是很正规(别人都是发布dll的嘛,呵呵),于是我自己创建了一个类库(ClassLibrary),创建对应的类,实现了IHttpHandler和IRequiresSessionState(拷贝原先ashx中正确的代码,也就是上一篇的代码全部)。然后编译成dll组件,在web.config的<httpHandlers>进行恰当的配置:

<configuration>
  <system.web>
    <!---Httphandler's configuration here……-->
    <httpHandlers>
      <add path="*.req" verb="*" type="TestLibrary.DrawImage"/>
    </httpHandlers>
………………

然后在WebForm和MVC中同时实验——结果很奇怪:在WebForm一切照旧正常,可是MVC程序中死活就是红叉叉报错(显示不出图片,郁闷中……)

后来多方请教,直到遇到了大神imran_ku07之后我才恍然大悟(看到 http://forums.asp.net/t/1800318.aspx这篇类似的问题)——原来当你写"/Image.req"的时候,默认路由就会按照“Controller/Action/DefaultOptionalValue”去匹配:这样的话,Controller就变成了“Image.req”,自然没有这个Controller,然后自然报错(图片无法出来)。

解决办法就是让这个路径不要通过默认的Route方式解析,使用IgnoreRoute方式排除这个路径(粗体):

[C#]

public class MvcApplication : System.Web.HttpApplication
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.IgnoreRoute("{resource}.req/{*pathInfo}");
            routes.MapRoute(
                "Default", // 路由名称
                "{controller}/{action}/{id}", // 带有参数的 URL
                new { controller = "Default", action = "Index", id = UrlParameter.Optional } // 参数默认值
            );

        }

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RegisterRoutes(RouteTable.Routes);
        }
    }

[VB.NET]

Public Class MvcApplication
    Inherits System.Web.HttpApplication
    Public Shared Sub RegisterRoutes(routes As RouteCollection)
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
        routes.IgnoreRoute("{resource}.req/{*pathInfo}")
        ' 路由名称
        ' 带有参数的 URL
            ' 参数默认值
        routes.MapRoute("Default", "{controller}/{action}/{id}", New With { _
            Key .controller = "Default", _
            Key .action = "Index", _
            Key .id = UrlParameter.[Optional] _
        })

    End Sub

    Protected Sub Application_Start()
        AreaRegistration.RegisterAllAreas()
        RegisterRoutes(RouteTable.Routes)
    End Sub
End Class

这样一来,果然在ASP mvc中也可以咯!神啦!

“福无双至,祸不单行”——下午某时有个中软的程序员找我询问关于如何屏蔽MVC插件的事情(它的项目根目录有plugin文件夹,然后里边有大量其它重要文件,现在不想要别人访问)。我想了以下,给了两个解决方案:

1)在根目录web.config中配置如下(相对于根目录的子目录——plugin中后面子文件夹和文件均无法访问)

 <system.web>
    <httpHandlers>
      <add path="/plugin/*" verb="*" type="System.Web.HttpNotFoundHandler"/>
    </httpHandlers>
………………

2)拷贝一个web.config文件到plugin文件夹中,然后这样配置(表示当前文件夹中所有子文件夹和文件均无法访问)

<system.web>
    <httpHandlers>
      <add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
    </httpHandlers>
………………

他的问题是解决了,我又冒出新问题了——既然MVC可以通过:“http://localhost/文件夹名/文件”的形式直接访问文件,那么它为什么不会把“文件夹名”解析成Controller,“文件”解析成Action呢?我猜想如下:

1)任何网页程序默认“潜规则”解析是根据地址栏输入的虚拟路径寻找对应的文件(传统ASP.NET Web也不例外),因此MVC也有此潜规则——如果地址栏输入的虚拟路径恰好可以映射找到真实的文件,那么直接返回结果,就不会在Route了。

2)如果找不到:

2.1)尝试Route(按照默认或者其它定义规则:http://localhost/{Controller}/{Action}/DefaultOptionalParameter)进行解析。

2.2)如果解析成功,那么返回Action中对应的View,解析失败,抛出异常。

3)如果某个请求地址(比如Image.req纯粹是一个ashx请求,真实文件路径均不存在),直接使用Route规则匹配肯定错误,此时你就应该告知系统“这是例外”(用IgnoreRoute即可)。

附加说明:http://visualstudiogallery.msdn.microsoft.com/24ef1a72-6b1c-428a-9908-8990d5d347f2

我做了一个可以供ASP.NET和MVC使用的验证码控件dll(其中ASP.NET一般页面可以直接使用,MVC的则必须通过一些手动配置才可以使用)。有兴趣可以下载看看哦!

posted @ 2012-05-06 17:57 Serviceboy 阅读(232) 评论(0) 编辑

网站上验证码效果一般制作方法是:

1)使用HttpHandler(一般处理程序)绘制随机验证码的图,以及产生随机码,并输出到页面的OutputStream中。

2)页面中使用异步方式(js等)进行刷新当前页面的验证码。

【示例】

1)创建一个“一般应用处理程序ashx”,代码如下:

[C#]

public class ValidationCode : IHttpHandler
    {
        //随机发生器
        static Random r = new Random(Guid.NewGuid().GetHashCode());
        //排除黑色、透明色颜色,因为底色黑色
        static PropertyInfo[] colors = (typeof(Brushes).GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.GetProperty | System.Reflection.BindingFlags.Static)).Where(p => p.Name != "Black" && p.Name != "Transparent").Select(p => p).ToArray();
        //排除黑色颜色,因为底色黑色
        static PropertyInfo[] linecolors = (typeof(Pens).GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.GetProperty | System.Reflection.BindingFlags.Static)).Where(p => p.Name != "Black").Select(p => p).ToArray();
        //获取静态类Brushes实例对象
        static object colorobj = typeof(Brushes).GetConstructor(BindingFlags.NonPublic, null, Type.EmptyTypes, null);

        //获取静态类Pens实例对象
        static object penobj = typeof(Pens).GetConstructor(BindingFlags.NonPublic, null, Type.EmptyTypes, null);

        //每个随机字符的宽度
        const float PERNUMBERWIDTH = 40.0f;
        //每个字符的高度
        const float PERNUMBERHEIGHT = 50.0f;

        public void ProcessRequest(HttpContext context)
        {
                //获取要产生多少随机数(默认产生5个)
                int reqNum = 5;
                if (context.Request.QueryString["reqNum"] != null)
                {
                    int.TryParse(context.Request.QueryString["reqNum"], out reqNum);
                }
            
                //产生多少大的背景图
                Bitmap bt = new Bitmap((int)(PERNUMBERWIDTH*reqNum), (int)PERNUMBERHEIGHT);
                Graphics g = Graphics.FromImage(bt);
               
                //产生4个随机数(number可以被保存到Session中)
                string numbers = "";

                //绘制数字
                for (int i = 1; i <= reqNum; i++)
                {
                    numbers += r.Next(0, 9).ToString();
                    var color = (PropertyInfo)colors.GetValue(r.Next(0, colors.Length));
                    context.Response.Write(color.Name + "<br/>");
                    Brush randomcolor = (Brush)color.GetValue(colorobj, null);
                    g.DrawString(numbers[i-1].ToString(), new Font("黑体", PERNUMBERWIDTH),randomcolor, new PointF((i-1)*PERNUMBERWIDTH, 0f));
                }

                //绘制随机线条
                int linenum = r.Next(10, 21);
                for (int i = 1; i <= linenum; i++)
                {
                     var linecolor = (PropertyInfo)linecolors.GetValue(r.Next(0, colors.Length));
                    Pen randomcolor = (Pen)linecolor.GetValue(penobj, null);
                    g.DrawLine(randomcolor, new PointF((float)(r.NextDouble() * PERNUMBERWIDTH * reqNum), (float)(r.NextDouble() * PERNUMBERHEIGHT)), new PointF((float)(r.NextDouble() * PERNUMBERWIDTH * reqNum), (float)(r.NextDouble() * PERNUMBERHEIGHT)));
                }
                g.Dispose();
                context.Response.Clear();
                context.Response.ContentType = "image/jpeg";
                bt.Save(context.Response.OutputStream, ImageFormat.Jpeg);
                bt.Dispose();
                context.Response.End();
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }

[VB.NET]

Public Class ValidationCode
    Implements IHttpHandler
    '随机发生器
    Shared r As New Random(Guid.NewGuid().GetHashCode())
    '排除黑色、透明色颜色,因为底色黑色
    Shared colors As PropertyInfo() = (GetType(Brushes).GetProperties(System.Reflection.BindingFlags.[Public] Or System.Reflection.BindingFlags.GetProperty Or System.Reflection.BindingFlags.[Static])).Where(Function(p) p.Name <> "Black" AndAlso p.Name <> "Transparent").[Select](Function(p) p).ToArray()
    '排除黑色颜色,因为底色黑色
    Shared linecolors As PropertyInfo() = (GetType(Pens).GetProperties(System.Reflection.BindingFlags.[Public] Or System.Reflection.BindingFlags.GetProperty Or System.Reflection.BindingFlags.[Static])).Where(Function(p) p.Name <> "Black").[Select](Function(p) p).ToArray()
    '获取静态类Brushes实例对象
    Shared colorobj As Object = GetType(Brushes).GetConstructor(BindingFlags.NonPublic, Nothing, Type.EmptyTypes, Nothing)

    '获取静态类Pens实例对象
    Shared penobj As Object = GetType(Pens).GetConstructor(BindingFlags.NonPublic, Nothing, Type.EmptyTypes, Nothing)

    '每个随机字符的宽度
    Const PERNUMBERWIDTH As Single = 40F
    '每个字符的高度
    Const PERNUMBERHEIGHT As Single = 50F

    Public Sub ProcessRequest(context As HttpContext)
        '获取要产生多少随机数(默认产生5个)
        Dim reqNum As Integer = 5
        If context.Request.QueryString("reqNum") IsNot Nothing Then
            Integer.TryParse(context.Request.QueryString("reqNum"), reqNum)
        End If

        '产生多少大的背景图
        Dim bt As New Bitmap(CInt(Math.Truncate(PERNUMBERWIDTH * reqNum)), CInt(Math.Truncate(PERNUMBERHEIGHT)))
        Dim g As Graphics = Graphics.FromImage(bt)

        '产生4个随机数(number可以被保存到Session中)
        Dim numbers As String = ""

        '绘制数字
        For i As Integer = 1 To reqNum
            numbers += r.[Next](0, 9).ToString()
            Dim color = DirectCast(colors.GetValue(r.[Next](0, colors.Length)), PropertyInfo)
            context.Response.Write(Convert.ToString(color.Name) & "<br/>")
            Dim randomcolor As Brush = DirectCast(color.GetValue(colorobj, Nothing), Brush)
            g.DrawString(numbers(i - 1).ToString(), New Font("黑体", PERNUMBERWIDTH), randomcolor, New PointF((i - 1) * PERNUMBERWIDTH, 0F))
        Next

        '绘制随机线条
        Dim linenum As Integer = r.[Next](10, 21)
        For i As Integer = 1 To linenum
            Dim linecolor = DirectCast(linecolors.GetValue(r.[Next](0, colors.Length)), PropertyInfo)
            Dim randomcolor As Pen = DirectCast(linecolor.GetValue(penobj, Nothing), Pen)
            g.DrawLine(randomcolor, New PointF(CSng(r.NextDouble() * PERNUMBERWIDTH * reqNum), CSng(r.NextDouble() * PERNUMBERHEIGHT)), New PointF(CSng(r.NextDouble() * PERNUMBERWIDTH * reqNum), CSng(r.NextDouble() * PERNUMBERHEIGHT)))
        Next
        g.Dispose()
        context.Response.Clear()
        context.Response.ContentType = "image/jpeg"
        bt.Save(context.Response.OutputStream, ImageFormat.Jpeg)
        bt.Dispose()
        context.Response.[End]()
    End Sub

    Public ReadOnly Property IsReusable() As Boolean
        Get
            Return False
        End Get
    End Property
End Class

注意:

1)一些诸如Brushes等特定因为是公用的,需要通过反射获取全部的颜色属性列表,因此使用了静态变量,这样不必每次都初始化,节省内存和时间。

2)Brushes避免黑色和透明色(本示例背景色是黑色),Pens只需避免黑色即可。有关Brushes颜色,可以查阅:http://msdn.microsoft.com/zh-cn/library/system.windows.media.brush(v=vs.95).aspx

3)Bitmap类是用于绘制使用的,一般是空白的黑色背景。一般配合Image类+Graphics画布使用,进行绘制。

4)BitMap的Save方法有若干个重载版本,其中之一可以指定输出流以及设置图片格式。本示例就是使用了这个函数。

【应用】

Html中代码(验证码部分,局部):

<h1>
        验证码
    </h1>
    <script>
        function ChangeSD() {
            document.getElementById("imgSD").src = "";
            document.getElementById("imgSD").src = "/ValidationCode.ashx?reqNum=10";
             };
   </script>
    <img src="/ValidationCode.ashx?reqNum=10" id="imgSD" />
    <input type="button" value="Change Validation Key" onclick="ChangeSD()" />

注意,之所以使用js设置img的src两次,是因为重复的路径不会引发请求。

posted @ 2012-04-30 13:07 Serviceboy 阅读(332) 评论(2) 编辑
摘要: 有一个MSDN客户提问在WinForm中如何实现拖拽效果——比如在WinForm中有一个Button,我要实现的效果是拖拽这个Button到目标位置后生成一个该控件的副本。其实这个操作主要分成三步走:1)确定被拖拽的对象:这里是Button(要使得Button被单击之后可以拖拽,那么必须处理其MouseDown事件,同时调用其DoDragDrop——该函数接受两个参数:i)要拖动的数据。ii)拖动的效果(该效果是2“目标位置”所能够接受的效果,是一个枚举值):[C#]Button1.DoDragDrop(Button1, DragDropEffects.Copy || DragDropEffe阅读全文
posted @ 2012-04-29 14:02 Serviceboy 阅读(401) 评论(0) 编辑
摘要: 在上一章中您学习了如何使用EntityFramework和SQL Server Compact创建一个ASP.NET MVC程序存储展示数据,在本章中您将学习如何在控制器和视图中对自动生成的增删改查代码进行自定义。注意:为了在您的控制器和数据访问层中创建一个抽象层,通常的做法是实现一个“库模式”。不过为了使得这些教程变得更加简单,暂且不必实现此模式,我们将在稍后的“实现单元工作库模式”中得以完成。本章中您要创建以下一系列页面:【创建一个详细信息页】Index页面默认架构忽略了Enrollments属性,那是因为这是一个集合属性。在详细信息页中你将通过一个Html表展示此一系列内容。在“Cont阅读全文
posted @ 2012-04-27 20:35 Serviceboy 阅读(61) 评论(0) 编辑
摘要: 在前几篇中你已经处理了由3个实体组成的数据模型,本章节中您将添加更多的数据实体以及关系,并且充分利用数据标注属性进一步地控制模型类的行为。以下便是当你完成这次任务之后完整的数据类模型:【使用属性控制格式化数据、数据验证及数据库映射】在这部分您将可以看到用于数据模型类的属性的若干例子,用以控制数据的格式化,验证以及数据库的映射。接着在稍后几章中您将创建一个完整的School数据模型,您通过对已经创建的类添加属性,并且通过创建新类用于维护模型中的实体类型。1)DisplayFormat属性:对于学生选课日期可能你在乎的仅是“日期”,但是显示的结果却都带有时间;使用数据标记属性你只需做一步代码的改变阅读全文
posted @ 2012-04-27 17:15 Serviceboy 阅读(54) 评论(0) 编辑