问题
We are looking for a way to make a textbox replace certain characters as a person types in it. Note, we are focusing on the control itself, not bindings, viewmodels, etc. For the sake of this question, assume there is a window with a textbox sitting in the middle of it and nothing else. No data, no viewmodel, no bindings, etc.
I've updated the question because it seems all the answers below keep focusing on bindings, dependency properties, coersion, etc. While I appreciate the suggestions, as I stated above, our control is not a bound control so they aren't applicable.
Now while I could explain the reasons for that, that would make this post about five times longer as it's actually a complex and advanced use-case, but that has nothing to do with the question itself, which is why I've simplified our scenario down to focus on the specific question we're trying to resolve, which is about a textbox control, or possibly a subclass of one replacing characters as you type.
Hope that makes more sense now.
回答1:
The best way to accomplish this is using the TextChanged
event:
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
var tb = (TextBox)sender;
using (tb.DeclareChangeBlock())
{
foreach (var c in e.Changes)
{
if (c.AddedLength == 0) continue;
tb.Select(c.Offset, c.AddedLength);
if (tb.SelectedText.Contains(' '))
{
tb.SelectedText = tb.SelectedText.Replace(' ', '_');
}
tb.Select(c.Offset + c.AddedLength, 0);
}
}
}
This has several advantages:
- You don't go through the entire string every time, just the replaced part
- It behaves well with the undo manager and with pasting text
- You can easily encapsulate it into an attached property which can be applied to any text box
回答2:
You can use an IValueConverter. It involves quite a bit of code, but it's the preferred way if you ever want to go the MVVM path, or just do things the WPF way.
First, create a class that implements IValueConverter
public class IllegalCharactersToUnderscoreConverter
: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var stringValue = value as String;
if(stringValue == null)
return value;
return RemoveIllegalCharacters(stringValue);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
}
Import the namespace containing your ValueConverter
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
Create an instance of your converter in Window.Resources
<Window.Resources>
<local:IllegalCharactersToUnderscoreConverter x:Key="IllegalCharactersToUnderscore" />
</Window.Resources>
Use the converter in your binding expression. UpdateSourceTrigger=PropertyChanged is required for the text to be converted as you type.
<TextBox Text="{Binding MyText,
Converter={StaticResource IllegalCharactersToUnderscore},
UpdateSourceTrigger=PropertyChanged}"/>
回答3:
You could subclass TextBox you can just override the Metadata of the TextBox Text property
public class FilteredTextBox : TextBox
{
public FilteredTextBox()
{
TextBox.TextProperty.OverrideMetadata(typeof(FilteredTextBox), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null, CoerceMyTextValue, true, UpdateSourceTrigger.PropertyChanged));
}
private static object CoerceMyTextValue(DependencyObject d, object baseValue)
{
if (baseValue != null)
{
var userEnteredString = baseValue.ToString();
return userEnteredString.Replace(' ', '_');
}
return baseValue;
}
}
And you dont need to use bindings at all, this will just update the internal TextBox TextProperty as you type
<Window x:Class="WpfApplication13.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="428" Width="738" Name="UI"
xmlns:my="clr-namespace:WpfApplication13" >
<StackPanel>
<my:FilteredTextBox />
</StackPanel>
</Window>
回答4:
I tried to achieve this thing by handling this in my ViewModel
property that is bind to TextBox
itself like below and it works. In this example I replace '!' with underscore. Here i just put the replacement logic in property setter and if there is a change I replaced the text and Raised the propertychange async.
private string text;
public string Text
{
get { return text; }
set
{
text = value;
if (text.Contains("!"))
{
text = text.Replace('!', '_');
Dispatcher.BeginInvoke((Action) (() => RaisePropertyChange("Text")));
}
RaisePropertyChange("Text");
}
}
and the textbox binding is
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}"/>
来源:https://stackoverflow.com/questions/18971198/can-you-replace-characters-in-a-textbox-as-you-type