(UWP)如何使密码显示按钮始终可见,并使其始终在PasswordBox中工作

时间:2016-09-16 06:51:39

标签: xaml uwp passwordbox

我在我的UWP应用程序中使用PasswordBox,我想始终显示密码显示按钮。此外,按下时它应按预期工作,并应显示密码。

我修改了PasswordBox控件的默认样式,将Password Reveal按钮的可见性设置为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="&#xE052;"
                                  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" />

2 个答案:

答案 0 :(得分:3)

这是设计使然,您可以参考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;
}

这里的基本想法是首先获取RevealButton中的PasswordBox,然后更改ClickMode的{​​{1}},以便在RevealButton事件被触发时触发正在按Click,默认情况下,指针释放时会触发Button / Tapped RightTapped事件,最后您可以更改Button在这两个事件中。 Tapped事件在PC上运行良好,但在移动设备上,我使用PasswordRevealMode作为RightTapped发布的点。

答案 1 :(得分:0)

只需找到按钮并将其可见性设置为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事件附加到每个实例。

但是有一个缺点:失去焦点后,按钮不再具有切换密码可见性的能力,尽管可见。目前还不清楚为什么。