(UWP) Как сделать так, чтобы кнопка раскрытия пароля всегда была видимой и чтобы она всегда работала в PasswordBox
Я использую PasswordBox в своем приложении UWP, где я хочу всегда показывать кнопку раскрытия пароля. Кроме того, при нажатии он должен работать как положено и должен показывать пароль.
Я изменил этот стиль по умолчанию для элемента управления 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>
Обратите внимание, что я изменил стиль ниже для кнопки "Показать пароль", чтобы видимость была установлена в значение "истина" (по умолчанию она свернута):
<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 ответа
Это по замыслу, вы можете обратиться к PasswordBox:
Кнопка раскрытия пароля отображается только тогда, когда PasswordBox впервые получает фокус и вводится символ. Если PasswordBox теряет фокус, а затем восстанавливает фокус, кнопка открытия не отображается снова, пока пароль не будет удален и ввод символов начнется заново.
По умолчанию он ведет себя так, даже если вы делаете RevealButton
видна всегда, проблема сейчас заключается в следующем Button
не работает должным образом.
Официальный рекомендуемый метод заключается в создании аналогичного интерфейса, например, 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
из RevealButton
так будет Click
событие будет запущено, когда Button
по умолчанию Tapped
/ RightTapped
событие Button
будет запущен при отпускании указателя, наконец, вы можете изменить PasswordRevealMode
в этих двух событиях. Приложенное событие отлично работает на ПК, но на мобильном я использовал 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
событие для каждого экземпляра.
Однако есть один недостаток: после потери фокуса кнопка больше не может переключать видимость пароля, несмотря на то, что он виден. Непонятно почему.