WPF の PasswordBox は セキュリティ上の問題から直接データバインドすることができません。 …が、セキュリティ上の問題を承知したうえで、それでもデータバインドしたい、簡便さを優先したい場合があると思います。 今回は、セキュリティ上の問題を承知したうえで、データバインドを実現する方法をまとめます。
目次
サンプルコード
概要
依存関係プロパティを利用して、PasswordBox.Password を読み出す仕組みを作ります。 PasswordBox の PasswordChanged をフックし、PasswordBox.PasswordChanged が発生したとき、依存関係プロパティ Password を更新するようにします。
基本的な使い方(実装手順)は以下のようになります。
- PasswordBoxHelper.cs を 自プロジェクト へ コピペ
- XAML で 名前空間(WpfPasswordBox) を追加
- XAML で PasswordBoxHelper.Password に データバインド
PasswordBoxHelper クラス
PasswordProperty の生成は、DependencyProperty.Regist ではなく DependencyProperty.RegisterAttached を用いて生成します。 また、PasswordProperty を生成するとき、 FrameworkPropertyMetadataOptions.BindsTwoWayByDefault を指定しておくと、XAML側で余計な指定( Mode=TwoWay)がなくなるので便利です。
XAML では local:PasswordBoxHelper.Password にデータバインドを行いますが、初回のみ PasswordChanged をフックできるよう確認を行います。
PasswordBoxHelper.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | namespace WpfPasswordBox { using System.Windows; using System.Windows.Controls; public class PasswordBoxHelper : DependencyObject { public static readonly DependencyProperty IsAttachedProperty = DependencyProperty.RegisterAttached( "IsAttached" , typeof ( bool ), typeof (PasswordBoxHelper), new FrameworkPropertyMetadata( false , PasswordBoxHelper.IsAttachedProperty_Changed)); public static readonly DependencyProperty PasswordProperty = DependencyProperty.RegisterAttached( "Password" , typeof ( string ), typeof (PasswordBoxHelper), new FrameworkPropertyMetadata( string .Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, PasswordBoxHelper.PasswordProperty_Changed)); public static bool GetIsAttached(DependencyObject dp) { return ( bool )dp.GetValue(PasswordBoxHelper.IsAttachedProperty); } public static string GetPassword(DependencyObject dp) { return ( string )dp.GetValue(PasswordBoxHelper.PasswordProperty); } public static void SetIsAttached(DependencyObject dp, bool value) { dp.SetValue(PasswordBoxHelper.IsAttachedProperty, value); } public static void SetPassword(DependencyObject dp, string value) { dp.SetValue(PasswordBoxHelper.PasswordProperty, value); } private static void IsAttachedProperty_Changed(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var passwordBox = sender as PasswordBox; if (( bool )e.OldValue) { passwordBox.PasswordChanged -= PasswordBoxHelper.PasswordBox_PasswordChanged; } if (( bool )e.NewValue) { passwordBox.PasswordChanged += PasswordBoxHelper.PasswordBox_PasswordChanged; } } private static void PasswordBox_PasswordChanged( object sender, RoutedEventArgs e) { var passwordBox = sender as PasswordBox; PasswordBoxHelper.SetPassword(passwordBox, passwordBox.Password); } private static void PasswordProperty_Changed(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var passwordBox = sender as PasswordBox; var newPassword = ( string )e.NewValue; if (!GetIsAttached(passwordBox)) { SetIsAttached(passwordBox, true ); } if (( string .IsNullOrEmpty(passwordBox.Password) && string .IsNullOrEmpty(newPassword)) || passwordBox.Password == newPassword) { return ; } passwordBox.PasswordChanged -= PasswordBoxHelper.PasswordBox_PasswordChanged; passwordBox.Password = newPassword; passwordBox.PasswordChanged += PasswordBoxHelper.PasswordBox_PasswordChanged; } } } |
XAML記述(使い方)
ヘルパークラスを記述した名前空間を追加(xmlns:local)し、PasswordBoxタグ 内で local:PassowrdBoxHelper.Password に データバインド します。
MainView.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | < Window x:Class = "WpfPasswordBox.MainView" xmlns:local = "clr-namespace:WpfPasswordBox" xmlns:vm = "clr-namespace:WpfPasswordBox.ViewModel" Title = "MainWindow" Height = "350" Width = "525" > < Window.DataContext > < vm:MainViewModel /> </ Window.DataContext > < Grid > < PasswordBox HorizontalAlignment = "Center" VerticalAlignment = "Center" Width = "160" local:PasswordBoxHelper.Password = "{Binding Path=User.Password}" /> < Button Content = "Button" HorizontalAlignment = "Left" Margin = "264,173,0,0" VerticalAlignment = "Top" Width = "74" Command = "{Binding Path=ExecuteCommand}" /> </ Grid > </ Window > |
参考記事
- Functional Fun - WPF PasswordBox and Data binding
- WPF Tutrial - PasswordBox
- Stack Overflow - PasswordBox Binding
- Programmer's Ranch - C# WPF/MVVM: Why You Shouldn't Bind PasswordBox Password
- ++C++ - XAML の高度な機能(WPF)
- .NETな日々 - [WPF] WPF入門 ~依存関係プロパティ ②~
- MSDN - PasswordBox クラス
- MSDN - FrameworkPropertyMetadata クラス
最後に… このブログに興味を持っていただけた方は、 ぜひ 「Facebookページ に いいね!」または 「Twitter の フォロー」 お願いします!!