Wpf 使用TextBox制作一个可以切换明密文的PasswordBox


前言

在做软件登录的时候,常常要用到输入密码框,但是Wpf自带的PasswordBox无法查看明文。所以我查了资料,用converter和TextBox做了一个可以查看密码的密码框


提示:以下是本篇文章正文内容,下面案例可供参考

一、修改TextBox模板

模板中主要用一个grid分开了按钮和TextBox原本自带的ScrollViewer 按钮用togglebutton改的,template里面啥也不带,然后内容填充用了Path Path这边我是用svg的图片拷贝的路径代码,具体可参考链接: [link](https://www.cnblogs.com/SunsetAzure/p/6656361.html). 最后的构造函数里面添加多重绑定(也可以写在样式里面)

代码如下:

        <Geometry x:Key="eyeOff">M17.0835 9.86562L17.082 9.86406L17.0804 9.86249C16.5116 8.66406 15.8304 7.69531 15.0382 6.95624L14.246 7.74999C14.9288 8.37812 15.5195 9.21406 16.0288 10.2641C14.7273 12.9562 12.882 14.2344 10.3601 14.2344C9.55852 14.2344 8.82415 14.1047 8.15383 13.8422L7.29915 14.6969C8.21321 15.1375 9.23196 15.3594 10.3601 15.3594C13.3632 15.3594 15.6007 13.7891 17.082 10.6672C17.2023 10.4141 17.2023 10.1203 17.0835 9.86562ZM16.0835 4.67499L15.421 4.01249C15.3726 3.96406 15.2929 3.96406 15.2445 4.01249L13.4195 5.83593C12.507 5.39374 11.4882 5.17187 10.3601 5.17187C7.35696 5.17187 5.11946 6.74218 3.63821 9.86406V9.86562C3.5179 10.1187 3.5179 10.4156 3.63821 10.6703C4.20696 11.8687 4.88821 12.8375 5.6804 13.5781L4.10696 15.1484C4.05852 15.1969 4.05852 15.2766 4.10696 15.325L4.76946 15.9875C4.8179 16.0359 4.89758 16.0359 4.94602 15.9875L16.0835 4.84999C16.132 4.80312 16.132 4.72343 16.0835 4.67499ZM8.5929 10.6625C8.56321 10.5344 8.54758 10.4016 8.54758 10.2656C8.54758 9.29843 9.3304 8.51562 10.2976 8.51562C10.4335 8.51562 10.5679 8.53124 10.6945 8.56093L8.5929 10.6625ZM11.4757 7.77968C11.1179 7.61093 10.7195 7.51562 10.2976 7.51562C8.77883 7.51562 7.54758 8.74687 7.54758 10.2656C7.54758 10.6875 7.6429 11.0859 7.81165 11.4437L6.47415 12.7812C5.79133 12.1531 5.20071 11.3172 4.69133 10.2672C5.99446 7.57499 7.83977 6.29687 10.3601 6.29687C11.1616 6.29687 11.896 6.42656 12.5663 6.68906L11.4757 7.77968ZM10.2976 12.0156C10.1976 12.0156 10.0991 12.0078 10.0038 11.9906L9.2054 12.7891C9.53977 12.9344 9.91008 13.0141 10.2976 13.0141C11.8163 13.0141 13.0476 11.7828 13.0476 10.2641C13.0476 9.87656 12.9679 9.50624 12.8226 9.17187L12.0241 9.97031C12.0398 10.0656 12.0491 10.1641 12.0491 10.2641C12.0476 11.2328 11.2648 12.0156 10.2976 12.0156Z</Geometry>
        <Geometry x:Key="eyeOn">M17.0798 9.59701C15.5991 6.47766 13.3607 4.90784 10.36 4.90784C7.35785 4.90784 5.12104 6.47766 3.64025 9.59857C3.58085 9.72434 3.55005 9.8617 3.55005 10.0008C3.55005 10.1399 3.58085 10.2772 3.64025 10.403C5.12104 13.5224 7.35941 15.0922 10.36 15.0922C13.3622 15.0922 15.5991 13.5224 17.0798 10.4015C17.2001 10.1484 17.2001 9.85474 17.0798 9.59701ZM10.36 13.9675C7.84051 13.9675 5.99577 12.6898 4.69461 10C5.99577 7.31022 7.84051 6.03249 10.36 6.03249C12.8796 6.03249 14.7243 7.31022 16.0255 10C14.7259 12.6898 12.8811 13.9675 10.36 13.9675ZM10.2976 7.25086C8.77928 7.25086 7.54841 8.48173 7.54841 10C7.54841 11.5183 8.77928 12.7492 10.2976 12.7492C11.8158 12.7492 13.0467 11.5183 13.0467 10C13.0467 8.48173 11.8158 7.25086 10.2976 7.25086ZM10.2976 11.7495C9.33068 11.7495 8.54811 10.9669 8.54811 10C8.54811 9.03312 9.33068 8.25055 10.2976 8.25055C11.2645 8.25055 12.047 9.03312 12.047 10C12.047 10.9669 11.2645 11.7495 10.2976 11.7495Z</Geometry>


    <!--眼睛按钮的样式-->
    <Style TargetType="{x:Type ToggleButton}" x:Key="EyeButton">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ToggleButton">
                    <ContentPresenter Height="{TemplateBinding Height}" Width="{TemplateBinding Width}"/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

        <Style x:Key="PasswordTextBox" TargetType="{x:Type local:PasswordTextBox}">
            <Setter Property="PasswordChar" Value="*"/>
            <Setter Property="Encrypt" Value="True"/>
            <Setter Property="InputMethod.IsInputMethodEnabled" Value="False"/>
            <Setter Property="FontSize" Value="14"/>
            <Setter Property="Height" Value="44"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="BorderBrush" Value="#D9D9D9"/>
            <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
            <Setter Property="HorizontalContentAlignment" Value="Left"/>
            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <Setter Property="AllowDrop" Value="true"/>
            <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
            <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type local:PasswordTextBox}">
                        <Border x:Name="border" 
                            BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
                            <Grid x:Name="PART_InnerGrid">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition/>
                                    <ColumnDefinition Width="auto"/>
                                </Grid.ColumnDefinitions>
                                <Grid x:Name="panel" VerticalAlignment="Center" Margin="12,0,0,0">
                                    <ScrollViewer x:Name="PART_ContentHost" BorderThickness="0" IsTabStop="False"
                                              Background="{x:Null}" VerticalContentAlignment="Center" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"
                                              />
                                    <!--创建提示字符-->
                                    <TextBlock x:Name="PART_PlaceHolder" FontFamily="{TemplateBinding FontFamily}"
                                           Text="{Binding Path=PlaceHolder,RelativeSource={RelativeSource TemplatedParent}}"  
                                           Foreground="#BFBFBF"
                                           Visibility="Collapsed"
                                           />
                                </Grid>
                                <!--触发按钮显示样式-->
                                <ToggleButton Focusable="False" Width="20" x:Name="PART_ToggleEye" Grid.Column="1" Margin="3,3" BorderThickness="0" Style="{StaticResource EyeButton}" 
                                          IsChecked="{Binding Encrypt, RelativeSource={RelativeSource TemplatedParent}}">
                                    <Grid Background="Transparent">
                                        <Path Stretch="Uniform" x:Name="img_eye" Data="{StaticResource eyeOn}" Fill="black" Opacity="0.25"/>
                                    </Grid>
                                </ToggleButton>
                            </Grid>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsChecked" SourceName="PART_ToggleEye" Value="true">
                                <Setter TargetName="img_eye" Property="Data" Value="{StaticResource eyeOff}"></Setter>
                            </Trigger>
                            <!--密码框为空不且没有获取焦点时,设置提示文字显示-->
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="Password"  Value=""/>
                                    <Condition Property="IsFocused" Value="False"/>
                                </MultiTrigger.Conditions>
                                <Setter TargetName="PART_PlaceHolder" Property="Visibility" Value="Visible"/>
                            </MultiTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="IsInactiveSelectionHighlightEnabled" Value="true"/>
                        <Condition Property="IsSelectionActive" Value="false"/>
                    </MultiTrigger.Conditions>
                    <Setter Property="SelectionBrush" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
                </MultiTrigger>
            </Style.Triggers>
        </Style> 

二、添加TextBox派生类

此类主要添加了三个依赖属性(还送了一个PlaceHolder水印)

代码如下:

public class PasswordTextBox : TextBox
    {
        /// <summary>
        /// 密码
        /// </summary>
        public string Password
        {
            get { return (string)GetValue(PasswordProperty); }
            set { SetValue(PasswordProperty, value); }
        }

        /// <summary>
        /// 密码修饰符
        /// </summary>
        public string PasswordChar
        {
            get { return (string)GetValue(PasswordCharProperty); }
            set { SetValue(PasswordCharProperty, value); }
        }
        
        /// <summary>
        /// 是否加密
        /// </summary>
        public bool Encrypt
        {
            get { return (bool)GetValue(EncryptProperty); }
            set
            {
                SetValue(EncryptProperty, value);
            }
        }

        /// <summary>
        /// 水印
        /// </summary>
        public string PlaceHolder
        {
            get { return (string)GetValue(PlaceHolderProperty); }
            set
            {
                SetValue(PlaceHolderProperty, value);
            }
        }

        /// <summary>
        /// 记住切换明文和密文之前,上一次插入符所在的位置
        /// </summary>
        int lastSelectionStart;

        public static DependencyProperty PasswordProperty = DependencyProperty.Register("Password", typeof(string), typeof(PasswordTextBox), new PropertyMetadata(""));
        public static DependencyProperty PasswordCharProperty = DependencyProperty.Register("PasswordChar", typeof(string), typeof(PasswordTextBox), new PropertyMetadata("*"));
        public static DependencyProperty EncryptProperty = DependencyProperty.Register("Encrypt", typeof(bool), typeof(PasswordTextBox), new PropertyMetadata(true, Changed));
        public static DependencyProperty PlaceHolderProperty = DependencyProperty.Register("PlaceHolder", typeof(string), typeof(PasswordTextBox), new PropertyMetadata(""));

        /// <summary>
        /// 当Encrypt改变时记住插入符在的位置
        /// 在Encrypt改变后,肯定会触发下一次textchanged,因为text的内容改变了
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        private static void Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            PasswordTextBox box = d as PasswordTextBox;
            box.lastSelectionStart = box.SelectionStart;
            box.TextChanged += Box_TextChanged;
        }

        /// <summary>
        /// 在文字改变之后,将插入符位置复位
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void Box_TextChanged(object sender, TextChangedEventArgs e)
        {
            PasswordTextBox box = sender as PasswordTextBox;
            box.SelectionStart = box.lastSelectionStart;
            box.TextChanged -= Box_TextChanged;
        }

        public PasswordTextBox()
        {
            MultiBinding mutiBuilding = new MultiBinding();
            mutiBuilding.Converter = new PasswordConverter();
            mutiBuilding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
            mutiBuilding.Bindings.Add(new Binding() { Path = new PropertyPath("Password"), Source = this });
            mutiBuilding.Bindings.Add(new Binding() { Path = new PropertyPath("PasswordChar"), Source = this });
            mutiBuilding.Bindings.Add(new Binding() { Path = new PropertyPath("Encrypt"), Source = this });
            BindingOperations.SetBinding(this, TextProperty, mutiBuilding);
        }

三、添加Converter

Converter主要应用于将输入的文字转化为密文****

public class PasswordConverter : IMultiValueConverter
    {
        string realPassword = "";
        string realPasswordChar = "*";
        bool encryptPawssword = true;

        /// <summary>
        /// 由返回值可以看出这是将绑定的值转化为最终显示的Text
        /// </summary>
        /// <param name="value">value的值顺序就和绑定的顺序一样</param>
        /// <param name="targetType"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns></returns>
        object IMultiValueConverter.Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
        {
            StringBuilder sb = new StringBuilder();
            if (value != null && value.Length >= 1)
            {
                string sValue = value[0].ToString();
                bool encrypt = (bool)value.Where(v => v.GetType() == typeof(bool)).FirstOrDefault();
                encryptPawssword = encrypt;

                realPassword = sValue;

                if (value.Length > 1 && encrypt)
                {
                    char passChar = value[1].ToString()[0];
                    realPasswordChar = passChar.ToString();
                    if (!string.IsNullOrEmpty(sValue))
                    {
                        for (int i = 0; i < sValue.Length; i++)
                        {
                            sb.Append(passChar);
                        }
                    }

                    return sb.ToString();
                }
                else
                {
                    return sValue;
                }
            }

            return null;
        }

        /// <summary>
        /// 将Text的值传进来,转化为绑定数值
        /// 这里需要注意,如果PasswordChar为*,那value的值就是*****x
        /// 假设已经输入了五位数,第六个数量入了g,那么value就是*****g
        /// 所以在此类需要保存真正的密码
        /// 举个例子 前五个你输入了12345,第六个输入了g
        /// 那么value的值就是*****g,全局realPassword值为12345
        /// 此时需要将g交给realPassword
        /// 如果你是在12后面插入的g,那valu就是**g***
        /// 所以需要将**g***和realPassword的位置做对比,将g插在第三位
        /// </summary>
        /// <param name="value"></param>
        /// <param name="targetTypes"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns></returns>
        object[] IMultiValueConverter.ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            string backValue = "";
            if (value != null)
            {
                string strValue = value.ToString();

                if (encryptPawssword)
                {
                    char replaceChar = realPasswordChar[0];
                    //这里需要注意一下,原作者这里用的是realPassword索引backValue+=realPassword[index]
                    //如果这么做的话,正常输入密码没问题,但是在密码中间插入密码,就会出现数组越界
                    IEnumerator<char> realChars = realPassword.GetEnumerator();
                    for (int index = 0; index < strValue.Length; ++index)
                    {
                        if (strValue[index] == replaceChar)
                        {
                            if (realChars.MoveNext()) backValue += realChars.Current;
                        }
                        else
                        {
                            backValue += strValue[index];
                        }
                    }

                    strValue = backValue;
                }

                return new object[] { strValue, realPasswordChar, encryptPawssword };
            }

            return null;
        }
    }

四、在页面中添加控件

    <Grid>
        <local:PasswordTextBox PlaceHolder="请输入密码" Style="{StaticResource PasswordTextBox}" VerticalAlignment="Top" Width="200"/>
    </Grid>

总结

此控件主要运用了MultiValueConverter,将输入的文字转为密文。 其中添加了依赖属性Password用于保存真正的密码。TextBox自带的Text只能用来看看了,因为在密文的时候,Text的内容就是****。 参考链接: [link](https://blog.csdn.net/SANYUNI/article/details/52775898).
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值