Display whitespace in a textbox as a different cha

2019-08-29 04:36发布

问题:

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.

回答1:

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.



回答2:

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.