我在我的UWP应用程序中使用PasswordBox,我想在其中始终显示密码显示按钮。此外,按下它应该像预期的那样工作,应该显示密码。
我已经修改了PasswordBox控件的默认样式,将密码显示按钮的可见性设置为true。它的工作和按钮总是可见的现在。但问题是密码显示功能只有在清除密码并从头输入时才有效。
<Style x:Key="PasswordBoxStyle" TargetType="PasswordBox">
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="PasswordBox">
<Grid Background="Transparent">
<Grid.Resources>
<Style x:Name="RevealButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid x:Name="ButtonLayoutGrid" BorderBrush="{ThemeResource TextBoxButtonBorderThemeBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{ThemeResource TextBoxButtonBackgroundThemeBrush}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAccentBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonLayoutGrid"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAccentBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltChromeWhiteBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ButtonLayoutGrid"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<TextBlock x:Name="GlyphElement"
Foreground="{ThemeResource SystemControlForegroundChromeBlackMediumBrush}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontStyle="Normal"
FontSize="16"
Text=""
FontFamily="{ThemeResource SymbolThemeFontFamily}"
AutomationProperties.AccessibilityView="Raw"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HeaderContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseMediumLowBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledTransparentBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlBackgroundBaseLowBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseLowBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledChromeDisabledLowBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledChromeDisabledLowBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Normal">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="BorderElement">
<DiscreteObjectKeyFrame KeyTime="0" Value="Gray"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderThickness" Storyboard.TargetName="BorderElement">
<DiscreteObjectKeyFrame KeyTime="0" Value="0.5"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="BackgroundElement">
<DiscreteObjectKeyFrame KeyTime="0" Value="White"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="BorderElement">
<DiscreteObjectKeyFrame KeyTime="0" Value="Gray"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderThickness" Storyboard.TargetName="BorderElement">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="BackgroundElement">
<DiscreteObjectKeyFrame KeyTime="0" Value="White"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Focused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="BorderElement">
<DiscreteObjectKeyFrame KeyTime="0" Value="Gray"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderThickness" Storyboard.TargetName="BorderElement">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="BackgroundElement">
<DiscreteObjectKeyFrame KeyTime="0" Value="White"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="ButtonStates">
<VisualState x:Name="ButtonVisible">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RevealButton"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="ButtonCollapsed" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border x:Name="BackgroundElement"
Grid.Row="1"
Background="{TemplateBinding Background}"
Margin="{TemplateBinding BorderThickness}"
Opacity="{ThemeResource TextControlBackgroundRestOpacity}"
Grid.ColumnSpan="2"
Grid.RowSpan="1"/>
<Border x:Name="BorderElement"
Grid.Row="1"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Grid.ColumnSpan="2"
Grid.RowSpan="1"/>
<ContentPresenter x:Name="HeaderContentPresenter"
x:DeferLoadStrategy="Lazy"
Visibility="Collapsed"
Grid.Row="0"
Foreground="{ThemeResource SystemControlForegroundBaseHighBrush}"
Margin="0,0,0,8"
Grid.ColumnSpan="2"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
FontWeight="Normal" />
<ScrollViewer x:Name="ContentElement"
Grid.Row="1"
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
Margin="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
IsTabStop="False"
ZoomMode="Disabled"
AutomationProperties.AccessibilityView="Raw"/>
<ContentControl x:Name="PlaceholderTextContentPresenter"
Grid.Row="1"
Foreground="{ThemeResource SystemControlPageTextBaseMediumBrush}"
Margin="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
IsTabStop="False"
Grid.ColumnSpan="2"
Content="{TemplateBinding PlaceholderText}"
IsHitTestVisible="False"/>
<Button x:Name="RevealButton"
Grid.Row="1"
Style="{StaticResource RevealButtonStyle}"
BorderThickness="{TemplateBinding BorderThickness}"
Margin="{ThemeResource HelperButtonThemePadding}"
IsTabStop="False"
Grid.Column="1"
Visibility="Visible"
FontSize="{TemplateBinding FontSize}"
VerticalAlignment="Stretch"
MinWidth="34" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
请注意,我已经改变了下面的样式显示密码按钮有可见性设置为true(它是默认折叠):
<Button x:Name="RevealButton"
Grid.Row="1"
Style="{StaticResource RevealButtonStyle}"
BorderThickness="{TemplateBinding BorderThickness}"
Margin="{ThemeResource HelperButtonThemePadding}"
IsTabStop="False"
Grid.Column="1"
Visibility="Visible"
FontSize="{TemplateBinding FontSize}"
VerticalAlignment="Stretch"
MinWidth="34" />
这是设计的,您可以参考PasswordBox:
密码显示按钮仅在PasswordBox第一次收到焦点并输入字符时显示。如果PasswordBox失去焦点,然后恢复焦点,显示按钮不会再次显示,除非密码被清除,字符输入重新开始。
默认情况下,它的行为是这样的,即使你使RevealButton
始终可见,现在的问题是这个Button
不能正常工作。
官方推荐的方法是创建一个类似的UI,例如CheckBox
,让用户切换显示模式。我还注意到,你在样式中将默认的ToggleButton
更改为Button
,如果你坚持使用这个Button
来切换显示模式,你可以在后面编写代码,例如:
public Page21()
{
this.InitializeComponent();
this.Loaded += Page21_Loaded;
}
private Button RevealButton;
private void Page21_Loaded(object sender, RoutedEventArgs e)
{
RevealButton = FindChildOfType<Button>(passwordbox);
RevealButton.Tapped += RevealButton_Tapped;
RevealButton.ClickMode = ClickMode.Press;
RevealButton.Click += RevealButton_Click;
RevealButton.RightTapped += RevealButton_RightTapped;
}
private void RevealButton_RightTapped(object sender, RightTappedRoutedEventArgs e)
{
Debug.WriteLine("RevealButton_RightTapped");
passwordbox.PasswordRevealMode = PasswordRevealMode.Hidden;
}
private void RevealButton_Tapped(object sender, TappedRoutedEventArgs e)
{
Debug.WriteLine("RevealButton_Tapped");
passwordbox.PasswordRevealMode = PasswordRevealMode.Hidden;
}
private void RevealButton_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine("RevealButton_Click");
passwordbox.PasswordRevealMode = PasswordRevealMode.Visible;
}
public static T FindChildOfType<T>(DependencyObject root) where T : class
{
var queue = new Queue<DependencyObject>();
queue.Enqueue(root);
while (queue.Count > 0)
{
DependencyObject current = queue.Dequeue();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(current); i++)
{
var child = VisualTreeHelper.GetChild(current, i);
var typedChild = child as T;
if (typedChild != null)
{
return typedChild;
}
queue.Enqueue(child);
}
}
return null;
}
这里的基本思想是首先在PasswordBox
中获得RevealButton
,然后更改RevealButton
的ClickMode
,因此当Button
被按下时Click
事件将被触发,默认情况下Button
的Tapped
/RightTapped
事件将在指针被释放时被触发,最后您可以在这两个事件中更改PasswordRevealMode
。点击事件在PC上运行良好,但在手机上,我使用RightTapped
作为Button
发布的点。
您可以通过简单地找到按钮并将其可见性设置为Visibility.Visible
来使按钮始终可见。我假设默认模板中的按钮使用TemplateBinding
,并且它的可见性不是通过编程切换的,因为直接设置它的可见性会破坏任何现有的绑定,比如前面提到的。
我使用以下扩展方法来查找按钮:
public static T GetChildOfType<T>(this DependencyObject Object) where T : DependencyObject
{
if (Object == null)
return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(Object); i++)
{
var Child = VisualTreeHelper.GetChild(Object, i);
var Result = (Child as T) ?? GetChildOfType<T>(Child);
if (Result != null) return Result;
}
return null;
}
然后,当PasswordBox
加载时,执行如下操作:
private void OnLoaded(object sender, RoutedEventArgs e)
{
var RevealButton = (sender as PasswordBox).GetChildOfType<Button>();
if (RevealButton != null)
RevealButton.Visibility = Visibility.Visible;
}
如果你愿意,你可以定义一个通用的ContentControl
类来子类PasswordBox
;否则,只需将上述Loaded
事件附加到每个实例。
不过有一个缺点:失去焦点后,按钮不再具有切换密码可见性的能力,尽管它是可见的。原因尚不清楚