I'm creating a FilteredTextBox
in WPF that subclasses the included TextBox
control. The FilteredTextBox
must only allow characters in the range [a-zA-Z0-9_]
to be entered, and I've got that part pretty much working. I've hooked into OnPreviewTextInput
to handle typed characters, OnPreviewDrop
to filter characters that are dragged and dropped, and I've added a PreviewExecutedHandler that runs whenever a command is executed on the control to handle pasting.
So far, so good.
The tricky part is that the control should also replace spaces with underscores when they're typed in.
I've come up with a solution, but it feels pretty hacked together and I don't know what it's missing. I feel like there's got to be a better technique that I'm just not aware of. What I've done:
internal class FilteredTextBox : TextBox
{
public FilteredTextBox()
{
CommandManager.AddPreviewExecutedHandler(this, this.HandlePreviewExecuteHandler);
}
private void HandlePreviewExecuteHandler(object sender, ExecutedRoutedEventArgs e)
{
var uiCmd = e.Command as RoutedUICommand;
if (uiCmd != null && (uiCmd.Text == "Space" || uiCmd.Text == "ShiftSpace"))
{
// We're manually handling spaces, so we need to make appropriate checks.
if (this.Text.Length == this.MaxLength) return;
if (this.SelectionLength == 0)
{
// If the user is just typing a space normally
// We need to cache CaretIndex b/c it's reset to 0 when we set Text.
var tmpIndex = this.CaretIndex;
this.Text = this.Text.Insert(tmpIndex, "_");
this.CaretIndex = tmpIndex + 1;
}
else
{
// Otherwise, replace the selected text with the underscore and fixup the caret.
this.SelectedText = "_";
this.CaretIndex += this.SelectedText.Length;
this.SelectionLength = 0;
}
e.Handled = true; // If someone hits the spacebar, say we handled it.
return;
}
}
}
Is there a smarter way?