WPF中使用附加属性解决MVVM模式中PasswordBox无法绑定的问题

感谢UniApi对本博客的的大力赞助。 创作不易,如果您觉得有帮助,请 支持LIncol29! 为了让我能够继续创作更好的内容,你也可以选择订阅博客的 VIP ,包年VIP仅需10元/年,所有VIP内容免费观看

附加属性

附加属性,在XAML中是一种特殊的依赖属性,它允许给这个对象添加一个不属于它的值(另一个对象的值),从而改变另一个对象的值。它们通常用于扩展已有控件的功能,使其能够响应特定布局容器或服务提供的属性。简单来说,附加属性就像是“借用”其他类的属性来增强自身的功能。

比如 Grid.Row Grid.Column就属于附加属性。

如何自定义附加属性

PasswordBox的 Password不是依赖属性,不支持直接绑定,需要自定义附加属性。

PasswordBox的属性绑定步骤

  • 新建PasswordBoxHelper类 使用propa 快捷键,然后按下 tab键,VS会自动创建附加属性的定义模板
    创建MyPwd以及IsBinding两个附加属性。
  • MyPwd附加属性中,默认值为string.Empty 当值发生改变时触发回调函数OnPwdPropertyChanged
  • 在OnPwdPropertyChanged 函数中将MyPwd(改变的值)传给PasswordBox的Password前端控件,并且需要设置光标位置
  • IsBinding附加属性中,new PropertyMetadata(false, OnPropertyChanged),默认值为false 当为true时,触发OnPropertyChanged
  • OnPropertyChanged当为新值,给PasswordBox的PasswordChanged事件绑定方法OnPwdChanged
  • OnPwdChanged方法主要是调用SetPwd方法,设置Pwd附加属性值。将PasswordBox的Password前端控件 –> MyPwd

代码示例

PasswordBoxHelper类

using System.Reflection;
using System.Windows;
using System.Windows.Controls;

namespace MVVMDemo
{
    public class PasswordBoxHelper
    {
        //附加属性 MyPwd
        public static string GetMyPwd(DependencyObject obj)
        {
            return (string)obj.GetValue(MyPwdProperty);
        }

        public static void SetMyPwd(DependencyObject obj, string value)
        {
            obj.SetValue(MyPwdProperty, value);
        }

        // Using a DependencyProperty as the backing store for MyPwd.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MyPwdProperty =
            DependencyProperty.RegisterAttached("MyPwd", typeof(string), typeof(PasswordBoxHelper), new PropertyMetadata(string.Empty,OnPwdPropertyChanged));


        //属性是否改变  Pwd -> Passwordbox  
        private static void OnPwdPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            PasswordBox passwordBox = d as PasswordBox;
            if (passwordBox == null)
            {
                return;
            }
            passwordBox.Password = (string)e.NewValue;
            SetSelection(passwordBox, passwordBox.Password.Length,0 );
        }

        /// <summary>
        /// 设置光标位置
        /// </summary>
        /// <param name="passwordBox"></param>
        /// <param name="start">光标开始位置</param>
        /// <param name="length">选中长度</param>
        private static void SetSelection(PasswordBox passwordBox, int start, int length)
        {
            passwordBox.GetType()
                       .GetMethod("Select", BindingFlags.Instance | BindingFlags.NonPublic)
                       .Invoke(passwordBox, new object[] { start, length
         });
        }


        //附加属性 IsBinding
        public static bool GetIsBinding(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsBindingProperty);
        }

        public static void SetIsBinding(DependencyObject obj, bool value)
        {
            obj.SetValue(IsBindingProperty, value);
        }

        // Using a DependencyProperty as the backing store for IsBinding.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsBindingProperty =
            DependencyProperty.RegisterAttached("IsBinding", typeof(bool), typeof(PasswordBoxHelper), new PropertyMetadata(false,OnPropertyChanged));

        //passwordbox -> mypwd
        private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            PasswordBox passwordBox = d as PasswordBox;

            if (passwordBox == null)
                return;

            if ((bool)e.NewValue)
            {
                //SetMyPwd(passwordBox, passwordBox.Password);
                passwordBox.PasswordChanged += OnPwdChanged;
            }
            if ((bool)e.OldValue)
            {
                passwordBox.PasswordChanged -= OnPwdChanged;
            }
        }

        private static void OnPwdChanged(object sender, RoutedEventArgs e)
        {
            PasswordBox pwd = sender as PasswordBox;

            SetMyPwd(pwd, pwd.Password);
        }
    }
}

前端控件绑定

<PasswordBox x:Name="pwdbox" Grid.Row="1" Grid.Column="1"  FontSize="25" VerticalAlignment="Center" Width="200" HorizontalAlignment="Left" 
             local:PasswordBoxHelper.IsBinding="True"
             local:PasswordBoxHelper.MyPwd="{Binding Password,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
             />

<StackPanel Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Horizontal" Grid.ColumnSpan="2">
    <TextBlock Grid.Row="1" Grid.Column="0" Text="密码: " FontSize="25" VerticalAlignment="Center" HorizontalAlignment="Right"/>
    <TextBox  Text="{Binding ElementName=pwdbox,Path=(local:PasswordBoxHelper.MyPwd)}" FontSize="25" Width="200" HorizontalAlignment="Right"/>
</StackPanel>
//双向绑定附加属性

local:PasswordBoxHelper.IsBinding="True"
             local:PasswordBoxHelper.MyPwd="{Binding Password,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"

//绑定源元素为:pwdbox的值,Path指用来绑定的属性路径,(...)表示附加属性
//XAML 引擎看到 Path=(local:PasswordBoxHelper.MyPwd),就去找名为 pwdbox 的元素,读取它身上的 PasswordBoxHelper.GetMyPwd(pwdbox);– 把这个值赋给当前控件的 Text。  

Text="{Binding ElementName=pwdbox,Path=(local:PasswordBoxHelper.MyPwd)}" 

推荐文章

如果你对MVVM已经有个掌握,可以查看进阶文章:如何使用ReactiveUI框架快速实现MVVM模式。

创作不易,如果您觉得有帮助,请支持LIncol29!
如有需要,请至网站地图学习本博客的教程
博客订阅:通过RSS或关注公众号[Lincol的编程世界] | 广告招租与合作请留言
本文链接:https://www.lincol29.cn/passwordbox-ap
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0协议转载请注明文章地址及作者哦~
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇