I'm using a passwordbox and need to do some operation every time the password is changed from the UI. This passwordbox is part of a Page in WPF window, and when the page is changed/unloaded, the passwordchanged event seems to get fired with empty value for password. Is there any way for us to recognize that the source of this event is page unload and not the user changing the password in the UI?
Edit - Adding the sample code to reproduce the case. There is a MainWindow which contains a Frame element to render Pages. There are 2 sample pages Page1.xaml and Page2.xaml. Page1 has a passwordbox and textbox, when its values are changed the texts in the mainWindow changes and displays the new values. When the page is changed then also the password change event is triggered with "" value but the textchanged event is not triggered. Can I prevent pwdChanged event also from getting triggered here?
<Window x:Class="WpfPasswwordTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfPasswwordTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Name="PasswordTextLabel" HorizontalContentAlignment="Center"></Label>
<Label Grid.Row="1" Name="TextboxLabel" HorizontalContentAlignment="Center"></Label>
<Button Grid.Row="2" Name="PageChangeButton" Height="50" Width="200" Content="Change page"/>
<Frame Name="PageFrame" Grid.Row="3" NavigationUIVisibility="Hidden">
</Frame>
</Grid>
public partial class MainWindow : Window
{
public Label pwdLabel, txtboxLabel;
private bool page1 = true;
public MainWindow()
{
InitializeComponent();
pwdLabel = PasswordTextLabel;
txtboxLabel = TextboxLabel;
Class1.Instance.mainWin = this;
PasswordTextLabel.Content = "Pwd not changed yet";
TextboxLabel.Content = "Textbox text not changed yet";
PageFrame.Source = new Uri("Page1.xaml", UriKind.Relative);
PageChangeButton.Click += pageChange;
}
private void pageChange(object s, EventArgs e)
{
if(page1)
PageFrame.Source = new Uri("Page2.xaml", UriKind.Relative);
else
PageFrame.Source = new Uri("Page1.xaml", UriKind.Relative);
page1 = !page1;
}
}
<Page x:Class="WpfPasswwordTest.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfPasswwordTest"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="Page1">
<Grid Height="auto" Width="500">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label Grid.Row="0"> This is page 1</Label>
<Label Name="SampleLabel" Grid.Row="1">Password box - </Label>
<PasswordBox Name="SamplePwdBox" Grid.Row="2"></PasswordBox>
<Label Name="SampleTextLabel" Grid.Row="3">Text box -</Label>
<TextBox Name="SampleTxtBox" Grid.Row="4"></TextBox>
</Grid>
public partial class Page1 : Page
{
public Page1()
{
InitializeComponent();
SamplePwdBox.PasswordChanged += pwdChanged;
SampleTxtBox.TextChanged += txtChanged;
}
private void pwdChanged(object s,EventArgs e)
{
Class1.Instance.mainWin.pwdLabel.Content = "pwd Changed to " + (s as PasswordBox)?.Password;
}
private void txtChanged(object s, EventArgs e)
{
Class1.Instance.mainWin.txtboxLabel.Content = "text Changed to " + (s as TextBox)?.Text;
}
}
<Page x:Class="WpfPasswwordTest.Page2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfPasswwordTest"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="Page2">
<Grid Height="100" Width="500">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Grid.Row="0">This is page 2</Label>
<Label Name="SampleLabel" Grid.Row="1">Nothing to show here</Label>
</Grid>
The observed behavior is normal as the
PasswordBoxclears itself when being unloaded.Some thoughts:
PasswordBoxas it encourages weak security management. AlthoughSecureStringis safer than a plainstringit's far from being safe. The recommended solution is to use Windows authentication or a 3rd party OAuth API. For most scenarios, you don't have to care about authentication because the user is already authenticated by the OS when he logged in to his Windows account.PassworddBox.PasswordChangedevent is generally pointless because you don't want to trigger the valdation system to validate each character input. Instead, you are interested in the final value. For this reason, you should handle the password when the user clicks the e.g. "Login" button or when thePasswordBoxhas lost focus (what suits your UI flow better). The common pattern is to have the user explicitly trigger the authentication by clicking a corresponding button (signaling that he is done with the input). This will fix your issues.Suggested solution
IPasswordSource.cs
MainWindow.xaml
MainWindow.xaml.cs
Clear the password before navigating away. Alternatively block the navigation button until the user is authenticated.
Page1.xaml.cs
Page1.xaml