wpf 您可以在键入时替换文本框中的字符吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18971198/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 09:43:00  来源:igfitidea点击:

Can you replace characters in a textbox as you type?

wpfinputtextbox

提问by Mark A. Donohoe

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.

希望现在更有意义。

回答by Eli Arbel

The best way to accomplish this is using the TextChangedevent:

实现此目的的最佳方法是使用TextChanged事件:

    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
  • 你不会每次都遍历整个字符串,只需要替换的部分
  • 它与撤消管理器和粘贴文本表现良好
  • 您可以轻松地将其封装到可以应用于任何文本框的附加属性中

回答by igelineau

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 MVVMpath, or just do things the WPF way.

您可以使用 IValueConverter。它涉及相当多的代码,但如果您想要走MVVM路径,或者只是以 WPF 方式做事,它是首选方式。

First, create a class that implements IValueConverter

首先,创建一个实现 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

导入包含您的 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 中创建转换器的实例

<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.

在绑定表达式中使用转换器。UpdateSourceTrigger=PropertyChanged 需要在您键入时转换文本。

<TextBox Text="{Binding MyText, 
                Converter={StaticResource IllegalCharactersToUnderscore}, 
                UpdateSourceTrigger=PropertyChanged}"/>

回答by sa_ddam213

You could subclass TextBox you can just override the Metadata of the TextBox Text property

你可以子类化 TextBox 你可以覆盖 TextBox Text 属性的元数据

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

而且您根本不需要使用绑定,这只会在您键入时更新内部 TextBox TextProperty

<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>

回答by Nitin

I tried to achieve this thing by handling this in my ViewModelproperty that is bind to TextBoxitself 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.

我试图通过在我的ViewModel属性中处理它来实现这一目标,该属性TextBox像下面一样绑定到自身并且它可以工作。在这个例子中,我替换了 '!' 带下划线。在这里,我只是将替换逻辑放在属性设置器中,如果有更改,我会替换文本并引发属性更改异步。

        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}"/>