はじめに
今回は WPF の PasswdBox のマスクされたパスワードの表示・非表示を MVVM で実装する方法を解説します。
設定を json ファイルで保存・読み込みを行うことを想定しているアプリケーションですが、今回は PasswdBox のみ焦点を当てます。
考え方
PasswdBox 単体では今回の実装が困難だったので、TextBox も併用します。具体的には以下の流れになります。
- PasswdBox と TextBox 重ねる
- TextBox は常に非表示にしておき、ボタンが押されたときに表示する(バインディングされた Password プロパティのテキストを表示)
- ボダンが押された時に、PasswordChar にNull 文字列の
\0
を代入しマスクを無効化する - ボタンが離された時に、PasswordChar に
●
を代入しマスクを有効化する
実際の動きは以下のようになります。
画面の作成
XAML を以下のように実装します。その他の TextBox は省略しています。
Nuget からMicrosoft.Xaml.Behaviors.Wpfをインストールしておいてください。ボタンをクリック・クリックを離したときのイベントを指定するために使用します。
<Window
x:Class="JsonConfigTester.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:bh="http://schemas.microsoft.com/xaml/behaviors"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:helper="clr-namespace:JsonConfigApp.Helper"
xmlns:local="clr-namespace:JsonConfigTester"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="設定画面"
MinWidth="350"
MinHeight="400"
MaxWidth="250"
MaxHeight="400"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<Grid>
<GroupBox
Margin="16"
BorderBrush="Black"
BorderThickness="1.3"
Header="設定">
<StackPanel Margin="8">
<StackPanel>
<!-- 省略 -->
<StackPanel Margin="0,16,0,0">
<StackPanel>
<Label
Padding="0,5,5,5"
Content="パスワード"
FontSize="16" />
</StackPanel>
<StackPanel>
<!-- 要素を横並びに重ねるためにGridを配置 -->
<Grid Margin="0,4,0,0">
<PasswordBox
x:Name="passwordBox"
Padding="5"
VerticalAlignment="Center"
helper:PasswordBoxHelper.IsAttached="True"
helper:PasswordBoxHelper.Password="{Binding Password, UpdateSourceTrigger=PropertyChanged}"
AutomationProperties.IsRequiredForForm="True"
FontSize="16"
PasswordChar="{Binding CharNullify}" />
<TextBlock
x:Name="showPassword"
Padding="5"
VerticalAlignment="Center"
FontSize="16"
Text="{Binding UnMaskedPassword}"
Visibility="{Binding ToggleUnMaskedTextBlock}" />
<Button
Width="30"
HorizontalAlignment="Right"
Content="表示"
Cursor="Hand">
<bh:Interaction.Triggers>
<bh:EventTrigger EventName="PreviewMouseLeftButtonDown">
<bh:InvokeCommandAction Command="{Binding ShowMaskedPasswordCommand}" />
</bh:EventTrigger>
<bh:EventTrigger EventName="PreviewMouseLeftButtonUp">
<bh:InvokeCommandAction Command="{Binding HiddenMaskedPasswordCommand}" />
</bh:EventTrigger>
</bh:Interaction.Triggers>
</Button>
</Grid>
</StackPanel>
</StackPanel>
</StackPanel>
<!-- 省略 -->
</StackPanel>
</GroupBox>
</Grid>
</Window>
まずは PasswordBox について見ていきます。
今回の趣旨とはそれるので詳細は省きますが、PasswordBox のPassword
プロパティのデータバインディングはサポートされていません。
そのためヘルパー関数を作成してデータバインディングを実現しています。
PasswordChar
ではマスク された文字列の表示・非表示をするためのバインディングしています。
次に、TextBox のVisibility
では、TextBox 自体の表示・非表示を制御するためのデータを ViewModel で保持します。
Button 要素では、Microsoft.Xaml.Behaviors.Wpf
を使用してマウスの左クリックを押した時・話したときに実行する処理を指定しています。
ViewModel の実装
ViewModel の実装は以下のようになります。
using JsonConfigApp.Command;
using JsonConfigApp.Helper;
using JsonConfigApp.Model;
using JsonConfigApp.Model.Repository;
using JsonConfigTester;
using System;
using System.Diagnostics;
using System.Text;
using System.Windows;
namespace JsonConfigApp.ViewModel
{
public sealed class MainWindowViewModel : BindableBase
{
// 省略
// PasswordBoxの文字列をデータバインディングするプロパティ
public string Password
{
get => _password;
set => SetProperty(ref _password, value);
}
private string _password;
// マスクされた文字列の表示・非表示を制御
public string UnMaskedPassword
{
get => _unMaskedPassword;
set => SetProperty(ref _unMaskedPassword, value);
}
private string _unMaskedPassword;
// Textboxの表示・非表示を制御
public Visibility ToggleUnMaskedTextBlock
{
get => _toggleUnMaskedTextBlock;
set => SetProperty(ref _toggleUnMaskedTextBlock, value);
}
// 初 期値は非表示に設定
private Visibility _toggleUnMaskedTextBlock = Visibility.Collapsed;
// マスクされた文字列の表示・非表示を制御
// デフォルトでは●を指定して文字列をマスキングする
public char CharNullify
{
get => _charNullify;
set => SetProperty(ref _charNullify, value);
}
private char _charNullify = '●';
// 左クリックが押されたときに実行されるコマンド
public DelegateCommand ShowMaskedPasswordCommand
=> _showMaskedPasswordCommand ?? (_showMaskedPasswordCommand = new DelegateCommand(OnShowMaskedPassword));
private DelegateCommand _showMaskedPasswordCommand;
// 左クリックが話されたときに実行されるコマンド
public DelegateCommand HiddenMaskedPasswordCommand
=> _hiddenMaskedPasswordCommand ?? (_hiddenMaskedPasswordCommand = new DelegateCommand(OnHiddenMaskedPassword));
private DelegateCommand _hiddenMaskedPasswordCommand;
// コンストラクタ内の処理は今回の実装には関係ないので省略
// 左クリックが押されたときの処理
private void OnShowMaskedPassword()
{
UnMaskedPassword = Password;
ToggleUnMaskedTextBlock = Visibility.Visible;
CharNullify = '\0'; // マスクを無効化
}
// 左クリックが話されたときの処理
private void OnHiddenMaskedPassword()
{
UnMaskedPassword = string.Empty;
ToggleUnMaskedTextBlock = Visibility.Collapsed;
CharNullify = '●'; // マスクを有効化
}
}
}
最後に
次回は設定を json ファイルに保存してアプリケーション実行時に読み込む処理まで見ていきたいと思います。