WPF 自定义密码框,可绑定,可控制密码显示隐藏

Converter
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows;

namespace WpfApp1
{
    /// <summary>
    /// 
    /// </summary>
    public class BooleanToVisibilityConverter : IValueConverter
    {
        public bool IsInversed { get; set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <param name="targetType"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns></returns>
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is bool realValue)
            {
                if (IsInversed)
                    realValue = !realValue;

                return realValue ? Visibility.Visible : Visibility.Collapsed;
            }

            return Visibility.Collapsed;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

控件代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace WpfApp1
{
    /// <summary>
    /// 通用密码框
    /// </summary>
    [TemplatePart(Name = PART_PasswordBox, Type = typeof(PasswordBox))]
    [TemplatePart(Name = PART_TextBox, Type = typeof(TextBox))]
    public class CommonPasswordBoxWithToggle : UserControl
    {
        #region 常量
        public const string PART_PasswordBox = "PART_PasswordBox";
        public const string PART_TextBox = "PART_TextBox";
        #endregion

        #region 属性
        /// <summary>
        /// 密码框
        /// </summary>
        private PasswordBox PARTPasswordBox { get; set; }
        /// <summary>
        /// 文本框
        /// </summary>
        private TextBox PARTTextBox { get; set; }
        #endregion

        #region 依赖属性

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

        // Using a DependencyProperty as the backing store for Password.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PasswordProperty =
            DependencyProperty.Register("Password", typeof(string), typeof(CommonPasswordBoxWithToggle), new PropertyMetadata(string.Empty));

        /// <summary>
        /// 是否显示密码
        /// </summary>
        public bool IsPasswordVisible
        {
            get { return (bool)GetValue(IsPasswordVisibleProperty); }
            set { SetValue(IsPasswordVisibleProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsPasswordVisible.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsPasswordVisibleProperty =
            DependencyProperty.Register("IsPasswordVisible", typeof(bool), typeof(CommonPasswordBoxWithToggle), new PropertyMetadata(false));

        /// <summary>
        /// 左侧图标按钮内容
        /// </summary>
        /// </summary>
        public string LeftIconButtonContent
        {
            get { return (string)GetValue(LeftIconButtonContentProperty); }
            set { SetValue(LeftIconButtonContentProperty, value); }
        }

        // Using a DependencyProperty as the backing store for LeftIconButtonContent.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty LeftIconButtonContentProperty =
            DependencyProperty.Register("LeftIconButtonContent", typeof(string), typeof(CommonPasswordBoxWithToggle), new PropertyMetadata(string.Empty));

        /// <summary>
        /// 左侧图标按钮显示
        /// </summary>
        /// </summary>
        public Visibility LeftIconButtonVisibility
        {
            get { return (Visibility)GetValue(LeftIconButtonVisibilityProperty); }
            set { SetValue(LeftIconButtonVisibilityProperty, value); }
        }

        // Using a DependencyProperty as the backing store for LeftIconButtonVisibility.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty LeftIconButtonVisibilityProperty =
            DependencyProperty.Register("LeftIconButtonVisibility", typeof(Visibility), typeof(CommonPasswordBoxWithToggle), new PropertyMetadata(Visibility.Collapsed));


        public string WaterText
        {
            get { return (string)GetValue(WaterTextProperty); }
            set { SetValue(WaterTextProperty, value); }
        }

        // Using a DependencyProperty as the backing store for WaterText.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty WaterTextProperty =
            DependencyProperty.Register("WaterText", typeof(string), typeof(CommonPasswordBoxWithToggle), new PropertyMetadata(string.Empty));

        /// <summary>
        /// 隐藏密码图标
        /// </summary>
        public string HidePasswordIcon
        {
            get { return (string)GetValue(HidePasswordIconProperty); }
            set { SetValue(HidePasswordIconProperty, value); }
        }

        // Using a DependencyProperty as the backing store for HidePasswordIcon.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty HidePasswordIconProperty =
            DependencyProperty.Register("HidePasswordIcon", typeof(string), typeof(CommonPasswordBoxWithToggle), new PropertyMetadata(string.Empty));

        /// <summary>
        /// 显示密码图标
        /// </summary>
        public string ShowPasswordIcon
        {
            get { return (string)GetValue(ShowPasswordIconProperty); }
            set { SetValue(ShowPasswordIconProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ShowPasswordIcon.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ShowPasswordIconProperty =
            DependencyProperty.Register("ShowPasswordIcon", typeof(string), typeof(CommonPasswordBoxWithToggle), new PropertyMetadata(string.Empty));


        #endregion
        #region 构造
        static CommonPasswordBoxWithToggle()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(CommonPasswordBoxWithToggle), new FrameworkPropertyMetadata(typeof(CommonPasswordBoxWithToggle)));
        }
        #endregion

        #region 方法
        /// <summary>
        /// 应用模板
        /// </summary>
        override public void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            try
            {
                PARTPasswordBox = Template.FindName(PART_PasswordBox, this) as PasswordBox;
                PARTPasswordBox.PasswordChanged -= PARTPasswordBox_PasswordChanged;
                PARTPasswordBox.PasswordChanged += PARTPasswordBox_PasswordChanged;

                PARTTextBox = Template.FindName(PART_TextBox, this) as TextBox;
                PARTTextBox.TextChanged -= PARTTextBox_TextChanged;
                PARTTextBox.TextChanged += PARTTextBox_TextChanged;
            }
            catch (Exception ex)
            {

            }
        }

        private void PARTTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (IsPasswordVisible)
            {
                PARTPasswordBox.Password = PARTTextBox.Text;
            }
            Password = PARTTextBox.Text;
        }

        private void PARTPasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
        {
            Password = PARTPasswordBox.Password;
        }

        #endregion
    }
}

样式

<local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<local:BooleanToVisibilityConverter x:Key="InversedBooleanToVisibilityConverter" IsInversed="True"/>
<!--锁图标-->
<sys:String x:Key="LockIcon">M11.2668 6.4V4.26667C11.2668 1.93333 9.33343 0 7.0001 0C4.66676 0 2.73343 1.93333 2.73343 4.26667V6.4H0.600098V16H13.4001V6.4H11.2668ZM3.8001 4.26667C3.8001 2.46667 5.2001 1.06667 7.0001 1.06667C8.8001 1.06667 10.2001 2.46667 10.2001 4.26667V6.4H3.8001V4.26667ZM12.3334 7.46667V11.7333H1.66676V7.46667H12.3334ZM1.66676 14.9333V12.8H12.3334V14.9333H1.66676Z</sys:String>
<!--隐藏密码图标-->
<sys:String x:Key="HidePasswordIcon">M15.1657 6.0505C15.3195 5.82104 15.2666 5.5159 15.0648 5.33685C14.8354 5.15782 14.5302 5.20825 14.3512 5.43772C14.326 5.46293 11.4966 8.8017 8.13263 8.8017C4.86953 8.8017 1.91407 5.43772 1.88885 5.4125C1.70982 5.20823 1.37947 5.18303 1.17522 5.36207C0.97095 5.54112 0.945752 5.87145 1.12479 6.07572C1.17522 6.15137 1.78799 6.8398 2.75634 7.60388L1.45513 8.95552C1.25087 9.15978 1.27606 9.49012 1.48033 9.66917C1.53077 9.77003 1.65938 9.82298 1.78547 9.82298C1.91407 9.82298 2.04017 9.77255 2.14355 9.66917L3.54562 8.21665C4.20883 8.6756 4.99813 9.10933 5.8656 9.41447L5.331 11.2251C5.25535 11.505 5.40665 11.7849 5.68909 11.8631H5.84291C6.07237 11.8631 6.27664 11.7092 6.32707 11.4798L6.86169 9.66917C7.27021 9.74482 7.70394 9.79777 8.13515 9.79777C8.56889 9.79777 9.00262 9.74734 9.40862 9.66917L9.94322 11.4545C9.99365 11.684 10.2231 11.8378 10.4274 11.8378C10.4778 11.8378 10.5283 11.8378 10.556 11.8126C10.8359 11.737 10.9897 11.4545 10.9141 11.1746L10.3719 9.38925C11.2394 9.08412 12.0287 8.65038 12.6919 8.19143L14.0687 9.61873C14.1696 9.7196 14.2982 9.77255 14.4268 9.77255C14.5554 9.77255 14.6815 9.72212 14.7849 9.61873C14.9892 9.41447 14.9892 9.10933 14.8101 8.90508L13.5114 7.55345C14.5529 6.78937 15.1657 6.05048 15.1657 6.05048V6.0505Z</sys:String>
<!--显示密码图标-->
<sys:String x:Key="ShowPasswordIcon">M8.01681 3.91217C11.2069 3.91217 13.9167 6.06444 15.2487 7.34645C15.5671 7.65338 15.7501 8.09132 15.7501 8.54611C15.7501 9.00089 15.5671 9.43883 15.2487 9.74577C13.9167 11.0278 11.2069 13.18 8.01681 13.18C4.82494 13.18 2.11168 11.024 0.779687 9.74015C0.461204 9.43509 0.279968 8.99902 0.279968 8.54611C0.279968 8.09319 0.462964 7.65713 0.779687 7.35206C2.11168 6.06819 4.82318 3.91217 8.01681 3.91217ZM8.01681 11.9448C10.818 11.9448 13.2621 9.99468 14.4692 8.83058C14.573 8.73139 14.5888 8.60974 14.5888 8.54611C14.5888 8.48247 14.573 8.36083 14.4692 8.26163C13.2603 7.09941 10.8163 5.14739 8.01681 5.14739C5.21205 5.14739 2.768 7.10128 1.55918 8.26725C1.45712 8.36457 1.44129 8.48435 1.44129 8.54611C1.44129 8.60787 1.45712 8.72578 1.55918 8.82497C2.768 9.99094 5.21381 11.9448 8.01681 11.9448ZM5.95087 8.54655C5.95087 7.40748 6.87743 6.48248 8.01493 6.48248C9.15399 6.48248 10.079 7.40592 10.079 8.54498C10.079 9.68405 9.15243 10.609 8.01493 10.609C6.87743 10.609 5.95087 9.68561 5.95087 8.54655ZM6.98368 8.54655C6.98368 9.1153 7.44618 9.5778 8.01493 9.5778C8.58368 9.5778 9.04618 9.1153 9.04618 8.54655C9.04618 7.9778 8.58368 7.5153 8.01493 7.5153C7.44618 7.5153 6.98368 7.9778 6.98368 8.54655Z</sys:String>

<!--CommonPasswordBoxWithToggleStyle-->
<Style x:Key="CommonPasswordBoxWithToggleStyle"
       TargetType="{x:Type local:CommonPasswordBoxWithToggle}">
    <Setter Property="ShowPasswordIcon" Value="{DynamicResource ShowPasswordIcon}"/>
    <Setter Property="HidePasswordIcon" Value="{DynamicResource HidePasswordIcon}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:CommonPasswordBoxWithToggle}">
                <Grid>
                    <Border x:Name="PART_Border"
                            Background="White" 
                            BorderBrush="#FFABADB3"
                            BorderThickness="1">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="12"/>
                                <ColumnDefinition Width="auto"/>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="auto"/>
                            </Grid.ColumnDefinitions>
                            <!--左侧图标-->
                            <Path Grid.Column="1" 
                                  x:Name="PathUC"
                                  Height="16"
                                  Width="16"
                                  HorizontalAlignment="Center"
                                  VerticalAlignment="Center"
                                  Data="{Binding LeftIconButtonContent, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                  Fill="{TemplateBinding Foreground}"
                                  Stretch="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=IconStretch}" 
                                  Margin="0 0 6 0"/>

                            <!--水印-->
                            <Border Grid.Column="2"
                                    Name="PART_WaterMark"
                                    Panel.ZIndex="100"
                                    IsHitTestVisible="False"
                                    BorderBrush="{TemplateBinding BorderBrush}"
                                    Opacity="0">
                                <Border.Background>
                                    <VisualBrush AlignmentX="Left"
                                                 Stretch="None">
                                        <VisualBrush.Visual>
                                            <TextBlock Text="{TemplateBinding WaterText}" 
                                                       FontSize="14"
                                                       Foreground="{DynamicResource FontSecondaryBodyColor}"/>
                                        </VisualBrush.Visual>
                                    </VisualBrush>
                                </Border.Background>
                            </Border>
                            <!-- 密文模式 -->
                            <PasswordBox Grid.Column="2" 
                                         x:Name="PART_PasswordBox"
                                         Visibility="{Binding IsPasswordVisible,RelativeSource={RelativeSource TemplatedParent},Converter={StaticResource InversedBooleanToVisibilityConverter}}"
                                         PasswordChar=""
                                         VerticalAlignment="Center"
                                         VerticalContentAlignment="Center"
                                         BorderThickness="0"
                                         FontSize="12"/>
                            <!-- 明文模式 -->
                            <TextBox Grid.Column="2" 
                                     x:Name="PART_TextBox" 
                                     Visibility="{Binding IsPasswordVisible, RelativeSource={RelativeSource TemplatedParent},Converter={StaticResource BooleanToVisibilityConverter}}"
                                     Text="{Binding Password, RelativeSource={RelativeSource TemplatedParent},UpdateSourceTrigger=PropertyChanged}"
                                     VerticalAlignment="Center"
                                     VerticalContentAlignment="Center"
                                     BorderThickness="0"
                                     FontSize="12"/>
                            <!-- 切换按钮 -->
                            <ToggleButton Grid.Column="3"
                                          Margin="5,0"
                                          IsChecked="{Binding IsPasswordVisible,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
                                          Width="16"
                                          Height="16">
                                <ToggleButton.Style>
                                    <Style TargetType="ToggleButton">
                                        <Setter Property="Template">
                                            <Setter.Value>
                                                <ControlTemplate TargetType="ToggleButton">
                                                    <Border Background="Transparent">
                                                        <Path x:Name="EyeIcon"
                                                              Data="{Binding HidePasswordIcon,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:CommonPasswordBoxWithToggle}}"
                                                              Fill="#666"
                                                              Stretch="Uniform"/>
                                                    </Border>
                                                    <ControlTemplate.Triggers>
                                                        <Trigger Property="IsChecked" 
                                                         Value="True">
                                                            <Setter TargetName="EyeIcon" 
                                                                    Property="Data"
                                                                    Value="{Binding ShowPasswordIcon,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:CommonPasswordBoxWithToggle}}"/>
                                                        </Trigger>
                                                    </ControlTemplate.Triggers>
                                                </ControlTemplate>
                                            </Setter.Value>
                                        </Setter>
                                    </Style>
                                </ToggleButton.Style>
                            </ToggleButton>
                        </Grid>
                    </Border>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="Password" Value="">
                        <Setter TargetName="PART_WaterMark" Property="Opacity" Value="1"/>
                    </Trigger>
                    <Trigger Property="Password" Value="{x:Null}">
                        <Setter TargetName="PART_WaterMark" Property="Opacity" Value="1"/>
                    </Trigger>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="PART_Border" Property="BorderBrush"
                        Value="LightBlue"/>
                    </Trigger>
                    <Trigger Property="IsFocused" Value="True">
                        <Setter TargetName="PART_Border" Property="BorderBrush"
                        Value="LightBlue"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

使用

<local:CommonPasswordBoxWithToggle Style="{DynamicResource CommonPasswordBoxWithToggleStyle}"
                                   WaterText="请输入密码"
                                   Password="{Binding YourPassword,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
                                   LeftIconButtonVisibility="Visible"
                                   LeftIconButtonContent="{DynamicResource LockIcon}"
                                   Width="300"
                                   Height="34"/>

效果

 

posted @ 2025-03-06 13:59  苏秦与真相  阅读(201)  评论(0)    收藏  举报