Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<PackageVersion Include="ShowMeTheXAML.AvalonEdit" Version="2.0.0" />
<PackageVersion Include="ShowMeTheXAML.MSBuild" Version="2.0.0" />
<PackageVersion Include="VirtualizingWrapPanel" Version="1.5.7" />
<PackageVersion Include="XAMLTest" Version="1.0.0-ci390" />
<PackageVersion Include="XAMLTest" Version="1.0.0-ci400" />
<PackageVersion Include="xunit" Version="2.4.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.4.5" />
<PackageVersion Include="Xunit.StaFact" Version="1.1.11" />
Expand Down
20 changes: 15 additions & 5 deletions MainDemo.Wpf/SmartHint.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,11 @@
</Grid>

<!-- Reveal style PasswordBox variants -->
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" Text="PasswordBox 'reveal' styles" Margin="0,40,0,0" />
<StackPanel Orientation="Horizontal" Margin="0,40,0,0">
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" Text="PasswordBox 'reveal' styles" />
<CheckBox x:Name="PasswordBoxesRevealedCheckBox" Content="IsPasswordRevealed" Margin="20,0,0,0" />
</StackPanel>

<Grid>
<Grid.Resources>
<Style TargetType="{x:Type PasswordBox}" BasedOn="{StaticResource MaterialDesignFloatingHintRevealPasswordBox}">
Expand All @@ -417,6 +421,7 @@
<Setter Property="materialDesign:TextFieldAssist.LeadingIcon" Value="{StaticResource LeadingIcon}" />
<Setter Property="materialDesign:HintAssist.IsFloating" Value="{Binding FloatHint}" />
<Setter Property="materialDesign:PasswordBoxAssist.Password" Value="{Binding RelativeSource={RelativeSource Self}, Path=Tag}" />
<Setter Property="materialDesign:PasswordBoxAssist.IsPasswordRevealed" Value="{Binding ElementName=PasswordBoxesRevealedCheckBox, Path=IsChecked}" />
<Setter Property="Padding">
<Setter.Value>
<MultiBinding Converter="{StaticResource CustomPaddingConverter}">
Expand Down Expand Up @@ -458,6 +463,7 @@
<Setter Property="materialDesign:TextFieldAssist.LeadingIcon" Value="{StaticResource LeadingIcon}" />
<Setter Property="materialDesign:HintAssist.IsFloating" Value="{Binding FloatHint}" />
<Setter Property="materialDesign:PasswordBoxAssist.Password" Value="{Binding RelativeSource={RelativeSource Self}, Path=Tag}" />
<Setter Property="materialDesign:PasswordBoxAssist.IsPasswordRevealed" Value="{Binding ElementName=PasswordBoxesRevealedCheckBox, Path=IsChecked}" />
<Setter Property="Padding">
<Setter.Value>
<MultiBinding Converter="{StaticResource CustomPaddingConverter}">
Expand Down Expand Up @@ -499,6 +505,7 @@
<Setter Property="materialDesign:TextFieldAssist.LeadingIcon" Value="{StaticResource LeadingIcon}" />
<Setter Property="materialDesign:HintAssist.IsFloating" Value="{Binding FloatHint}" />
<Setter Property="materialDesign:PasswordBoxAssist.Password" Value="{Binding RelativeSource={RelativeSource Self}, Path=Tag}" />
<Setter Property="materialDesign:PasswordBoxAssist.IsPasswordRevealed" Value="{Binding ElementName=PasswordBoxesRevealedCheckBox, Path=IsChecked}" />
<Setter Property="Padding">
<Setter.Value>
<MultiBinding Converter="{StaticResource CustomPaddingConverter}">
Expand Down Expand Up @@ -534,7 +541,10 @@
</Grid>

<!-- ComboBox variants -->
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" Text="ComboBox styles" Margin="0,40,0,0" />
<StackPanel Orientation="Horizontal" Margin="0,40,0,0">
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" Text="ComboBox styles" />
<CheckBox x:Name="ComboBoxesEditableCheckBox" Content="IsEditable" Margin="20,0,0,0" />
</StackPanel>
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource MaterialDesignFloatingHintComboBox}">
Expand All @@ -544,7 +554,7 @@
<Setter Property="materialDesign:HintAssist.IsFloating" Value="{Binding FloatHint}" />
<Setter Property="Text" Value="{Binding RelativeSource={RelativeSource Self}, Path=Tag}" />
<Setter Property="ItemsSource" Value="{Binding ComboBoxOptions}" />
<Setter Property="IsEditable" Value="True" />
<Setter Property="IsEditable" Value="{Binding ElementName=ComboBoxesEditableCheckBox, Path=IsChecked}" />
<Setter Property="Padding">
<Setter.Value>
<MultiBinding Converter="{StaticResource CustomPaddingConverter}">
Expand Down Expand Up @@ -587,7 +597,7 @@
<Setter Property="materialDesign:HintAssist.IsFloating" Value="{Binding FloatHint}" />
<Setter Property="Text" Value="{Binding RelativeSource={RelativeSource Self}, Path=Tag}" />
<Setter Property="ItemsSource" Value="{Binding ComboBoxOptions}" />
<Setter Property="IsEditable" Value="True" />
<Setter Property="IsEditable" Value="{Binding ElementName=ComboBoxesEditableCheckBox, Path=IsChecked}" />
<Setter Property="Padding">
<Setter.Value>
<MultiBinding Converter="{StaticResource CustomPaddingConverter}">
Expand Down Expand Up @@ -630,7 +640,7 @@
<Setter Property="materialDesign:HintAssist.IsFloating" Value="{Binding FloatHint}" />
<Setter Property="Text" Value="{Binding RelativeSource={RelativeSource Self}, Path=Tag}" />
<Setter Property="ItemsSource" Value="{Binding ComboBoxOptions}" />
<Setter Property="IsEditable" Value="True" />
<Setter Property="IsEditable" Value="{Binding ElementName=ComboBoxesEditableCheckBox, Path=IsChecked}" />
<Setter Property="Padding">
<Setter.Value>
<MultiBinding Converter="{StaticResource CustomPaddingConverter}">
Expand Down
37 changes: 37 additions & 0 deletions MaterialDesignThemes.UITests/WPF/PasswordBoxes/PasswordBoxTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -295,4 +295,41 @@ public async Task PasswordBox_WithHintAndValidationError_RespectsPadding(string

recorder.Success();
}

[Fact]
[Description("Issue 3095")]
public async Task PasswordBox_WithRevealedPassword_RespectsKeyboardTabNavigation()
{
await using var recorder = new TestRecorder(App);

var stackPanel = await LoadXaml<StackPanel>(@"
<StackPanel Orientation=""Vertical"">
<TextBox x:Name=""TextBox1"" Width=""200"" />
<PasswordBox x:Name=""PasswordBox"" Width=""200""
materialDesign:PasswordBoxAssist.IsPasswordRevealed=""True""
Style=""{StaticResource MaterialDesignFloatingHintRevealPasswordBox}"" />
<TextBox x:Name=""TextBox2"" Width=""200"" />
</StackPanel>");

var textBox1 = await stackPanel.GetElement<TextBox>("TextBox1");
var passwordBox = await stackPanel.GetElement<PasswordBox>("PasswordBox");
var revealPasswordTextBox = await passwordBox.GetElement<TextBox>("RevealPasswordTextBox");
var textBox2 = await stackPanel.GetElement<TextBox>("TextBox2");

// Assert Tab forward
await textBox1.MoveKeyboardFocus();
Assert.True(await textBox1.GetIsKeyboardFocused());
await textBox1.SendKeyboardInput($"{Key.Tab}");
Assert.True(await revealPasswordTextBox.GetIsKeyboardFocused());
await revealPasswordTextBox.SendKeyboardInput($"{Key.Tab}");
Assert.True(await textBox2.GetIsKeyboardFocused());

// Assert Tab backwards
await textBox2.SendKeyboardInput($"{ModifierKeys.Shift}{Key.Tab}");
Assert.True(await revealPasswordTextBox.GetIsKeyboardFocused());
await revealPasswordTextBox.SendKeyboardInput($"{Key.Tab}{ModifierKeys.None}");
Assert.True(await textBox1.GetIsKeyboardFocused());

recorder.Success();
}
}
24 changes: 24 additions & 0 deletions MaterialDesignThemes.Wpf/Behaviors/PasswordBoxBehavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,41 @@ internal class PasswordBoxBehavior : Behavior<PasswordBox>
{
private void PasswordBoxLoaded(object sender, RoutedEventArgs e) => PasswordBoxAssist.SetPassword(AssociatedObject, AssociatedObject.Password);

private void PasswordBoxPreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (PasswordBoxAssist.GetIsPasswordRevealed(AssociatedObject) &&
AssociatedObject.FindChild<TextBox>("RevealPasswordTextBox") is { } revealPasswordTextBox)
{
if (ReferenceEquals(e.OldFocus, revealPasswordTextBox) && ReferenceEquals(e.NewFocus, AssociatedObject))
{
// When password box receives keyboard focus, but it came from the nested reveal TextBox. We request focus transfer to the previous element from the password box's POV
TraversalRequest request = new TraversalRequest(FocusNavigationDirection.Previous);
AssociatedObject.MoveFocus(request);
e.Handled = true;
}
else if (!ReferenceEquals(e.OriginalSource, revealPasswordTextBox))
{
// When password box receives keyboard focus while the password is revealed, we transfer the focus to the nested reveal TextBox.
revealPasswordTextBox.Focus();
e.Handled = true;
}
}

}

protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += PasswordBoxLoaded;
AssociatedObject.PreviewGotKeyboardFocus += PasswordBoxPreviewGotKeyboardFocus;
}

protected override void OnDetaching()
{
if (AssociatedObject != null)
{
AssociatedObject.Loaded -= PasswordBoxLoaded;
AssociatedObject.PreviewGotKeyboardFocus -= PasswordBoxPreviewGotKeyboardFocus;
}
base.OnDetaching();
}
Expand Down