How can I display all space characters entered in a System.Windows.Control.TextBox
as some other character, such as a dot? · · · ·
e.g.:
Enter your text: [abc······def]
Ideally I would also want to display the dot in a lighter color than the rest of the text
When the user enters whitespace, the textbox should only display something else. Ideally I do not want to modify the actual string. So the internal value of TextBox.Text
should remain unchanged, and when the user copies the content to the clipboard, or interacts in a similar way with the textbox, they should get spaces and not dots.
The solutions I found in a related Stackoverflow question, overriding the OnTextChangedEvent or using an IValueConverter, can replace the the spaces as they are entered, but this will of course also modify the backing string and I would prefer this to be totally transparent to the application and the user.
The easiest way to do that would be to manipulate the value using the String.Replace
method. You could do this in a TextBox.TextChanged
event handler:
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = (TextBox)sender;
// take note of current Caret position within TextBox
int caretIndex = textBox.CaretIndex;
textBox.Text = textBox.Text.Replace(" ", "•");
// reset Caret to original position
textBox.CaretIndex = caretIndex;
}
The trick here is to use a character that the user cannot type: "•"
(Just copy and paste this character). Using this unavailable character, it will be easy to restore the spaces afterwards:
string originalValue = textBox.Text.Replace("•", " ");
Unfortunately though, you won't be able to colour this dot a different colour without either using a RichTextBox
, or putting in a lot of effort.
You can do this with a value converter - something like this worked for me:
public sealed class WhiteSpaceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string str = (string)value;
if (str == null) return value;
return str.Replace(" ", "·");
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string str = (string)value;
if (str == null) return value;
return str.Replace("·", " ");
}
}
I also added the following to the text box's command binding for ApplicationCommands.Copy
(so if the user selects and copies the whitespace, they get the acutal space instead of the dot character replacing it):
static Converters.WhiteSpaceConverter conv = new Converters.WhiteSpaceConverter();
private void TextBox_Copy_Executed(object sender, ExecutedRoutedEventArgs e)
{
TextBox tb = sender as TextBox;
if (tb != null)
Clipboard.SetText((string)conv.ConvertBack(tb.SelectedText, typeof(TextBox), null, System.Globalization.CultureInfo.CurrentCulture));
}
Giving me XAML like this:
<cnv:WhiteSpaceConverter x:Key="WhiteSpaceConverter" />
...
<TextBox FontFamily="Consolas"
Text="{Binding ViewModelStringProperty,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource WhiteSpaceConverter}}"
>
<TextBox.CommandBindings>
<CommandBinding Command="{x:Static ApplicationCommands.Copy}" Executed="TextBox_Copy_Executed" />
</TextBox.CommandBindings>
</TextBox>
Caveat emptor: this will end up translating the dot character to a space if the user enters one into the textbox, and would also result in losing that character from the backing string if the converter ran.