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 の フォロー」 お願いします!!