自定义WPF的PasswordBox控件,解决MVVM绑定问题

    最近在用MVVM框架做一个用户登录模块,涉及到密码框绑定的问题遇到阻碍。WPF中有现成的密码输入框控件PasswordBox,通过Password属性获取密码,但是Password属性不能进行数据绑定,原因是Password属性不是依赖属性。

    网上常见的解决思路是“将密码框的密码和某一个缓冲区进行同步, 缓冲区在和后台进行绑定. 其中密码框与缓冲区之间的同步可采用事件进行通知, 并将缓冲区打造成依赖属性, 然后缓冲区就支持绑定了, 并给后台提供正确的密码”,这个方法确实可以达到绑定的功能,但是有一个问题“在更改了密码框的密码后, 需要手动更新密码框插入符(CaretIndex)的位置”,虽然也有相对应的解决方案,但效果不好。本思路的解决方案请参考周银辉的[WPF]实现密码框的密码绑定》http://www.cnblogs.com/zhouyinhui/archive/2009/08/27/1554943.html

    另外一种思路是重写PasswordBox,在此参考,该作者已经用TextBox作为基础重写了类似PasswordBox的控件

Passwords in WPF: How to find yours and how to prevent it being found

http://www.codeproject.com/Articles/30976/Passwords-in-WPF-How-to-find-yours-and-how-to-prev

为了实现数据绑定,笔者将该控件作了相应修改,实现如下所示:

    /// <summary>
    /// More secure PasswordBox based on TextBox control
    /// </summary>
    public class SecurePasswordBox : TextBox
    {
        // Fake char to display in Visual Tree
        private char PWD_CHAR = '';

        /// <summary>
        /// Only copy of real password
        /// </summary>
        /// <remarks>For more security use System.Security.SecureString type instead</remarks>
        private string _password = string.Empty;

        /// <summary>
        /// TextChanged event handler for secure storing of password into Visual Tree,
        /// text is replaced with PWD_CHAR chars, clean text is keept into
        /// Text property (CLR property not snoopable without mod)   
        /// </summary>
        protected override void OnTextChanged(TextChangedEventArgs e)
        {
            if (dirtyBaseText == true)
                return;

            string currentText = this.BaseText;

            int selStart = this.SelectionStart;
            if (currentText.Length < _password.Length)
            {
                // Remove deleted chars          
                _password = _password.Remove(selStart, _password.Length - currentText.Length);
                SetRealText(this, _password);
            }
            if (!string.IsNullOrEmpty(currentText))
            {
                for (int i = 0; i < currentText.Length; i++)
                {
                    if (currentText[i] != PWD_CHAR)
                    {
                        // Replace or insert char
                        if (this.BaseText.Length == _password.Length)
                        {
                            _password = _password.Remove(i, 1).Insert(i, currentText[i].ToString());
} else { _password = _password.Insert(i, currentText[i].ToString());
} } }
   SetRealText(this, _password);
this.BaseText = new string(PWD_CHAR, _password.Length); this.SelectionStart = selStart; } base.OnTextChanged(e); } // flag used to bypass OnTextChanged private bool dirtyBaseText; /// <summary> /// Provide access to base.Text without call OnTextChanged /// </summary> private string BaseText { get { return base.Text; } set { dirtyBaseText = true; base.Text = value; dirtyBaseText = false; } } /// <summary> /// Clean Password /// </summary> public new string Text { get { return _password; } set { _password = value; this.BaseText = new string(PWD_CHAR, value.Length); } } //数据绑定用的附加属性RealText public static string GetRealText(DependencyObject obj) { return (string)obj.GetValue(RealTextProperty); } public static void SetRealText(DependencyObject obj, string value) { obj.SetValue(RealTextProperty, value); } // Using a DependencyProperty as the backing store for RealText. This enables animation, styling, binding, etc... public static readonly DependencyProperty RealTextProperty = DependencyProperty.RegisterAttached("RealText", typeof(string), typeof(SecurePasswordBox), new UIPropertyMetadata("")); }

数据绑定如下:【注意:附加属性的绑定默认是单向的】

<local:SecurePasswordBox  local:SecurePasswordBox.RealText="{Binding Password,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Text="{Binding Password,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"/>


 

 

 

posted @ 2013-07-02 17:39  jojinshallar  阅读(1179)  评论(0编辑  收藏  举报