WPF 实现自绘验证码

控件名:VerifyCode

作者:WPFDevelopersOrg

原文链接:   https://github.com/WPFDevelopersOrg/WPFDevelopers

  • 框架使用大于等于.NET40

  • Visual Studio 2022;

  • 项目使用 MIT 开源许可协议;

如何通过DrawingVisual进行自绘验证码

  • 首先定义验证码池作为随机生成验证码所需; private const string strCode = "abcdefhkmnprstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"; 可自行编写所需的内容(可以注意到上方的验证码池中某些存在差异的字母与数字不在其中这是为 了更好的方便生成的验证码不会给用户造成误解,用户应该输入字母l还是数字1,所以进行了排除);

  • 通过方法CreateCode生成四或更多位随机验证码,然后调用方法CreateCheckCodeImage进行绘制验证码;

    • 第一步绘制边框与背景;

    • 第二步绘制验证码;

    • 第三步绘制背景的线条;

    • 第四步绘制随机颜色进行扰乱视觉;

    • 第四步创建并返回ImageSource进行展现;

  • 鼠标点击验证码控件就能实现更换验证码;

1)VerifyCode.cs 代码如下;

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace WPFDevelopers.Controls
{
    [TemplatePart(Name = ImageTemplateName, Type = typeof(Image))]
    public class VerifyCode : Control
    {
        private const string ImageTemplateName = "PART_Image";
        private const string strCode = "abcdefhkmnprstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789";

        public static readonly DependencyProperty ImageSourceProperty =
            DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(VerifyCode),
                new PropertyMetadata(null));

        private Image _image;
        private Size _size = new Size(70, 23);

        public VerifyCode()
        {
            Foreground = DrawingContextHelper.Brush;
            Loaded += CheckCode_Loaded;
        }

        /// <summary>
        ///     随机生成的验证码
        /// </summary>
        public ImageSource ImageSource
        {
            get => (ImageSource)GetValue(ImageSourceProperty);
            set => SetValue(ImageSourceProperty, value);
        }
        private void CheckCode_Loaded(object sender, RoutedEventArgs e)
        {
            ImageSource = CreateCheckCodeImage(CreateCode(4), (int)ActualWidth, (int)ActualHeight);
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _image = GetTemplateChild(ImageTemplateName) as Image;
            if (_image != null)
                _image.PreviewMouseDown += _image_PreviewMouseDown;
        }

        private void _image_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (!IsLoaded)
                return;

            ImageSource = CreateCheckCodeImage(CreateCode(4), (int)ActualWidth, (int)ActualHeight);
        }

        private string CreateCode(int strLength)
        {
            var _charArray = strCode.ToCharArray();
            var randomCode = "";
            var temp = -1;
            var rand = new Random(Guid.NewGuid().GetHashCode());
            for (var i = 0; i < strLength; i++)
            {
                if (temp != -1)
                    rand = new Random(i * temp * (int)DateTime.Now.Ticks);
                var t = rand.Next(strCode.Length - 1);
                if (!string.IsNullOrWhiteSpace(randomCode))
                    while (randomCode.ToLower().Contains(_charArray[t].ToString().ToLower()))
                        t = rand.Next(strCode.Length - 1);
                if (temp == t)
                    return CreateCode(strLength);
                temp = t;

                randomCode += _charArray[t];
            }

            return randomCode;
        }

        private ImageSource CreateCheckCodeImage(string checkCode, int width, int height)
        {
            if (string.IsNullOrWhiteSpace(checkCode))
                return null;
            if (width <= 0 || height <= 0)
                return null;
            var drawingVisual = new DrawingVisual();
            var random = new Random(Guid.NewGuid().GetHashCode());
            using (var dc = drawingVisual.RenderOpen())
            {
                dc.DrawRectangle(Brushes.White, new Pen(Foreground, 1), new Rect(_size));
                var formattedText = DrawingContextHelper.GetFormattedText(checkCode, Foreground,
                    FlowDirection.LeftToRight, 20, FontWeights.Bold);
                dc.DrawText(formattedText,
                    new Point((_size.Width - formattedText.Width) / 2, (_size.Height - formattedText.Height) / 2));

                for (var i = 0; i < 10; i++)
                {
                    var x1 = random.Next(width - 1);
                    var y1 = random.Next(height - 1);
                    var x2 = random.Next(width - 1);
                    var y2 = random.Next(height - 1);

                    dc.DrawGeometry(Brushes.Silver, new Pen(Brushes.Silver, 0.5D),
                        new LineGeometry(new Point(x1, y1), new Point(x2, y2)));
                }

                for (var i = 0; i < 100; i++)
                {
                    var x = random.Next(width - 1);
                    var y = random.Next(height - 1);
                    var c = new SolidColorBrush(Color.FromRgb((byte)random.Next(0, 255), (byte)random.Next(0, 255),
                        (byte)random.Next(0, 255)));
                    dc.DrawGeometry(c, new Pen(c, 1D),
                        new LineGeometry(new Point(x - 0.5, y - 0.5), new Point(x + 0.5, y + 0.5)));
                }

                dc.Close();
            }

            var renderBitmap = new RenderTargetBitmap(70, 23, 96, 96, PixelFormats.Pbgra32);
            renderBitmap.Render(drawingVisual);
            return BitmapFrame.Create(renderBitmap);
        }
    }
}

2)VerifyCode.xaml 代码如下;

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:controls="clr-namespace:WPFDevelopers.Controls">

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Basic/ControlBasic.xaml"/>
    </ResourceDictionary.MergedDictionaries>

    <Style TargetType="{x:Type controls:VerifyCode}" BasedOn="{StaticResource ControlBasicStyle}">
        <Setter Property="Background" Value="{x:Null}"/>
        <Setter Property="Width" Value="100"/>
        <Setter Property="Height" Value="40"/>
        <Setter Property="Cursor" Value="Hand"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type controls:VerifyCode}">
                    <Image x:Name="PART_Image" Stretch="Fill" Source="{TemplateBinding ImageSource}"/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

3)VerifyCodeExample.xaml 代码如下;

<UserControl x:Class="WPFDevelopers.Samples.ExampleViews.VerifyCodeExample"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews"
             xmlns:wpfdev="https://github.com/WPFDevelopersOrg/WPFDevelopers"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <UniformGrid Rows="2" Columns="2">
        <wpfdev:VerifyCode Foreground="LimeGreen"/>
        <wpfdev:VerifyCode Foreground="Red"/>
        <wpfdev:VerifyCode Foreground="DodgerBlue"/>
        <wpfdev:VerifyCode Foreground="HotPink"/>
    </UniformGrid>
</UserControl>
2019c32b5b37b503265e20b43218e331.png

技术群:添加小编微信并备注进群

小编微信:mm1552923   

公众号:dotNet编程大全    

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐