WPF Watermark PasswordBox from Watermark TextBox
Asked Answered
C

5

6

I am using a Watermark textbox as in Watermark TextBox in WPF

 <Grid Grid.Row="0" Background="{StaticResource brushWatermarkBackground}" Style="{StaticResource EntryFieldStyle}" >
        <TextBlock Margin="5,2" Text="This prompt dissappears as you type..." Foreground="{StaticResource brushWatermarkForeground}"
                   Visibility="{Binding ElementName=txtUserEntry, Path=Text.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}}" />
        <TextBox Name="txtUserEntry" Background="Transparent" BorderBrush="{StaticResource brushWatermarkBorder}" />
    </Grid>

How can I apply this for a PasswordBox?

Corliss answered 22/10, 2009 at 13:8 Comment(0)
G
21

The general approach is the same: write a custom control style, and show the watermark whenever the password box is empty. The only problem here is that PasswordBox.Password is not a dependency property, and you can't use it in a trigger. Also PasswordBox is sealed, so you can't override this notification behavior, but you can use attached properties here.

The following code demonstrates how.

XAML:

<Window x:Class="WpfTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:WpfTest="clr-namespace:WpfTest"
    Title="Password Box Sample" Height="300" Width="300">
  <Window.Resources>
    <Style x:Key="{x:Type PasswordBox}"
        TargetType="{x:Type PasswordBox}">
      <Setter Property="WpfTest:PasswordBoxMonitor.IsMonitoring"
              Value="True"/>
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type PasswordBox}">
            <Border Name="Bd"
                    Background="{TemplateBinding Background}"
                    BorderThickness="{TemplateBinding BorderThickness}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    SnapsToDevicePixels="true">
              <Grid>
                <ScrollViewer x:Name="PART_ContentHost"
                              SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                <TextBlock Text="Please enter your password" 
                           Margin="4, 2, 0, 0"
                           Foreground="Gray" 
                           Visibility="Collapsed"
                           Name="txtPrompt" />
              </Grid>
            </Border>
            <ControlTemplate.Triggers>
              <Trigger Property="IsEnabled"
                                         Value="false">
                <Setter TargetName="Bd"
                                            Property="Background"
                                            Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                <Setter Property="Foreground"
                                            Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
              </Trigger>
              <Trigger Property="WpfTest:PasswordBoxMonitor.PasswordLength" Value="0">
                <Setter Property="Visibility" TargetName="txtPrompt" Value="Visible"/>
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </Window.Resources>
  <Grid>
    <PasswordBox VerticalAlignment="Top"/>
  </Grid>
</Window>

C#:

using System.Windows;
using System.Windows.Controls;

namespace WpfTest {
    public partial class Window1 : Window {
        public Window1() {
            InitializeComponent();
        }
    }

  public class PasswordBoxMonitor : 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(PasswordBoxMonitor), 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(PasswordBoxMonitor), 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);
    }
  }
}

Please notice PasswordBoxMonitor in XAML code.

Guipure answered 23/10, 2009 at 0:16 Comment(3)
Hi Although this post is quite old, but can you tell me please how to achieve this in windows phone 8? I have tried the above procedure but it seems that <ControlTemplate.Triggers> </ControlTemplate.Triggers> is not in there in Windows phone 8. How could I achieve if I want to build a windows phone 8 apps with textbox and Password box with watermark, please help.Milquetoast
Fast and simple solution. But it seems that it does not support the focus template.Intrepid
What if you want to make another one that says "Re-enter Password" can you do that without having to re-write the entire style?Tag
O
11

you can show/hide the background by yourself instead of using triggers:

XAML:

<PasswordBox x:Name="passwordBox" PasswordChanged="passwordChanged" 
        Background="{StaticResource PasswordHint}" />

Code behind:

// helper to hide watermark hint in password field
private void passwordChanged(object sender, RoutedEventArgs e)
{           
    if (passwordBox.Password.Length == 0)
        passwordBox.Background.Opacity = 1;
    else
        passwordBox.Background.Opacity = 0;
}
Overreact answered 17/3, 2011 at 18:11 Comment(3)
This is the simplest solution I can find, although it took some time for me to understand the Background property here. It would have been much better if there was a declaration for "PasswordHint" in the answer. However, Works very well... +1Priapitis
I've edited this answer to include a reference to the StaticResource called PasswordHint. Worked for me, and simple.Sid
As mentioned in the comments, you are missing explanation for PasswordHint thus making your answer incompleteMaxfield
E
4

you can use my approach for a watermark behavior. all you have to do is copy and paste the TextBoxWatermarkBehavior and the change the Behavior<TextBox> to Behavior<PasswordBox>.

you can find a demo project here

Extemporary answered 16/7, 2010 at 13:37 Comment(3)
It works very well. I thinks this should be the answer..ThanksDichogamy
id guess so too, but do you have an english explanation as well? google translate isnt exactly the most acurate translatorComines
@blindmeis, Thanks. Your suggestion worked for me. I have modified the TextBoxWatermarkBehavior class as you suggested and the final PasswordBoxWatermarkBehavior class, I have given it as a seperate answerAnnexation
A
0

@blindmeis's suggestion is good. For PasswordBox the class would be as follows.

public class PasswordBoxWatermarkBehavior : System.Windows.Interactivity.Behavior<PasswordBox>
{
    private TextBlockAdorner adorner;
    private WeakPropertyChangeNotifier notifier;

    #region DependencyProperty's

    public static readonly DependencyProperty LabelProperty =
        DependencyProperty.RegisterAttached("Label", typeof(string), typeof(PasswordBoxWatermarkBehavior));

    public string Label
    {
        get { return (string)GetValue(LabelProperty); }
        set { SetValue(LabelProperty, value); }
    }

    public static readonly DependencyProperty LabelStyleProperty =
        DependencyProperty.RegisterAttached("LabelStyle", typeof(Style), typeof(PasswordBoxWatermarkBehavior));

    public Style LabelStyle
    {
        get { return (Style)GetValue(LabelStyleProperty); }
        set { SetValue(LabelStyleProperty, value); }
    }

    #endregion

    protected override void OnAttached()
    {
        base.OnAttached();
        this.AssociatedObject.Loaded += this.AssociatedObjectLoaded;
        this.AssociatedObject.PasswordChanged += AssociatedObjectPasswordChanged;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        this.AssociatedObject.Loaded -= this.AssociatedObjectLoaded;
        this.AssociatedObject.PasswordChanged -= this.AssociatedObjectPasswordChanged;

        this.notifier = null;
    }

    private void AssociatedObjectPasswordChanged(object sender, RoutedEventArgs e)
    {
        this.UpdateAdorner();
    }

    private void AssociatedObjectLoaded(object sender, System.Windows.RoutedEventArgs e)
    {
        this.adorner = new TextBlockAdorner(this.AssociatedObject, this.Label, this.LabelStyle);

        this.UpdateAdorner();

        //AddValueChanged for IsFocused in a weak manner
        this.notifier = new WeakPropertyChangeNotifier(this.AssociatedObject, UIElement.IsFocusedProperty);
        this.notifier.ValueChanged += new EventHandler(this.UpdateAdorner);
    }

    private void UpdateAdorner(object sender, EventArgs e)
    {
        this.UpdateAdorner();
    }


    private void UpdateAdorner()
    {
        if (!String.IsNullOrEmpty(this.AssociatedObject.Password) || this.AssociatedObject.IsFocused)
        {
            // Hide the Watermark Label if the adorner layer is visible
            this.AssociatedObject.TryRemoveAdorners<TextBlockAdorner>();
        }
        else
        {
            // Show the Watermark Label if the adorner layer is visible
            this.AssociatedObject.TryAddAdorner<TextBlockAdorner>(adorner);
        }
    }
}
Annexation answered 22/7, 2016 at 4:19 Comment(0)
D
0

Werner Wichtig 's approach is simple and good, but not applicable on Background property with some text. I have used it with Style property.

XAML code for PasswordBox > Style resource:

<Window.Resources>
    <Style x:Key="PasswordStyle" TargetType="PasswordBox" >
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="PasswordBox">
                    <Border MinWidth="{TemplateBinding MinWidth}">
                        <Grid>
                            <StackPanel Margin="8">
                                <ScrollViewer x:Name="PART_ContentHost"/>
                            </StackPanel>
                            <StackPanel>
                                <TextBlock Name="PART_TempText" Text="Enter Password" Foreground="#807C7C"
    Visibility="Collapsed" TextAlignment="Center" HorizontalAlignment="Center" Padding="8" />
                            </StackPanel>
                        </Grid>
                    </Border>
                    <ControlTemplate.Triggers>
                        <DataTrigger Binding="{Binding Password.Length, RelativeSource={RelativeSource Self}}" Value="0">
                            <Setter TargetName="PART_TempText" Property="Visibility" Value="Visible" />
                        </DataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

Your targeted PasswordBox code:

<PasswordBox x:Name="txtPasswordBox" PasswordChanged="txtPasswordBox_PasswordChanged" 
    Style="{StaticResource PasswordStyle}" />

Code-behind logic implemented on PasswordChanged event of PasswordBox:

private void txtPasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
    if (txtPasswordBox.Password.Length == 0)
        txtPasswordBox.Style = Resources["PasswordStyle"] as Style;
    else
        txtPasswordBox.Style = null;
}

Here is final result:

enter image description here

Declinometer answered 6/8, 2024 at 14:50 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.