天天看點

點選式驗證碼

看到網上的點選式驗證碼,覺得很友善.

環境 W8.1 VS2013 .NET4.5 WPF

驗證碼類 CreateValidateImg 主要包含生成驗證圖檔的方法,和比較驗證碼的方法

點選式驗證碼
點選式驗證碼
/// <summary>
    /// 生成驗證碼圖檔
    /// 随機的驗證碼包含0-9十個數字和A-Z 26個字母(ascii65-90)
    /// </summary>
    internal class CreateValidateImg
    {
        private static string codesource = "QAZXSWEDCVFRTGBNHYUJMKIOLP0123456789";// 随機源
        private static Random rand = new Random();// 随機工具
        private static Dictionary<Point, string> pointdic = null;// 坐标系(鍵)所在驗證碼(值)
        private static string codestr = null;// 驗證碼
        private static int width = 201;// 寬
        private static int height = 121;// 高
        internal static MemoryStream ms = null;// 儲存驗證碼選擇圖檔的流
        public static MemoryStream mscode = null;// 儲存驗證碼圖檔的流
        
        /// <summary>
        /// 畫出驗證碼圖檔,
        /// </summary>
        /// <returns></returns>
        internal static void ValidateImg()
        {
            /*建立驗證碼選擇圖檔*/
            Bitmap vpic = new Bitmap(width, height);// 建立一個圖檔 用于驗證碼選擇

            Graphics g = Graphics.FromImage(vpic);// 根據這個圖檔建立繪畫工具
            // 開始繪畫
            g.FillRectangle(Brushes.Gold, 0, 0, width, height);// 填充一個顔色
            // 1.畫一個3行4列的網格,均分160長 60高
            for (int i = 0; i < 4; i++)
            {
                g.DrawLine(Pens.Blue, new Point(0, i * 40), new Point(width, i * 40));
            }
            for (int i = 0; i < 5; i++)
            {
                g.DrawLine(Pens.Blue, new Point(i * 50, 0), new Point(i * 50,width));
            }
            // 2.畫12個字元
            pointdic = new Dictionary<Point, string>(12);// 初始化坐标系的值
            for (int i = 0; i < 3; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    char a = codesource[rand.Next(0, 36)];// 碼
                    g.DrawString(a.ToString(), new Font("Anonymous Pro", 15,FontStyle.Bold), Brushes.Black,
                        new Point(j * 50 + 16, i * 40 + 10));// 畫畫
                    pointdic.Add(new Point(j,i), a.ToString());// 坐标 碼 鍵值對
                }
            }
            // 建立記憶體流,将圖檔儲存在其中
            ms = new MemoryStream();
            vpic.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
            // 讓記憶體流回到開頭的位置
            ms.Seek(0, SeekOrigin.Begin);

            /********再建立驗證碼圖檔********/
            Bitmap vimg = new Bitmap(200,30);
            g = Graphics.FromImage(vimg);
            g.FillRectangle(Brushes.Orange, 0, 0, 200, 30);
            // 從字典中随機5個驗證碼字元
            string[] vcodearray = pointdic.Values.ToArray<string>();
            codestr = "";
            for (int i = 0; i < 5; i++)
            {
                string vc = vcodearray[rand.Next(0, 12)];
                g.DrawString(vc, new Font("Anonymous Pro", 15, FontStyle.Bold), Brushes.Black,
                        new Point(i*30+30,5));// 畫畫
                codestr = codestr + vc;
            }
            mscode = new MemoryStream();
            vimg.Save(mscode, System.Drawing.Imaging.ImageFormat.Jpeg);
            mscode.Seek(0, SeekOrigin.Begin);
        }


        /// <summary>
        /// 畫出驗證碼圖檔中被點選的部分
        /// </summary>
        /// <param name="newxy"></param>
        /// <returns></returns>
        internal static MemoryStream ValidateImgPartial(int x,int y)
        {

            // 目前的驗證碼圖檔 從流中讀出來
            Bitmap bmp = new Bitmap(ms);
            // 将點選指定的部分複制出來
            Bitmap pbmp = bmp.Clone(new Rectangle(x * 50, y * 40, 51, 41), System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            // 傳回圖檔資料
            MemoryStream pms = new MemoryStream();
            pbmp.Save(pms, System.Drawing.Imaging.ImageFormat.Jpeg);
            pms.Seek(0, SeekOrigin.Begin);
            return pms;
        }
        /// <summary>
        /// 根據坐标鍵擷取單個驗證碼
        /// </summary>
        /// <param name="xy"></param>
        internal static string GetCodeByPoint(int x ,int y)
        {
            Point xy = new Point(x, y);
            if(pointdic.ContainsKey(xy))
            {
                string c=pointdic[xy];
                return c;
            }
            return "no code are finded";
        }

        /// <summary>
        /// 根據坐标數組比較驗證碼正确性
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        internal static string ToValidate(Point[] point)
        {
            StringBuilder sb = new StringBuilder();
            foreach (Point item in point)
            {
                if (pointdic.ContainsKey(item))
                {
                    sb.Append(pointdic[item]);
                }
                else
                {
                    return "驗證碼有誤";
                }
            }
            if (string.Compare(codestr, sb.ToString(), true)==0)
            {
                return "驗證碼正确";
            }
            else
            {
                return "驗證碼有誤";
            }
        }
    }      

View Code

WPF窗體 包含一個驗證碼圖檔框 ,一個驗證碼選擇圖檔框,點選時存放所點選的驗證碼圖檔的框(共有5個)

點選式驗證碼
點選式驗證碼
/// <summary>
    /// VImage.xaml 的互動邏輯
    /// </summary>
    public partial class VImage : Window
    {
        public VImage()
        {
            InitializeComponent();
        }
        private List<System.Drawing.Point> pointlist = new List<System.Drawing.Point>();// 收集每次點選的坐标,在重置按鈕時會被清空
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            InItValidateImage();
        }

        private void InItValidateImage()
        {
            // 1。驗證碼圖檔
            BitmapImage big = new BitmapImage();
            big.BeginInit();// 初始化開始
            CreateValidateImg.ValidateImg();
            big.StreamSource = CreateValidateImg.mscode;// 圖檔源來自這個流
            big.EndInit();// 初始化完成 
            imgcode.Source = big;// 設為IMAGE控件的源

            // 2。驗證碼選框圖檔
            big = new BitmapImage();// 用于IMAGE控件的圖像源
            big.BeginInit();// 初始化開始
            big.StreamSource = CreateValidateImg.ms;// 圖檔源來自這個流
            big.EndInit();// 初始化完成 
            imgbox.Source = big;// 設為IMAGE控件的源
        }

        private void imgbox_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Point xy = e.GetPosition(imgbox);
            //MessageBox.Show((int)xy.X / 50 + "   " + (int)xy.Y / 40);
            Image[] img5 = new Image[] { vimg1, vimg2, vimg3, vimg4, vimg5 };
            for (int i = 0; i < img5.Length; i++)
            {
                if (img5[i].Source == null)
                {
                    BitmapImage big = new BitmapImage();
                    big.BeginInit();
                    big.StreamSource=CreateValidateImg.ValidateImgPartial((int)xy.X / 50,(int)xy.Y / 40);
                    big.EndInit();
                    img5[i].Source = big;
                    //MessageBox.Show(CreateImg.GetCodeByPoint((int)xy.X / 50, (int)xy.Y / 40) + "  " + (int)xy.X / 50 + "   " + (int)xy.Y / 40);
                    this.pointlist.Add(new System.Drawing.Point((int)xy.X / 50, (int)xy.Y / 40));// 加入到坐标集合
                    if (i != img5.Length-1)
                        break;
                    // 比對驗證碼.當點完最後一個時才會驗證
                    // 1.收集所有點選過的坐标(目前是放在本視窗定義的一個成員内)
                    isvalidatelbl.Content = CreateValidateImg.ToValidate(this.pointlist.ToArray());
                }
            }
        }

        // 重置
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // 清空圖檔
            Image[] img5 = new Image[] { vimg1, vimg2, vimg3, vimg4, vimg5 };
            foreach (Image item in img5)
            {
                item.Source = null;
            }
            // 清空坐标清單
            this.pointlist = new List<System.Drawing.Point>();
            // 清空驗證結果資訊
            isvalidatelbl.Content = "請選擇驗證碼";
            // 更換驗證碼
            this.InItValidateImage();
        }

        

    }      

View Code

WPF窗體XAML

點選式驗證碼
點選式驗證碼
<Window x:Class="WpfApplication1.VImage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="VImage" Height="400" Width="300" Loaded="Window_Loaded" WindowStartupLocation="CenterOwner">
    <Grid>
        <Image x:Name="imgbox" HorizontalAlignment="Left" Height="121" Margin="52,79,0,0" VerticalAlignment="Top" Width="201" MouseLeftButtonUp="imgbox_MouseLeftButtonUp"/>
        <WrapPanel x:Name="wpl1" Height="45" Margin="0,214,0,0" VerticalAlignment="Top">
            <Image x:Name="vimg1" Height="41" Width="51" RenderTransformOrigin="0.5,0.5" VerticalAlignment="Top" HorizontalAlignment="Left"/>
            <Image x:Name="vimg2" Height="41" Width="51" OpacityMask="#FFDE4A4A" RenderTransformOrigin="0.5,0.5" VerticalAlignment="Top" HorizontalAlignment="Left"/>
            <Image x:Name="vimg3" Height="41" Width="51" OpacityMask="#FFDE4A4A" RenderTransformOrigin="0.5,0.5" VerticalAlignment="Top" HorizontalAlignment="Left"/>
            <Image x:Name="vimg4" Height="41" OpacityMask="#FFDE4A4A" Width="51" RenderTransformOrigin="0.5,0.5" VerticalAlignment="Top" HorizontalAlignment="Left"/>
            <Image x:Name="vimg5" Height="41" Width="51" RenderTransformOrigin="0.5,0.5" OpacityMask="#FFDE4A4A" VerticalAlignment="Top" HorizontalAlignment="Left"/>
        </WrapPanel>
        <Button Content="reset" HorizontalAlignment="Left" Height="22" Margin="106,322,0,0" VerticalAlignment="Top" Width="68" Click="Button_Click" RenderTransformOrigin="2.088,0.727"/>
        <Image x:Name="imgcode" HorizontalAlignment="Left" Height="31" Margin="65,43,0,0" VerticalAlignment="Top" Width="148" RenderTransformOrigin="0.574,0.774"/>
        <Label Content="請在表格中點選輸入驗證碼" HorizontalAlignment="Left" Height="28" Margin="25,10,0,0" VerticalAlignment="Top" Width="187"/>
        <Label x:Name="isvalidatelbl" Content="請點選圖檔選擇驗證碼" VerticalAlignment="Top" Margin="0,275,0,0" HorizontalContentAlignment="Center" Foreground="#FF8D2121" FontWeight="Bold"/>

    </Grid>
</Window>      

View Code

大體實作思路:

1.畫出兩個圖檔,一張是包含驗證碼的圖檔(小圖),一張是包含驗證碼和其它字元的圖檔(大圖),在這圖上點選選擇驗證碼,這兩個圖的生成是同時的,成對的.

點選式驗證碼

2.将兩張圖檔儲存為流,并且設為Image控件的Source值.這個方法見http://www.cnblogs.com/pilang/archive/2010/05/30/1747485.html

3.點選時,得到坐标.就是那個3行4清單格裡的數字的左上角坐标,如上圖中的X坐标是(2,1).當點選選擇時,通過擷取點選的坐标計算所點字元的相對坐标,如點選X所在的方格時,會得到此時滑鼠的坐标值(須以目前圖檔做為參照元素),将X/50,Y/40就得到X(2,1)的相對坐标.50,40指X所在方格的長寬.

4.将上面得到的坐标還原成實際方格所在坐标,就是再乘以相應的長和寬.到原圖中複制出以此坐标為起點,長和寬的值為大小構造的矩形區域.實際就是那個被點的字元塊.複制後設為下面的小格中的Image的source值

5.對比驗證碼,收集每次點選的坐标,在背景字典中(坐标和驗證碼字元的鍵值對)找出這些值,然後比較字元即可

其它 :坐标計算 圖檔部分複制 WPF控件的用法和winform差别很大,以前的辦法都不好使了