What's the best way to create a WPF TextBox that replaces spaces with underscores?
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?
I would bind the TextBox
to a ValueConverter
that eliminates whitespace on demand and replaces them with underscores.
The ValueConverter would look something like this:
public class SpaceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return System.Convert.ToString(value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string text = System.Convert.ToString(value);
//the meat and potatoes is this line
text = text.Replace(" ", "_");
return text;
}
}
And your TextBox
would bind to it this way:
<TextBox Text="{Binding Path=UserString, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource SpaceConverter}}" />
Note that UserString
would have to be in the current DataContext
.
Don't forget to define SpaceConverter
in the XAML as well. One way to do this, assuming you're working on a UserControl
, would be:
<UserControl.Resources>
<local:SpaceConverter x:Key="SpaceConverter" />
</UserControl.Resources>
Where local
is defined as the namespace containing SpaceConverter.cs.
I think your logic is good, but I'd put it in an override of OnPreviewKeyDown
.
Would it be better to just replace space with _ when the control loses focus or do you absolutely need it to be replaced as the user keys it in?
精彩评论