WPF Basic Controls: PasswordBox Style

WPF Basic Controls: PasswordBox Style

Basic Controls

Last updated 5/5/2022 6:49 AM
驚鏵 WPF开发者
4 min read
Category
WPF
Tags
.NET WPF

Other Basic Controls

  1. Window
  2. Button
  3. CheckBox
  4. ComboBox
  5. DataGrid
  6. DatePicker
  7. Expander
  8. GroupBox
  9. ListBox
  10. ListView
  11. Menu

PasswordBox achieves the effect below.

01 — Code as follows

  1. PasswordBox does not support watermark, so we need to use DependencyObject attached property to implement watermark.
public class ElementHelper : DependencyObject
{
    public static string GetWatermark(DependencyObject obj)
    {
        return (string)obj.GetValue(WatermarkProperty);
    }

    public static void SetWatermark(DependencyObject obj, string value)
    {
        obj.SetValue(WatermarkProperty, value);
    }

    public static readonly DependencyProperty WatermarkProperty =
        DependencyProperty.RegisterAttached("Watermark", typeof(string), typeof(ElementHelper), new PropertyMetadata("Please input"));

}
  1. Import namespace xmlns:wpfs="clr-namespace:WPFDevelopers.Minimal.Helpers".
<TextBlock
  x:Name="PART_TextBlockWatermark"
  Text="{Binding Path=(wpfs:ElementHelper.Watermark),RelativeSource={RelativeSource TemplatedParent}}"
  Foreground="{StaticResource RegularTextSolidColorBrush}"
  VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
  FontSize="{StaticResource NormalFontSize}"
  Margin="7.5,0"
  Visibility="Collapsed"
/>
  1. After setting the watermark, the next step is to determine when the password box content is empty to show the watermark. Add attached class PasswordBoxHelper.
public class PasswordBoxHelper : DependencyObject
{
    public static bool GetIsMonitoring(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsMonitoringProperty);
    }

    public static void SetIsMonitoring(DependencyObject obj, bool value)
    {
        obj.SetValue(IsMonitoringProperty, value);
    }

    public static readonly DependencyProperty IsMonitoringProperty =
        DependencyProperty.RegisterAttached("IsMonitoring", typeof(bool), typeof(PasswordBoxHelper), new UIPropertyMetadata(false, OnIsMonitoringChanged));



    public static int GetPasswordLength(DependencyObject obj)
    {
        return (int)obj.GetValue(PasswordLengthProperty);
    }

    public static void SetPasswordLength(DependencyObject obj, int value)
    {
        obj.SetValue(PasswordLengthProperty, value);
    }

    public static readonly DependencyProperty PasswordLengthProperty =
        DependencyProperty.RegisterAttached("PasswordLength", typeof(int), typeof(PasswordBoxHelper), new UIPropertyMetadata(0));

    private static void OnIsMonitoringChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var pb = d as PasswordBox;
        if (pb == null)
        {
            return;
        }
        if ((bool)e.NewValue)
        {
            pb.PasswordChanged += PasswordChanged;
        }
        else
        {
            pb.PasswordChanged -= PasswordChanged;
        }
    }

    static void PasswordChanged(object sender, RoutedEventArgs e)
    {
        var pb = sender as PasswordBox;
        if (pb == null)
        {
            return;
        }
        SetPasswordLength(pb, pb.Password.Length);
    }
}
  1. In XAML, determine when the attached property PasswordLength equals 0 to show the watermark.
<Trigger Property="wpfs:PasswordBoxHelper.PasswordLength" Value="0">
  <Setter
    Property="Visibility"
    TargetName="PART_TextBlockWatermark"
    Value="Visible"
  />
</Trigger>
  1. Styles.PasswordBox.xaml code is as follows.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    xmlns:wpfs="clr-namespace:WPFDevelopers.Minimal.Helpers">

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="../Themes/Basic/ControlBasic.xaml"/>
        <ResourceDictionary Source="../Themes/Basic/Animations.xaml"/>
    </ResourceDictionary.MergedDictionaries>

    <Style TargetType="{x:Type PasswordBox}" BasedOn="{StaticResource ControlBasicStyle}">
        <Setter Property="KeyboardNavigation.TabNavigation" Value="None" />
        <Setter Property="FocusVisualStyle" Value="{x:Null}" />
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Setter Property="PasswordChar" Value="●" />
        <Setter Property="MinHeight" Value="{StaticResource MinHeight}" />
        <Setter Property="MinWidth" Value="180"/>
        <Setter Property="AllowDrop" Value="True" />
        <Setter Property="Cursor" Value="Hand"/>
        <Setter Property="Padding" Value="6,0"/>
        <Setter Property="wpfs:PasswordBoxHelper.IsMonitoring" Value="True"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type PasswordBox}">
                    <Border x:Name="PART_Border"
                            CornerRadius="{Binding Path=(wpfs:ElementHelper.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}"
                            BorderThickness="1"
                            Width="{TemplateBinding Width}"
                            Height="{TemplateBinding Height}">
                        <Border.Background>
                            <SolidColorBrush Color="{DynamicResource WhiteColor}" />
                        </Border.Background>
                        <Border.BorderBrush>
                            <SolidColorBrush Color="{DynamicResource BaseColor}" />
                        </Border.BorderBrush>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal" />
                                <VisualState x:Name="Disabled" />
                                <VisualState x:Name="MouseOver" >
                                    <Storyboard>
                                        <ColorAnimation Duration="00:00:0.3"
                                                        Storyboard.TargetName="PART_Border"
                                                        Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)"
                                                        To="{StaticResource PrimaryNormalColor}"/>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <!--<ScrollViewer x:Name="PART_ContentHost" />-->
                        <Grid Margin="{TemplateBinding Padding}">
                            <ScrollViewer x:Name="PART_ContentHost" />
                            <TextBlock x:Name="PART_TextBlockWatermark"
                                Text="{Binding Path=(wpfs:ElementHelper.Watermark),RelativeSource={RelativeSource TemplatedParent}}"
                                Foreground="{StaticResource RegularTextSolidColorBrush}"
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                FontSize="{StaticResource NormalFontSize}"
                                Margin="7.5,0" Visibility="Collapsed"/>
                        </Grid>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="wpfs:PasswordBoxHelper.PasswordLength" Value="0">
                            <Setter Property="Visibility" TargetName="PART_TextBlockWatermark" Value="Visible"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>
  1. Styles.PasswordBox.xaml usage code is as follows.
<WrapPanel Margin="0,10">
  <PasswordBox />
  <PasswordBox Margin="10,0" ws:ElementHelper.Watermark="Please enter password" />
  <PasswordBox IsEnabled="False" />
</WrapPanel>

02 — Effect Preview

Keep Exploring

Related Reading

More Articles
Same category / Same tag 9/13/2025

Migration Series from WPF to Avalonia: Why I Must Migrate My WPF Application to Avalonia

In the past few years, our host computer software has mainly been developed using WPF and WinForm . These technologies work well on the Windows platform and have accompanied us from small-scale trial production to the current stage of large-scale delivery. However, with business development and changes in customer requirements, the single Windows technology stack has gradually become a hurdle we must overcome.

Continue Reading
Same category / Same tag 1/26/2025

Implementing Internationalization in WPF Using Custom XML Files

This article details the method of implementing internationalization in WPF applications using custom XML files, including installing the necessary NuGet packages, dynamically retrieving the language list, dynamically switching languages, using translated strings in code and XAML interfaces, and provides a source code link to help developers easily achieve internationalization in WPF applications.

Continue Reading