Reputation: 8487
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
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
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