Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Smart WPF Login Overlay (Windows 8 Style)

0.00/5 (No votes)
3 Dec 2012 1  
Login Overlay for WPF applications with a styling similar to the Windows 8 Login Screen.

Contents

  1. News
  2. Introduction
  3. Background
  4. Requirements
  5. Using the Control
  6. List of all class members
  7. The code of the UserControl
  8. Points of Interest
  9. Recent Changes
  10. History

News

After the member IFFI has asked me if there shouldn't be an option for entering the user name, I decided to implement this functionality. I am also of the opinion that it makes more sense when the user has the ability to enter both : the user name and the password. Only in this way it is guaranteed that an user switch is easy as it should be and that the control can be used in various applications. However, I also leave the old functionality (without the option to enter the user name). All code changes can be found here. Furthermore i have added a second Demo to demonstrate this new functionality (see here).

Introduction

For everbody who is interested in a lightweight user validation control (for WPF applications) I have implemented a Smart Login Overlay WPF Control. The Login Overlay - especially the PasswordBox - has the same visual styles and behaviour like the Windows 8 Login Screen. I have made several Screenshots of the Windows 8 Login Screen and try to imitate it as good as possible.

Hereinafter are some screenshots of the control in action so you can see what I'm talking about.

Behaviour of the control

If you try to sign in with no credentials you will get this behaviour (the text is customizable) :

Smart Login Overlay NO credentials - Demo Window

If you try to sign in with wrong credentials you will get this behaviour (the text is customizable) :

Smart Login Overlay WRONG credentials - Demo Window

There is also a fully functional RevealButton implemented (like in the Windows 8 PasswordBox) :

Smart Login Overlay reveal button - Demo Window

Furthermore there is a hint when CapsLock is active (the text is customizable) :

Smart Login Overlay CapsLock active - Demo Window

Background

A few days earlier I have implemented and written an article about a Smart Password Box in my blog (Windows 8 PasswordBox Style for WPF). After this was made I decided to make it a little bit smarter and style it like the Windows 8 Login Screen.

I implement most of these components in my spare time. Therefore I ask you to please be considerate if anything should not be perfect. If you need some assistance please do not hesitate to ask me.

Requirements

To use the SmartLoginOverlay WPF Control, you require :

Using the Control

The usage of the control is really simple. Just add a reference to the "WPFSmartLibraryLight35.dll" in your project (RightClick on References and choose "Add Reference"). Your references should look like this:

Reference to the WPFSmartlibraryLight

After the reference was added you should declare an XAML Namespace which maps to the various WPFSmartLibrary CLR-Namespaces. Add the following XAML Namespace to your Window Tag:

<... xmlns:wpfsl="http://schemas.softarcs.com/wpfsmartlibrary" ...>

After you have done this it will be possible to use the control in your XAML code like this:

<wpfsl:SmartLoginOverlay FullSpan="On" Background="#FF16499A"
	               UserName="{Binding UserName}"
		       Password="{Binding Password}"
		       AccessPassword="{Binding UserPassword}"
		       UserImageSource="{Binding UserImageSource}"
		       AdditionalUserInfo="{Binding EMailAddress}"
		       AdditionalSystemInfo="Locked" />

Two things are very important and should be followed:

  1. The control should be a child of a Grid panel. If the parent of the control is not a Grid panel, the Span properties will not work properly (More information about the Span properties).
  2. To Guarantee that the control is the topmost element it must either be the last defined control in your XAML or the Panel.ZIndex property must be set to the highest value of all defined controls.

There are three possible approaches using the control:

  1. Full MVVM with auto password validation provided by the control
    • You can see an example of this approach in the XAML code above. The password validation will be performed automatically by the user control if the AccessPassword property is set.
  2. Full MVVM with custom password validation in the viewmodel using command binding (recommended)
    • The XAML Code for this approach would look like this (as you can see the AccessPassword property is not set) :
    • <wpfsl:SmartLoginOverlay x:Name="SmartLoginOverlayControl"
                       FullSpan="On" Background="#FF16499A"
                       UserName="{Binding UserName}"
                       Password="{Binding Password}"
                       UserImageSource="{Binding UserImageSource}"
                       AdditionalUserInfo="{Binding EMailAddress}"
                       Command="{Binding SubmitCommand}"
                       CommandParameter="{Binding RelativeSource={RelativeSource Self} }" />
      		

      Here is a sample implementation of the Command property and the corresponding command methods

      // ---------------------------------------------------------------------------------
      // Put this in the constructor of your ViewModel class
      this.SubmitCommand = new ActionCommand( this.ExecuteSubmit, this.CanExecuteSubmit );
      // ---------------------------------------------------------------------------------
      
      public ICommand SubmitCommand { get; private set; }
      
      private void ExecuteSubmit(object commandParameter)
      {
        Debug.WriteLine( "Here you would implement the submission and a following validation of this data:\n" +
      		this.UserName + "\n" + this.EMailAddress + "\n" + this.Password );
      
        var accessControlSystem = commandParameter as SmartLoginOverlay;
      
        if (accessControlSystem != null)
        {
          if (this.Password.Equals( this.UserPassword ))
          {
            accessControlSystem.Unlock();
          }
          else
          {
            accessControlSystem.ShowWrongCredentialsMessage();
          }
        }
      }
      
      private bool CanExecuteSubmit(object commandParameter)
      {
        return !string.IsNullOrEmpty( this.Password );
      }		
      		
  3. Assign fixed values to the properties and let the control do the validation or validate the password in code behind (for everybody who is not familiar with MVVM or don't wanna use it)
    • The XAML Code for this approach would look like this :
    • <wpfsl:SmartLoginOverlay x:Name="SmartLoginOverlayControl"
                       FullSpan="On" Background="#FF16499A"
                       UserName="PSY" AccessPassword="gangnamstyle"
                       UserImageSource="E:\WPFSmartLibraryLight\LoginOverlayDemo\Images\PSY.png"
                       AdditionalUserInfo="psy@youtuberecord.com"
                       AdditionalSystemInfo="Locked" />
      	

      And if you want to handle the password validation on your own in code behind just leave the AccessPassword unset and assign an event handler to the SubmitRequested event.

All three approaches are shown in the demo application.

List of all class members

SmartLoginOverlay Class

Namespace           : SoftArcs.WPFSmartLibrary.UserControls
Assembly              : WPFSmartLibraryLight35.dll
XMLNS for XAML : http://schemas.softarcs.com/wpfsmartlibrary

The SmartLoginOverlay type exposes the following members :

Constructors

Name Description
SmartLoginOverlay Initializes a new instance of the SmartLoginOverlay class.

Properties

Name Description Property Type
AccessPassword Gets or sets the password which will be accepted by the intern validation process. This is a dependency property (BindsTwoWayByDefault). String
AdditionalSystemInfo Gets or sets the additional system information which will be displayed under the AdditionalUserInfo with a smaller font size and a opacity of 0.6. This is a dependency property. String
AdditionalUserInfo Gets or sets the additional user information which will be displayed under the UserName with a smaller font size. This is a dependency property. String
CapsLockInfo Gets or sets the hint which will be displayed when CapsLock is active. This is a dependency property. The default value is "Caps Lock is active". String
Command Gets or sets the command to invoke when the submit button is clicked or the enter key is pressed. This is a dependency property. (Inherited from AdvancedUserControl) ICommand
CommandParameter Gets or sets the parameter to pass to the Command property. This is a dependency property. (Inherited from AdvancedUserControl) ICommand
DisappearAnimation Gets or sets the type of the disappear animation. This is a dependency property. The default value is MoveAndFadeOutToRight. DisappearAnimationType
FullColumnSpan Turn a full column span of the UserControl On or Off. This is a dependency property. The default value is Off. This applies only when the parent element is a Grid panel. (Inherited from AdvancedUserControl) SwitchState
FullRowSpan Turn a full row span of the UserControl On or Off. This is a dependency property. The default value is Off. This applies only when the parent element is a Grid panel. (Inherited from AdvancedUserControl) SwitchState
FullSpan Turn a full row and column span of the UserControl On or Off. This is a dependency property. The default value is Off. This applies only when the parent element is a Grid panel. (Inherited from AdvancedUserControl) SwitchState
IsUserOptionAvailable Gets or sets the option for entering the user name. This is a dependency property. Boolean
NoCredentialsInfo Gets or sets the hint which will be displayed when trying to sign in with NO credentials. This is a dependency property. The default value is "Enter your credentials and try again.". String
Password Gets or sets the Password which is inputted by the user. This is a dependency property (BindsTwoWayByDefault). (Because it is a dependency property it is bindable contrary to the not bindable Password property of the standard PasswordBox) String
SubmitButtonTooltip Gets or sets the tooltip of the submit button. This is a dependency property. The default value is "Submit". String
UserImageSource Gets or sets the URI to the image which will be displayed for the recent user. This is a dependency property. String
UserName Gets or sets the name of the user which will be displayed as the recent user. This is a dependency property (BindsTwoWayByDefault and DefaultUpdateSourceTrigger=PropertyChanged). String
Watermark Gets or sets the Watermark in the PasswordBox which is shown when the PasswordBox is empty. This is a dependency property. The default value is "Enter password". String
WrongCredentialsInfo Gets or sets the hint which will be displayed when trying to sign in with WRONG credentials. This is a dependency property. The default value is "The password is incorrect. Make sure that you use the password for your account. You can reset the password at any time under 'myaccount.credentialserver.com/reset'.". String

Methods

Name Description
Lock Reset all animations, assimilate the background if neccessary and set the focus to the PasswordBox Control.
ShowNoCredentialsMessage Shows the NoCredentialsInfo and set the focus to the OK button.
ShowWrongCredentialsMessage Shows the WrongCredentialsInfo and set the focus to the OK button.
Unlock Perform the defined (or the default) DisappearAnimation and a FadeOut animation to make the overlay disappear.

Events

Name Description
SubmitRequested Occurs when the sumbit button is clicked or the enter key is pressed while the control has the focus.
The DisappearAnimationType enumeration
public enum DisappearAnimationType
{
	FadeOut,
	MoveAndFadeOutToRight,
	MoveAndFadeOutToTop,
	MoveAndFadeOutToRightSimultaneous,
	MoveAndFadeOutToTopSimultaneous
}
The AdvancedUserControl base class

The AdvancedUserControl exposes some interesting properties which are useful when creating new UserControls.

First off all it has the Span Properties : FullSpan, FullRowSpan, FullColumnSpan. With this properties it is posssible to span the UserControl over all rows resp. columns of the parent Grid. Now you will say : “Ok, bu I can do this with the AttachedProperties Grid.Row resp. Grid.Column !” Yes that’s true but when you add columns or rows to your parent Grid you have always to adjust the AttachedProperties to fit all columns resp. rows. With the Span Properties of the AdvancedUserControl you have the advantage that it will always span over all columns resp. rows. You have not to take care about it when you add more columns resp. rows.

The Span Properties themselves use also an interesting enumeration : The SwitchState. The SwitchState enumeration is something similar to the boolean type but in my opinion it fits better in some use cases. The SwitchState enumeration contains only two values "On and Off". In some cases I was not really satisfied with the build-in boolean "true / false" - type. I think that in some cases it is more properly to use a SwitchState than a boolean type.

Other properties exposed by the AdvancedUserControl are the Command property and the corresponding CommandParameter property. If you are using MVVM you will always try to use commanding instead of code behind event handler. So if you are creating a new UserControl you can connect the most important event to the Command property so that the user of your UserControl is able to use it in his ViewModel.

The SwitchState enumeration
public enum SwitchState
{
	On,
	Off
}

The code of the UserControl

Now let's get to the code. First of all here is the XAML code of the UserControl (see recent changes here) :

<ft:AdvancedUserControl x:Class="SoftArcs.WPFSmartLibrary.SmartUserControls.SmartLoginOverlay"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:sys="clr-namespace:System;assembly=mscorlib"
                xmlns:ap="clr-namespace:SoftArcs.WPFSmartLibrary.UIClassAttachedProperties"
                xmlns:ft="clr-namespace:SoftArcs.WPFSmartLibrary.FoundationTypes"
                mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="350"
                x:Name="VisualRoot" Loaded="SmartLoginOverlay_Loaded">

  <ft:AdvancedUserControl.RenderTransform>
    <TranslateTransform x:Name="VisualRootTranslateTransform" />
  </ft:AdvancedUserControl.RenderTransform>

  <ft:AdvancedUserControl.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="..\ResourceDictionaries\CommonRD\TextAndFontsRD.xaml" />
        <ResourceDictionary Source="..\ResourceDictionaries\SmartStyles\SmartPasswordBoxesRD.xaml" />
      </ResourceDictionary.MergedDictionaries>

      <Style TargetType="{x:Type Label}" BasedOn="{StaticResource {x:Type Label}}">
        <Setter Property="HorizontalAlignment" Value="Left" />
        <Setter Property="VerticalAlignment" Value="Top" />
        <Setter Property="Foreground" Value="White" />
        <Setter Property="FontFamily" Value="Segoe UI Light" />
        <Setter Property="FontSize" Value="{StaticResource StandardFontSize}" />
        <!--<Setter Property="FontFamily" Value="Segoe WP SemiLight" />-->
      </Style>

      <Style TargetType="{x:Type PasswordBox}" BasedOn="{StaticResource Win8ExtendedPasswordBoxStyle}">
        <Setter Property="HorizontalAlignment" Value="Left" />
        <Setter Property="VerticalAlignment" Value="Top" />
        <Setter Property="Height" Value="25" />
        <Setter Property="Width" Value="160" />
        <Setter Property="FontSize" Value="{StaticResource MediumFontSize}" />
      </Style>

      <KeyTime x:Key="FadeOutDurationSimultaneousKeyTime">0:0:0.5</KeyTime>
      <KeyTime x:Key="FadeOutDurationKeyTime">0:0:0.3</KeyTime>
      <KeyTime x:Key="FadeInDurationKeyTime">0:0:0.1</KeyTime>

      <Storyboard x:Key="MoveOutToTopStoryboard">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="LayoutRootTranslateTransform"
                             Storyboard.TargetProperty="Y">
          <SplineDoubleKeyFrame KeyTime="0" Value="0"/>
          <SplineDoubleKeyFrame KeyTime="{StaticResource FadeOutDurationKeyTime}" Value="-300.0"/>
        </DoubleAnimationUsingKeyFrames>
      </Storyboard>

      <Storyboard x:Key="MoveOutToRightStoryboard">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="LayoutRootTranslateTransform"
                             Storyboard.TargetProperty="X">
          <SplineDoubleKeyFrame KeyTime="0" Value="0"/>
          <SplineDoubleKeyFrame KeyTime="{StaticResource FadeOutDurationKeyTime}" Value="300.0"/>
        </DoubleAnimationUsingKeyFrames>
      </Storyboard>

      <Storyboard x:Key="FadeOutStoryboard">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot"
                             Storyboard.TargetProperty="Opacity">
          <SplineDoubleKeyFrame KeyTime="0" Value="1"/>
          <SplineDoubleKeyFrame KeyTime="{StaticResource FadeOutDurationKeyTime}" Value="0"/>
        </DoubleAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot"
                             Storyboard.TargetProperty="Visibility">
          <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
          <DiscreteObjectKeyFrame KeyTime="{StaticResource FadeOutDurationKeyTime}"
                          Value="{x:Static Visibility.Hidden}"/>
        </ObjectAnimationUsingKeyFrames>
      </Storyboard>

      <Storyboard x:Key="MoveOutToTopSimultaneousStoryboard">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="VisualRootTranslateTransform"
                             Storyboard.TargetProperty="Y">
          <SplineDoubleKeyFrame KeyTime="0" Value="0"/>
          <SplineDoubleKeyFrame KeyTime="{StaticResource FadeOutDurationSimultaneousKeyTime}"
                         Value="-300.0"/>
        </DoubleAnimationUsingKeyFrames>
      </Storyboard>

      <Storyboard x:Key="MoveOutToRightSimultaneousStoryboard">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="VisualRootTranslateTransform"
                             Storyboard.TargetProperty="X">
          <SplineDoubleKeyFrame KeyTime="0" Value="0"/>
          <SplineDoubleKeyFrame KeyTime="{StaticResource FadeOutDurationSimultaneousKeyTime}"
                         Value="300.0"/>
        </DoubleAnimationUsingKeyFrames>
      </Storyboard>

      <Storyboard x:Key="FadeOutSimultaneousStoryboard">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="VisualRoot"
                             Storyboard.TargetProperty="Opacity">
          <SplineDoubleKeyFrame KeyTime="0" Value="1"/>
          <SplineDoubleKeyFrame KeyTime="{StaticResource FadeOutDurationSimultaneousKeyTime}"
                         Value="0"/>
        </DoubleAnimationUsingKeyFrames>
        <ColorAnimationUsingKeyFrames Storyboard.TargetName="VisualRoot"
                            Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)">
          <DiscreteColorKeyFrame KeyTime="0" Value="Transparent" />
        </ColorAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="VisualRoot"
                             Storyboard.TargetProperty="Visibility">
          <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
          <DiscreteObjectKeyFrame KeyTime="{StaticResource FadeOutDurationSimultaneousKeyTime}"
                          Value="{x:Static Visibility.Hidden}"/>
        </ObjectAnimationUsingKeyFrames>
      </Storyboard>
    </ResourceDictionary>
  </ft:AdvancedUserControl.Resources>

  <Grid x:Name="LayoutRoot">
    <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
      <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" MinHeight="170" />
      </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
      </Grid.ColumnDefinitions>
      <Grid.RenderTransform>
        <TranslateTransform x:Name="LayoutRootTranslateTransform" />
      </Grid.RenderTransform>

      <Border Grid.RowSpan="3" BorderThickness="1" VerticalAlignment="Top" Margin="0,20,0,0">
        <Image x:Name="imgUser" Source="../CommonImages/UserSilhouette.png" MaxHeight="150" />
      </Border>

      <StackPanel Grid.Row="0" Grid.Column="1" Margin="0,20,0,0">
        <Label x:Name="lblUserName" Margin="12,0" Padding="0" FontSize="22"
             Content="{Binding ElementName=VisualRoot, Path=UserName, Mode=TwoWay}" />

        <Label x:Name="lblAdditionalUserInfo" Margin="12,0" Padding="0" FontSize="12"
             Content="{Binding ElementName=VisualRoot, Path=AdditionalUserInfo, Mode=TwoWay}" />

        <Label x:Name="lblAdditionalSystemInfo" Margin="12,1" Padding="0" FontSize="12" Opacity="0.6"
             Content="{Binding ElementName=VisualRoot, Path=AdditionalSystemInfo, Mode=TwoWay}" />
      </StackPanel>

      <StackPanel Grid.Row="1" Grid.Column="1">
        <PasswordBox x:Name="PasswordBoxControl" Margin="12,18,12,5" Width="200"
                  KeyDown="PasswordBoxControl_OnKeyDown"
                  GotFocus="PasswordBoxControl_OnGotFocus"
                  LostFocus="PasswordBoxControl_OnLostFocus"
                  ap:PasswordBoxBinding.Password="{Binding ElementName=VisualRoot, Path=Password,
                                              Mode=TwoWay}"/>

        <Label x:Name="lblCapsLockInfo" Margin="12,1" Padding="0" FontSize="10" Foreground="#FFFD8B6C"
             Content="{Binding ElementName=VisualRoot, Path=CapsLockInfo, Mode=TwoWay}"
             Visibility="Hidden" />
      </StackPanel>

      <StackPanel x:Name="FaultMessagePanel" Grid.Row="2" Grid.Column="1" Visibility="Hidden">
        <TextBlock x:Name="tblNoCredentialsMessage" Margin="12,18,12,5" Padding="0" FontSize="10"
                Foreground="#FFFD8B6C" Width="200" TextWrapping="Wrap"
                Text="{Binding ElementName=VisualRoot, Path=NoCredentialsInfo, Mode=TwoWay}" />
        <TextBlock x:Name="tblWrongCredentialsMessage" Margin="12,18,12,5" Padding="0" FontSize="10"
                Foreground="#FFFD8B6C" Width="200" TextWrapping="Wrap"
                Text="{Binding ElementName=VisualRoot, Path=WrongCredentialsInfo, Mode=TwoWay}" />
        <Button x:Name="btnOK" Style="{StaticResource Win8_OKButtonStyle}" Margin="12,1"
              HorizontalAlignment="Left" FontFamily="Segoe UI" Click="btnOK_OnClick"/>
      </StackPanel>
    </Grid>
  </Grid>

</ft:AdvancedUserControl>

Now let me explain the most important parts of the XAML Code.

I would like to point you to the following line

<Style TargetType="{x:Type PasswordBox}" BasedOn="{StaticResource Win8ExtendedPasswordBoxStyle}">	

Here we define an implicit style for the PasswordBox type which is based on the Win8ExtendedPasswordBoxStyle style. This is the most important part of the UserControl because this style defines the Win8 appearance and the Win8 behaviour of the PasswordBox. You can find the XAML code of the style here.

Next i want to show you the XAML code of the PasswordBox

<PasswordBox x:Name="PasswordBoxControl" Margin="12,18,12,5" Width="200"
            KeyDown="PasswordBoxControl_OnKeyDown"
            GotFocus="PasswordBoxControl_OnGotFocus"
            LostFocus="PasswordBoxControl_OnLostFocus"
            ap:PasswordBoxBinding.Password="{Binding ElementName=VisualRoot, Path=Password,
                                            Mode=TwoWay}"/>
	

The most important thing here is the Password attached property of the PasswordBoxBinding class. Without this attached property it would not be possible to bind the Password property of the PasswordBox because it is not a DependencyProperty. Here is the code of the attached property :

public class PasswordBoxBinding
{
  private static bool IsUpdating;

  public static string GetPassword(DependencyObject dpo)
  {
    return (string)dpo.GetValue( PasswordProperty );
  }
  public static void SetPassword(DependencyObject dpo, string value)
  {
    dpo.SetValue( PasswordProperty, value );
  }
  /// <summary>
  /// Gets or sets the bindable Password property on the PasswordBox control. This is a dependency property.
  /// </summary>
  public static readonly DependencyProperty PasswordProperty =
        DependencyProperty.RegisterAttached( "Password", typeof( string ),
                                 typeof( PasswordBoxBinding ),
                                 new FrameworkPropertyMetadata( String.Empty,
                                 new PropertyChangedCallback( OnPasswordPropertyChanged ) ) );
  /// <summary>
  /// Handles changes to the 'Password' attached property.
  /// </summary>
  private static void OnPasswordPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
  {
    PasswordBox targetPasswordBox = sender as PasswordBox;

    if (targetPasswordBox != null)
    {
      targetPasswordBox.PasswordChanged -= PasswordBox_PasswordChanged;

      if (IsUpdating == false)
      {
        targetPasswordBox.Password = (string)e.NewValue;
      }

      targetPasswordBox.PasswordChanged += PasswordBox_PasswordChanged;
    }
  }

  /// <summary>
  /// Handle the 'PasswordChanged'-event on the PasswordBox
  /// </summary>
  private static void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
  {
    PasswordBox passwordBox = sender as PasswordBox;

    IsUpdating = true;
    SetPassword( passwordBox, passwordBox.Password );
    IsUpdating = false;
  }
}	

At least i would like to say a few words about this part of the XAML code

<Button x:Name="btnOK" Style="{StaticResource Win8_OKButtonStyle}" Margin="12,1"
        HorizontalAlignment="Left" FontFamily="Segoe UI" Click="btnOK_OnClick"/>
	

This is the submit button. The look of the Button is defined in the Win8_OKButtonStyle which is implemented like this

<Style x:Key="Win8_OKButtonStyle" TargetType="{x:Type Button}">
  <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type Button}">
        <Grid>
          <Border x:Name="OuterBorder" Background="White" Width="70" Height="25">
            <Border x:Name="ContentBorder" Background="#FF1FAEFF" Margin="0.7"
                  BorderThickness="{TemplateBinding BorderThickness}">
              <TextBlock x:Name="OK_TextBlock" Text="OK" HorizontalAlignment="Center" VerticalAlignment="Center"
                      Foreground="White" />
            </Border>
          </Border>
        </Grid>

        <ControlTemplate.Triggers>
          <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="True">
            <Setter TargetName="ContentBorder" Property="Background" Value="#FF3EB9FF" />
          </DataTrigger>

          <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsPressed}" Value="True">
            <Setter TargetName="ContentBorder" Property="Background" Value="Transparent" />
            <Setter TargetName="OK_TextBlock" Property="Foreground" Value="Black" />
          </DataTrigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style	

There is just one thing I want to point on. As you can see in the XAML code of the UserControl i have defined an implicit style for the label type where i have first used the Segoe WP SemiLight font because it fits best to the Windows 8 font (no this is commented out). Unfortunately this font is not available as a standard font in Windows 7 so i switched to the available Segoe UI Light font (if you install the Windows Phone SDK 7.0, 7.1 - maybe 8.0 - the just mentioned font will be installed).

Here is the code of the Win8ExtendedPasswordBoxStyle used for the PasswordBox :

<Style x:Key="Win8ExtendedPasswordBoxStyle" TargetType="{x:Type PasswordBox}"
     BasedOn="{StaticResource {x:Type PasswordBox}}">
  <Setter Property="Foreground" Value="{StaticResource {x:Static SystemColors.ControlTextBrushKey}}"/>
  <Setter Property="Background" Value="{StaticResource {x:Static SystemColors.WindowBrushKey}}"/>
  <Setter Property="FontFamily" Value="Segoe UI"/>
  <Setter Property="PasswordChar" Value="?"/>
  <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
  <Setter Property="BorderThickness" Value="0"/>
  <Setter Property="HorizontalContentAlignment" Value="Left"/>
  <Setter Property="VerticalContentAlignment" Value="Center"/>
  <Setter Property="Padding" Value="4,1,1,2"/>
  <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
  <Setter Property="AllowDrop" Value="true"/>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type PasswordBox}">
        <Border x:Name="OuterBorder"
              BorderBrush="{TemplateBinding BorderBrush}"
              BorderThickness="{TemplateBinding BorderThickness}"
              Background="{TemplateBinding Background}"
              SnapsToDevicePixels="true">

          <Grid VerticalAlignment="Center">
            <Grid.ColumnDefinitions>
              <ColumnDefinition />
              <ColumnDefinition Width="Auto" />
              <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>

            <ScrollViewer x:Name="PART_ContentHost"
                      SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
            <TextBox x:Name="RevealedPassword"
                  Text="{TemplateBinding ap:PasswordBoxBinding.Password}"
                  VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                  VerticalContentAlignment="Center"
                  Padding="{TemplateBinding Padding}"
                  Margin="{TemplateBinding Padding}"
                  Background="{TemplateBinding Background}"
                  Visibility="Hidden" BorderThickness="0" IsReadOnly="True" FontFamily="Segoe UI" />
            <TextBlock x:Name="PART_TextBlockHint"
                    Padding="{TemplateBinding Padding}"
                    Background="Transparent"
                    Cursor="IBeam" Margin="2,0" FontFamily="Segoe UI Light" Foreground="#FF5D5F62"
                    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                    HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
                    Visibility="{Binding ElementName=RevealedPassword, Path=Text,
                            Converter={StaticResource StringToOppositeVisibilityConverter}}" />

            <Button x:Name="PART_RevealButton"
                  Grid.Column="1" SnapsToDevicePixels="True"
                  Style="{StaticResource RevealButtonExtendedStyle}"
                  Visibility="{Binding ElementName=RevealedPassword, Path=Text,
                                Converter={StaticResource StringToVisibilityConverter}}">
            </Button>

            <Button x:Name="PART_SubmitButton" FontSize="11"
                  Grid.Column="2" SnapsToDevicePixels="True"
                  Style="{StaticResource SubmitButtonStyle}">
            </Button>
          </Grid>
        </Border>

        <ControlTemplate.Triggers>
          <Trigger Property="IsEnabled" Value="false">
            <Setter Property="Background" TargetName="OuterBorder"
                  Value="{StaticResource {x:Static SystemColors.ControlBrushKey}}" />
            <Setter Property="Foreground"
                  Value="{StaticResource {x:Static SystemColors.GrayTextBrushKey}}" />
            <Setter Property="Opacity" TargetName="PART_RevealButton" Value="0.5" />
            <Setter Property="Text" TargetName="PART_TextBlockHint" Value="{}{disabled} "/>
          </Trigger>
          <DataTrigger Binding="{Binding ElementName=PART_RevealButton, Path=IsPressed}" Value="True">
            <Setter TargetName="RevealedPassword" Property="Visibility" Value="Visible" />
          </DataTrigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

... and afterwards the both corresponding Button styles.

First the RevealButtonExtendedStyle:

<Style x:Key="RevealButtonExtendedStyle" TargetType="{x:Type Button}">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type Button}">
        <Grid>
          <Border x:Name="PasswordRevealGlyphBorder" Background="Transparent" Margin="0,1"
                BorderThickness="{TemplateBinding BorderThickness}">
                    <TextBlock x:Name="GlyphElement" Foreground="Black"
                            VerticalAlignment="Center" HorizontalAlignment="Center" 
                            Text="&#xE052;" FontFamily="Segoe UI Symbol" Margin="3,0"
                            FontSize="{TemplateBinding FontSize}" />
          </Border>
        </Grid>

        <ControlTemplate.Triggers>
          <DataTrigger Binding="{Binding ElementName=GlyphElement, Path=IsMouseOver}" Value="True">
            <Setter TargetName="PasswordRevealGlyphBorder" Property="Background" 
                  Value="Gainsboro" />
          </DataTrigger>

          <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsPressed}"
                    Value="True">
            <Setter TargetName="PasswordRevealGlyphBorder" Property="Background" Value="Black" />
            <Setter TargetName="GlyphElement" Property="Foreground" Value="White" />
          </DataTrigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

... and second the SubmitButtonStyle:

<Style x:Key="SubmitButtonStyle" TargetType="{x:Type Button}">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type Button}">
        <Grid>
          <Border x:Name="SignInGlyphBorder"
                Background="#FF1FAEFF" Margin="1"
                BorderThickness="{TemplateBinding BorderThickness}">
            <TextBlock x:Name="GlyphElement" Foreground="White" Padding="3,0"
                    HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                    Text="&#x2192;" FontFamily="Calibri" Margin="3" SnapsToDevicePixels="True"
                    FontWeight="Bold" FontSize="{TemplateBinding FontSize}" />
          </Border>
        </Grid>

        <ControlTemplate.Triggers>
          <DataTrigger Binding="{Binding ElementName=GlyphElement, Path=IsMouseOver}" Value="True">
            <Setter TargetName="SignInGlyphBorder" Property="Background" Value="#FF3EB9FF" />
          </DataTrigger>

          <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsPressed}"
                    Value="True">
            <Setter TargetName="SignInGlyphBorder" Property="Background" Value="Transparent" />
            <Setter TargetName="GlyphElement" Property="Foreground" Value="Black" />
          </DataTrigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

Conclusion

I hope that you picked up something useful from this article. Suggestions or informations about mistakes will be beneficial. And one request : If you like reading the article (or using the code) please rate it. Thanks.

Points of Interest

I have implemented the control using the .NET 3.5 Framework and not the .NET 4.0 Framework ... Why ? Because there are still many applications running outside with .NET 3.5 ! And if I use some classes from the .NET 4.0 Framework in my Styles (like the VisualStateManager class) it would not be so easy to adapt the code to .NET 3.5 applications. I know that it would be possible to use the WPF Toolkit Library for some certain classes like the just mentioned (VisualStateManager), but then it would be neccessary to reference one more library. As often as possible i try to write my libraries in .NET 3.5 because the possibilities for reusing them are more versatile.

If you have a .NET 4.0 project you can use the library without drawbacks. All the styles and classes are working properly.

There will be more cool stuff available soon here on CodeProject, because I decided to write some articles about all the smart controls I have created in the last years resp. months.

Recent Changes

Adding the input capability of the user name, has led to the following code changes.

First of all i have added a TextBox control to the XAML code of the UserControl :

<TextBox x:Name="tbUserName" Margin="12,5,12,0"
        FontSize="{StaticResource MediumFontSize}"
        Style="{StaticResource SmartWatermarkTextBoxStyle}"
        Height="{Binding ElementName=PasswordBoxControl, Path=ActualHeight}"
        Text="{Binding ElementName=VisualRoot, Path=UserName, Mode=TwoWay,
                  UpdateSourceTrigger=PropertyChanged}"
        Visibility="{Binding ElementName=VisualRoot, Path=IsUserOptionAvailable,
                      Converter={conv:BoolToVisibilityConverter}}" />
	

Now let me explain the most important parts of this TextBox control.

To make it a little bit smarter the TextBox was styled using the SmartWatermarkTextBoxStyle. This ensures, that we have a Watermark in the TextBox. The XAML code of the SmartWatermarkTextBoxStyle will be shown afterwards. The second interesting point is the binding of the Height property. It is binded to the ActualHeight of the PasswordBoxControl. This ensures that we have balanced represantation of the controls. But the most important thing is the binding of the Visibility property. It is binded to the IsUserOptionAvailable property using a BoolToVisibilityConverter converter. So you have only to set this property to true if you want to activate the input capability of the user name.

As promised here is the XAML code of the SmartWatermarkTextBoxStyle :

<Style x:Key="SmartWatermarkTextBoxStyle" BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
  <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
  <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
  <Setter Property="BorderBrush" Value="{StaticResource TextBoxBorder}"/>
  <Setter Property="FontFamily" Value="Segoe UI"/>
  <Setter Property="BorderThickness" Value="1"/>
  <Setter Property="HorizontalContentAlignment" Value="Left"/>
  <Setter Property="VerticalContentAlignment" Value="Center"/>
  <Setter Property="Padding" Value="4,1,1,1"/>
  <Setter Property="AllowDrop" Value="true"/>
  <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type TextBox}">
        <Border x:Name="OuterBorder" BorderBrush="{TemplateBinding BorderBrush}"
              BorderThickness="{TemplateBinding BorderThickness}"
              Background="{TemplateBinding Background}"
              SnapsToDevicePixels="true">
          <Grid>
            <ScrollViewer x:Name="PART_ContentHost"
                      SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
            <TextBlock x:Name="PART_TextBlockHint"
                    Text="Enter Username" SnapsToDevicePixels="True"
                    Padding="{TemplateBinding Padding}" Background="Transparent"
                    Cursor="IBeam" Margin="2,0" FontFamily="Segoe UI Light" Foreground="#FF5D5F62"
                    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                    HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
                    Visibility="{TemplateBinding Text,
                            Converter={StaticResource StringToOppositeVisibilityConverter}}" />
          </Grid>
        </Border>

        <ControlTemplate.Triggers>
          <Trigger Property="IsEnabled" Value="false">
            <Setter Property="Background" TargetName="OuterBorder"
                  Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
            <Setter Property="Foreground"
                  Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
          </Trigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>
	

History

04th Dec 2012 - Added the option to enter the username and described all code changes here

28th Nov 2012 - Improved the article and added much more information about the UserControl

23rd Nov 2012 - First published

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here