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
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
<Window x:Class="WpfPasswordBox.MainView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 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 の フォロー」 お願いします!!