PasswordTextBox for Silverlight 2 Beta 2

14. March 2008

The standard TextBox control that is built into Silverlight 2 Beta 2 is a bit lacking and is missing alot of features that we have become accustomed to in a TextBox. One of those features is being able to mask the characters being displayed when its used to accept passwords. Here's a small PasswordTextBox control I built that does just that; it masks the characters from being displayed so you can accept passwords like you're used to.

Update 6/7/2008 - I just tested this with Silverlight 2 Beta 2 and it works just the same as it does with Beta 1. 

Updated 3/17/2008 - I updated this code to support the Delete and Backspace keys, and to maintain caret/cursor position within the box. I also added the PasswordChar property that defines what the masking character is used to hide the password entered.

/// Copyright 2008 Chris Pietschmann (http://pietschsoft.com)
/// This work is licensed under a Creative Commons Attribution 3.0 United States License
/// http://creativecommons.org/licenses/by/3.0/us/
///
/// This is a Password TextBox built for use with Silverlight 2 Beta 1
/// The reason this was built, is because the standard TextBox in
/// Silverlight 2 Beta 1 does not have Password support.
/// Original Link: http://pietschsoft.com/post/2008/03/PasswordTextBox-for-Silverlight-2-Beta-1.aspx
///

using System.Windows.Controls;

namespace SilverlightPasswordTextBox
{
    public partial class PasswordTextBox : TextBox
    {
        public PasswordTextBox()
        {
            this.TextChanged += new TextChangedEventHandler(PasswordTextBox_TextChanged);
            this.KeyDown += new System.Windows.Input.KeyEventHandler(PasswordTextBox_KeyDown);
        }

        #region Event Handlers

        public void PasswordTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (base.Text.Length >= _Text.Length)
                _Text += base.Text.Substring(_Text.Length);
            DisplayMaskedCharacters();
        }

        public void PasswordTextBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
        {
            int cursorPosition = this.SelectionStart;
            int selectionLength = this.SelectionLength;

            // Handle Delete and Backspace Keys Appropriately
            if (e.Key == System.Windows.Input.Key.Back
                || e.Key == System.Windows.Input.Key.Delete)
            {
                if (cursorPosition < _Text.Length)
                    _Text = _Text.Remove(cursorPosition, (selectionLength > 0 ? selectionLength : 1));
            }
           
            base.Text = _Text;
            this.Select((cursorPosition > _Text.Length ? _Text.Length : cursorPosition), 0);
            DisplayMaskedCharacters();
        }

        #endregion

        #region Private Methods

        private void DisplayMaskedCharacters()
        {
            int cursorPosition = this.SelectionStart;
           
            // This changes the Text property of the base TextBox class to display all Asterisks in the control
            base.Text = new string(_PasswordChar, _Text.Length);

            this.Select((cursorPosition > _Text.Length ? _Text.Length : cursorPosition), 0);
        }

        #endregion

        #region Properties

        private string _Text = string.Empty;
        /// <summary>
        /// The text associated with the control.
        /// </summary>
        public new string Text
        {
            get { return _Text; }
            set
            {
                _Text = value;
                DisplayMaskedCharacters();
            }
        }

        private char _PasswordChar = '*';
        /// <summary>
        /// Indicates the character to display for password input.
        /// </summary>
        public char PasswordChar
        {
            get { return _PasswordChar; }
            set { _PasswordChar = value; }
        }

        #endregion
    }
}

Update 4/2/2008: Here's the above code translated to VB.NET

'' Copyright 2008 Chris Pietschmann (http://pietschsoft.com)
'' This work is licensed under a Creative Commons Attribution 3.0 United States License
'' http://creativecommons.org/licenses/by/3.0/us/
''
'' This is a Password TextBox built for use with Silverlight 2 Beta 1
'' The reason this was built, is because the standard TextBox in
'' Silverlight 2 Beta 1 does not have Password support.
'' Original Link: http://pietschsoft.com/post/2008/03/PasswordTextBox-for-Silverlight-2-Beta-1.aspx
Public Class PasswordTextBox
    Inherits TextBox

    Public Sub PasswordTextBox_TextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs) Handles Me.TextChanged
        If MyBase.Text.Length >= _Text.Length Then
            _Text += MyBase.Text.Substring(_Text.Length)
        End If
        DisplayMaskedCharacters()
    End Sub

    Public Sub PasswordTextBox_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs) Handles Me.KeyDown
        Dim cursorPosition As Integer = Me.SelectionStart
        Dim selectionLength As Integer = Me.SelectionLength

        '' Handle Delete and Backspace Keys Appropriately
        If e.Key = Key.Back Or e.Key = Key.Delete Then
            If cursorPosition < _Text.Length Then
                Dim lengthToRemove As Integer = 1
                If selectionLength > 0 Then lengthToRemove = selectionLength
                _Text = _Text.Remove(cursorPosition, lengthToRemove)
            End If
        End If

        MyBase.Text = _Text
        If cursorPosition > _Text.Length Then
            Me.Select(_Text.Length, 0)
        Else
            Me.Select(cursorPosition, 0)
        End If
        DisplayMaskedCharacters()
    End Sub

    Private Sub DisplayMaskedCharacters()
        Dim cursorPosition As Integer = Me.SelectionStart

        '' This changes the Text property of the base TextBox class to display all Asterisks in the control
        MyBase.Text = New String(_PasswordChar, _Text.Length)

        If cursorPosition > _Text.Length Then
            Me.Select(_Text.Length, 0)
        Else
            Me.Select(cursorPosition, 0)
        End If
    End Sub

    Private _Text As String = String.Empty
    Overloads Property Text() As String
        Get
            Return _Text
        End Get
        Set(ByVal value As String)
            _Text = value
            DisplayMaskedCharacters()
        End Set
    End Property

    Private _PasswordChar As Char = "*"
    Public Property PasswordChar() As Char
        Get
            Return _PasswordChar
        End Get
        Set(ByVal value As Char)
            _PasswordChar = value
        End Set
    End Property

End Class

 

Silverlight



Comments

Ron
Ron
3/19/2008 7:49:56 AM #
Looks good, however, one complaint. You construct the string using a for-loop with "p += "*"", which can be inefficient. I would recommend in this case  using "base.Text = new String( '*', _Text.Length )".

At first I thought you did it this way because the Silverlight runtime may have cut out that string constructor for brevity, but the Silverlight documentation claims it exists: msdn2.microsoft.com/.../xsa4321w(VS.95).aspx

Ron
Ron
3/19/2008 7:53:17 AM #
One more thing. Asterisks are kind of old-school password characters. I would actually recommend using the U+2022 "Bullet" character: "••••••••••"

Looks cleaner and more professional Smile
3/19/2008 9:14:36 AM #
Pingback from code-inside.de

Wöchentliche Rundablage: ASP.NET MVC, Silverlight 2, LINQ… | Code-Inside Blog
3/21/2008 10:53:17 PM #
Pingback from yahoo.325mb.com

caret
Edson
Edson
3/26/2008 10:52:00 PM #
i      I seem to be getting this error in studio 2008
Format of the initialization string does not conform to specification starting at index 0.

      while i am doing this as my connection string
        <connectionStrings>
        <add name="AccessFileName" connectionString="C:\Documents and Settings\Edson CTR Fernandes\My Documents\Visual Studio 2008\WebSites\CrystalReportsWebSite1\HLN_RCL.MDB" providerName="System.Data.OleDb&quot;"/>
         </connectionStrings>
3/29/2008 2:58:32 AM #
Hi Chris,

I'm using this code in my college project.
Thank you so much for your contribution.
Jim Moore
Jim Moore
4/3/2008 11:40:07 AM #
Hi Chris,

Is it possible to write this in VB?

Thanks,
Jim
4/3/2008 5:57:38 PM #
No problem. I have posted a VB.NET version of this control within the post.
Jim Moore
Jim Moore
4/4/2008 3:10:57 AM #
Awesome! Thank you so much. I'll give it a try,
Jim
Jim Moore
Jim Moore
4/4/2008 3:13:08 AM #
Chris,

How do you wire this up? My app.xaml is loading page.xmal.   page.xmal has the textbox. page.xmal.vb  inherits 'UserControl', not 'TextBox'. If I create a seperate class with this code in it, how do I get the page.xmal to talk to the PasswordText.xmal when the inheritance is different? Can I place this logic directly into page.xmal? I tried this though, but then the class will only inherit either 'UserControl' or 'Textbox', but not both.

Can you help with this?

Thanks,
Jim
Rui Marinho
Rui Marinho
4/4/2008 4:26:24 AM #
Hi chirs.. how are you? nice post it works fine this code.

I was wondering if you now if its possible to make integreation with a login UI in silverlight and the asp autentication!

Greetings
Rui
5/11/2008 10:41:38 PM #
Just to let you know that (on the Mac with Safari at least), SelectionLength is broken for both the TextBox and the WatermarkedTextBox.

Smile

Michael Foord
5/27/2008 6:23:11 PM #
Hi,

There was a "little" bug in the code.. here is my version (supports selection by "Home" / "End" / "Mouse" etc`)

Use as you like...

Amit.



        public partial class PasswordTextBox : TextBox
        {
            private string text;

            private const char passwordMaskChar = '*';
            private int lastSelectionLength;
            private int lastCursorPosition;

            public new string Text
            {
                get { return text; }
                set { text = value; }
            }

            public PasswordTextBox()
            {
                text = string.Empty;
                lastSelectionLength = 0;
                lastCursorPosition = 0;
                this.KeyDown += PasswordTextBox_KeyDown;
                this.TextChanged += PasswordTextBox_TextChanged;
                this.SelectionChanged += new RoutedEventHandler(PasswordTextBox_SelectionChanged);

            }

            void PasswordTextBox_SelectionChanged(object sender, RoutedEventArgs e)
            {

                if (this.SelectionLength != lastSelectionLength)
                {
                    lastSelectionLength = this.SelectionLength;
                    //Debug.WriteLine("Length = " + lastSelectionLength);
                }

                if (this.SelectionStart != lastCursorPosition)
                {
                    lastCursorPosition = this.SelectionStart;
                    //Debug.WriteLine("Position = " + lastCursorPosition);
                }

            }
            
            public void PasswordTextBox_TextChanged(object sender, TextChangedEventArgs e)
            {
                //Debug.WriteLine("In PasswordTextBox_TextChanged");
                if (base.Text.Length > text.Length)
                {
                    // append some chars in a position position
                    for (int i = 0; i < base.Text.Length; i++)
                    {
                        if (base.Text[i] != passwordMaskChar)
                        {
                            text = text.Insert(i, base.Text[i].ToString());
                        }
                    }
                }
                else if (base.Text.Length == text.Length)
                {
                    // replace an existing char
                    for (int i = 0; i < base.Text.Length; i++)
                    {
                        if (base.Text[i] != passwordMaskChar)
                        {
                            text = text.Substring(0, i) + base.Text[i] + text.Substring(i+1);
                        }
                    }
                }
                else
                {
                    int amountOfCharsToReplace = text.Length - base.Text.Length + 1;
                    text = text.Substring(0, lastCursorPosition) + base.Text[lastCursorPosition] + text.Substring(lastCursorPosition + amountOfCharsToReplace);
                }

                //Debug.WriteLine(text);

                MaskText();
            }

            public void PasswordTextBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
            {
                //Debug.WriteLine("In PasswordTextBox_KeyDown");

                switch (e.Key)
                {
                        
                    case Key.Delete:
                        if (this.SelectionStart < text.Length)
                        {
                            text = text.Remove(this.SelectionStart, (lastSelectionLength > 0 ? lastSelectionLength : 1));
                        }
                        break;
                    case Key.Back:
                        if (!(lastSelectionLength == 0 && lastCursorPosition == 0))
                        {
                            text = text.Remove(this.SelectionStart, (lastSelectionLength > 0 ? lastSelectionLength : 1));
                        }
                        break;
                }
                return;
            }

            private void MaskText()
            {
                int cursorPosition = this.SelectionStart;

                base.Text = new string(passwordMaskChar, text.Length);

                this.Select((cursorPosition > text.Length ? text.Length : cursorPosition), 0);
            }



        }
Comments are closed