Jim Flood
Jim Flood

Reputation: 8487

How to get password as SecureString from DevExpress WinForms TextEdit

WPF has PasswordBox which receives password input into a SecureString, but I have a WinForms application. I can use SecurePasswordTextBox, but I want something that blends with DevExpress controls and DevExpress appearance customization. On the DevExpress TextEdit control you can set UseSystemPasswordChar = true, but it doesn't use SecureString (and won't: cf. 10/22/2010, cf. also.

How can I easily get SecureString support into a DevExpress WinForms TextEdit control?

I came up with something, which I post below as my own answer. Does anybody else have a solution?

Edit: I'm accepting my own answer, which works, because I need the DevExpress appearance.

Upvotes: 0

Views: 2708

Answers (2)

Greg Sansom
Greg Sansom

Reputation: 20850

The simplest way is to use the WPF PasswordBox hosted inside an ElementHost.

You can drag the ElementHost control from the ToolBox, or do the whole thing in code:

public Form1()
{
    InitializeComponent();
    System.Windows.Forms.Integration.ElementHost host = new System.Windows.Forms.Integration.ElementHost();
    System.Windows.Controls.PasswordBox pb=new System.Windows.Controls.PasswordBox();
    host.Child =  pb;
    this.Controls.Add(host);
}

Of course this does not use the DevExpress control, but I can't see any reason for using a Rich Text Editor as a password box when there is a simple alternative.

Upvotes: 1

Jim Flood
Jim Flood

Reputation: 8487

Here is my own answer:

Instead of subclassing TextEdit, which seems too complicated, I just catch the KeyPress events and hide each pressed character away until the EditValueChanging event. I keep unique tokens in a fake string, so that I can figure out what to change in my SecureString on each EditValueChanging event.

Here is the proof-of-concept -- a simple form with a TextEdit control and two event handlers:

public partial class PasswordForm : DevExpress.XtraEditors.XtraForm
{
    private Random random = new Random();
    private HashSet<char> pool = new HashSet<char>();
    private char secret;
    private char token;
    private List<char> fake = new List<char>();

    public PasswordForm()
    {
        InitializeComponent();
        this.Password = new SecureString();
        for (int i = 0; i < 128; i++)
        {
            this.pool.Add((char)(' ' + i));
        }
    }

    public SecureString Password { get; private set; }

    private void textEditPassword_EditValueChanging(object sender, DevExpress.XtraEditors.Controls.ChangingEventArgs e)
    {
        string value = e.NewValue as string;

        // If any characters have been deleted...
        foreach (char c in this.fake.ToArray())
        {
            if (value.IndexOf(c) == -1)
            {
                this.Password.RemoveAt(this.fake.IndexOf(c));
                this.fake.Remove(c);
                this.pool.Add(c);
            }
        }

        // If a character is being added...
        if (this.token != '\0')
        {
            int i = value.IndexOf(this.token);
            this.Password.InsertAt(i, this.secret);
            this.secret = '\0';
            fake.Insert(i, this.token);
        }
    }

    private void textEditPassword_KeyPress(object sender, KeyPressEventArgs e)
    {
        if (Char.IsControl(e.KeyChar))
        {
            this.token = '\0';
        }
        else
        {
            this.token = this.pool.ElementAt(random.Next(this.pool.Count)); // throws ArgumentOutOfRangeException when pool is empty
            this.pool.Remove(this.token);
            this.secret = e.KeyChar;
            e.KeyChar = this.token;
        }
    }
}

Upvotes: 1

Related Questions