WPF PasswordBox样式&获取密码

Wpf的密码框PasswordBox有一个属性叫SecurePassword,它不是依赖属性,无法绑定。

出于安全考虑,你永远不要将明文密码保存在内存中,所以就不要想如何如何绑定到SecurePassword啦。

当然,非要绑定还是有办法的,添加两个附加属性就好了,方法你可以自行百度~这不是我关心的重点~

今天主要实现一个Password样式,和获取明文密码的功能。

效果图:

image

tip:输入1234后点击查看密码弹出明文密码

下面是代码过程~

1。要想实现水印功能,要监听密码属性,可是密码不是依赖属性无法根据它确定是否有密码。但是password自身有一个PasswordChanged事件,这个事件可以监听到密码改变。于是,添加一个附加监听属性和一个是否有密码属性,注册PasswordChanged事件,就可以确定是否有密码了~进而样式里面就可以绑定到是否有密码这个属性上了~

属性如下 :

添加一个PasswordAttachedProperty.cs,里面定义两个附加属性

namespace WpfTemplate
{
    public class MonitorPasswordProperty : BaseAttachedProperty<MonitorPasswordProperty, bool>
    {
        public override void OnValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
            var passwordBox = sender as PasswordBox;
            if (passwordBox == null) return;
            passwordBox.PasswordChanged -= PasswordBox_PasswordChanged;
            if ((bool)e.NewValue)
            {
                HasTextProperty.SetValue(passwordBox, passwordBox.SecurePassword.Length > 0);
                passwordBox.PasswordChanged += PasswordBox_PasswordChanged;
            }
        }

        private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
            HasTextProperty.SetValue((PasswordBox)sender);
        }
    }

    public class HasTextProperty : BaseAttachedProperty<HasTextProperty, bool>
    {
        public static void SetValue(DependencyObject sender)
{
            SetValue(sender, ((PasswordBox)sender).SecurePassword.Length > 0);
        }
    }
}

文中BaseAttachedProperty 类声明 可参见   

2。基于这两个附加属性,开始编写样式

<Style TargetType="{x:Type PasswordBox}" x:Key="password">

        <Setter Property="FontSize" Value="{StaticResource FontSizeLarge}" />
        <Setter Property="Padding" Value="10" />
        <Setter Property="Margin" Value="0 5 0 5" />
        <Setter Property="BorderBrush" Value="White" />
        <Setter Property="BorderThickness" Value="0 0 0 1" />
        <Setter Property="CaretBrush" Value="White"/>

        <Setter Property="local:MonitorPasswordProperty.Value" Value="True" />

        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="Foreground" Value="White"/>

        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type PasswordBox}">
                    <Grid>
                        <Border x:Name="border" 
                                BorderBrush="{TemplateBinding BorderBrush}" 
                                BorderThickness="{TemplateBinding BorderThickness}"
                                Background="{TemplateBinding Background}"
                                SnapsToDevicePixels="True">
                            <ScrollViewer x:Name="PART_ContentHost" Focusable="False" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
                        </Border>

                        <TextBlock IsHitTestVisible="False"
                                   Text="{TemplateBinding Tag}"
                                   x:Name="placeholder"
                                   FontFamily="{TemplateBinding FontFamily}"
                                   FontSize="{TemplateBinding FontSize}"
                                   Padding="{TemplateBinding Padding}"
                                   Visibility="{TemplateBinding local:HasTextProperty.Value, Converter={local:BooleanToVisiblityConverter}}"
                                   VerticalAlignment="Center"
                                   HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
                                   Foreground="Gray"
                                   >
                        </TextBlock>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter Property="Opacity" TargetName="border" Value="0.56"/>
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="BorderBrush" TargetName="border" Value="#FF7EB4EA"/>
                        </Trigger>
                        <Trigger Property="IsKeyboardFocused" Value="True">
                            <Setter Property="BorderBrush" TargetName="border" Value="#FF569DE5"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
</Style>

这样水印功能就开发完了~

3。获取 密码功能

passwordbox的没有Password属性,只有一个SecurePassword,它 是SecureString类型,要获取密码就要先获取这个东西。

在mvvm模式下,没有绑定,viewmodel如何得到这个属性呢,

    实现一个IHavePassword接口给MainWindow实现,里面有一个SecurePassword,具体如下 :

public interface IHavePassword
{
      SecureString SecurePassword { get; }
}

 将SecureString转成String

    给SecureString类添加一个扩展方法:

public static class SecureStringHelpers
    {
        public static string Unsecure(this SecureString securePassword)
        {
            if (securePassword == null) return string.Empty;
            var unmanagedString = IntPtr.Zero;
            try
            {
                unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(securePassword);
                return Marshal.PtrToStringUni(unmanagedString);
            }
            finally
            {
                Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
            }
        }
    }

这样的话就可以直接像pass.Unsecure()这样获取明文啦

mainwindow实现接口后代码如下 

public partial class MainWindow : Window,IHavePassword
    {
        public MainWindow()
        {
            InitializeComponent(); ;
            DataContext = new MainWindowViewModel(this);
        }
        public SecureString SecurePassword => PasswordText?.SecurePassword;
    }

2.MainWindowViewModel内容如下 

public class MainWindowViewModel:BaseViewModel
    {
        private Window mWindow;//window 对象
        public bool ButtonIsBusy { get; set; }
        public ICommand MinimizeCommand { get; set; }
        public ICommand MaximizeCommand { get; set; }
        public ICommand CloseCommand { get; set; }
        public ICommand BusyCommand { get; set; }
        //获取密码Command
        public ICommand GetPasswordCommand { get; set; }
        #region constructor
        public MainWindowViewModel(Window window)
        {
            mWindow = window;
            MinimizeCommand = new RelayCommand(() => mWindow.WindowState = WindowState.Minimized);
            MaximizeCommand = new RelayCommand(() => mWindow.WindowState ^= WindowState.Maximized);
            CloseCommand = new RelayCommand(() => mWindow.Close());
            BusyCommand = new RelayCommand(() => ButtonIsBusy = !ButtonIsBusy);
            //初始化command
            GetPasswordCommand = new RelayCommand(() => MessageBox.Show("输入的密码是 "+(mWindow as IHavePassword).SecurePassword.Unsecure()));
        }
        #endregion
    }

下面在MainWindow.xaml里面引用方法如下 :

<Grid Background="#1E1E1E">
        <WrapPanel  Margin="10">
            <PasswordBox Width="150"
                         Tag="请输入密码"
                         x:Name="PasswordText"
                         Style="{StaticResource password}"/>
            <Button Content="查看密码" Style="{StaticResource TextRectButton}" 
                    Foreground="White"
                    BorderBrush="White"
                    Command="{Binding GetPasswordCommand}"/>
        </WrapPanel>
    </Grid>

大功告成啦哈哈哈哈哈哈咯~

© 版权声明
THE END
喜欢就支持一下吧
点赞18 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情

    暂无评论内容