【问题标题】:Remove the last entered character in combobox删除组合框中最后输入的字符
【发布时间】:2026-02-19 12:45:01
【问题描述】:

我又遇到了一个问题。

我已设置我的组合框,使其仅接受与组合框项目中任何项目的名称匹配的字符。

现在我遇到了一个问题。请看一下我的代码,然后我会向您解释问题:

private void myComboBox_KeyUp(object sender, KeyEventArgs e)
    {
        // Get the textbox part of the combobox
        TextBox textBox = cbEffectOn.Template.FindName("PART_EditableTextBox", cbEffectOn) as TextBox;

        // holds the list of combobox items as strings
        List<String> items = new List<String>();

        // indicates whether the new character added should be removed
        bool shouldRemoveLastChar = true;

        for (int i = 0; i < cbEffectOn.Items.Count; i++)
        {
            items.Add(cbEffectOn.Items.GetItemAt(i).ToString());
        }

        for (int i = 0; i < items.Count; i++)
        {
            // legal character input
            if (textBox.Text != "" && items.ElementAt(i).StartsWith(textBox.Text))
            {
                shouldRemoveLastChar = false;
                break;
            }
        }

        // illegal character input
        if (textBox.Text != "" && shouldRemoveLastChar)
        {
            textBox.Text = textBox.Text.Remove(textBox.Text.Length - 1);
            textBox.CaretIndex = textBox.Text.Length;
        }
    }

在最后一个 if 条件下,我从组合框中删除了最后一个字符。但是用户可以使用方向键或鼠标来改变光标的位置,在文本中间输入文本。

因此,如果通过在文本中间输入一个字符,如果文本变得无效,我的意思是如果它与 ComboBox 中的项目不匹配,那么我应该删除最后输入的字符。谁能建议我如何获取最后插入的字符并将其删除?

更新:

string OldValue = "";

private void myComboBox_KeyDown(object sender, KeyEventArgs e)
{
    TextBox textBox = cbEffectOn.Template.FindName("PART_EditableTextBox", cbEffectOn) as TextBox;

    List<String> items = new List<String>();

    for (int i = 0; i < cbEffectOn.Items.Count; i++)
    {
        items.Add(cbEffectOn.Items.GetItemAt(i).ToString());
    }

    OldValue = textBox.Text;

    bool shouldReplaceWithOldValue = true;

    string NewValue = textBox.Text.Insert(textBox.CaretIndex,e.Key.ToString()).Remove(textBox.CaretIndex + 1,textBox.Text.Length - textBox.CaretIndex);

    for (int i = 0; i < items.Count; i++)
    {
        // legal character input
        if (NewValue != "" && items.ElementAt(i).StartsWith(NewValue, StringComparison.InvariantCultureIgnoreCase))
        {
            shouldReplaceWithOldValue = false;
            break;
        }
    }

    //// illegal character input
    if (NewValue != "" && shouldReplaceWithOldValue)
    {
        e.Handled = true;
    }

}

这里我已经尝试移动KeyDown事件中的所有代码来解决上述问题。此代码运行良好,但有 1 个问题。

如果我有任何名为 Birds & Animals 的项目,则在输入 Birds 和空格后,我无法输入 &。

我知道问题出在哪里,但不知道解决方案。

问题是:要输入 & 我必须按 shift 键,然后按 7 键。但两者都作为不同的密钥发送。

我想到的解决方案: 1)我应该将我的代码移动到 KeyUp 事件。但是这里会出现长按和快速打字的问题。 2)我想我应该用一些东西替换 e.Key 。但不知道是什么。

【问题讨论】:

  • 您是否正在尝试创建 Intellisense ComboBox?
  • 我不知道,你说的智能感知组合框是什么意思。我基本上想强制用户输入与 ComboBox 的一项匹配的文本。

标签: c# wpf combobox


【解决方案1】:

我不确定这是否是您正在尝试做的事情,但我觉得您正在尝试做我们通常在 Visual Studio Intellisense 中看到的内容,并在我们键入时调整结果。

您应该使用 WPF 提供的验证机制,而不是删除击键。这是如何工作的示例。

涵盖的场景:

  1. 输入完全匹配一个组合框项目:TypedInput & SelectedItem 都显示完全匹配。
  2. 输入部分匹配某些元素:TypedInput 将弹出列表列入候选名单。绑定显示匹配的文本,而 SelectedItem 保持为空。
  3. 输入与列表中的任何项目都不匹配,无论是从开始还是在某些位置 随机点:用户在视觉上得到反馈(有可能 添加额外的反馈信息)带有典型的红色轮廓。这 TypedInput 仍然是最后一个有效条目,SelectedItem 可能或可能 不为 null,具体取决于最后一个 TypedInput 是否匹配任何项目。

完整代码:

MainWindow.xaml

<Window x:Class="Sample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:l="clr-namespace:Sample"
        Title="MainWindow" Height="350" Width="525" 
        DataContext="{Binding Source={x:Static l:MainWindowViewModel.CurrentInstance}}">
    <StackPanel>
        <TextBlock>
            <Run Text="Typed valid text" />
            <Run Text="{Binding TypedText}"/>
        </TextBlock>
        <TextBlock>
            <Run Text="Valid SelectedItem" />
            <Run Text="{Binding SelectedItem}"/>
        </TextBlock>
        <ComboBox ItemsSource="{Binding FilteredItems}" IsEditable="True" IsTextSearchEnabled="False" SelectedItem="{Binding SelectedItem}">
            <ComboBox.Text>
                <Binding Path="TypedText" UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <l:ContainsValidationRule />
                    </Binding.ValidationRules>
                </Binding>
            </ComboBox.Text>
        </ComboBox>
    </StackPanel>
</Window>

MainWindow.xaml.cs

namespace Sample
{
    public partial class MainWindow { public MainWindow() { InitializeComponent(); } }
}

ContainsValidationRule.cs -- 解决方案的关键

namespace Sample
{
    using System.Globalization;
    using System.Linq;
    using System.Windows.Controls;

    public class ContainsValidationRule : ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            var result = MainWindowViewModel.CurrentInstance.Items.Any(x => x.ToLower(cultureInfo).Contains((value as string).ToLower(cultureInfo)));
            return new ValidationResult(result, "No Reason");
        }
    }
}

MainWindowViewModel - 支持 ViewModel 单例

namespace Sample
{
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Runtime.CompilerServices;

    public sealed class MainWindowViewModel : INotifyPropertyChanged
    {
        private string _typedText;
        private string _selectedItem;
        private static readonly MainWindowViewModel Instance = new MainWindowViewModel();

        private MainWindowViewModel()
        {
            Items = new[] { "Apples", "Apples Green", "Bananas", "Bananas & Oranges", "Oranges", "Grapes" };
        }

        public static MainWindowViewModel CurrentInstance { get { return Instance; } }

        public string SelectedItem
        {
            get { return _selectedItem; }
            set
            {
                if (value == _selectedItem) return;
                _selectedItem = value;
                OnPropertyChanged();
            }
        }

        public string TypedText
        {
            get { return _typedText; }
            set
            {
                if (value == _typedText) return;
                _typedText = value;
                OnPropertyChanged();
                OnPropertyChanged("FilteredItems");
            }
        }

        public IEnumerable<string> Items { get; private set; }

        public IEnumerable<string> FilteredItems
        {
            get
            {
                return Items == null || TypedText == null ? Items : Items.Where(x => x.ToLowerInvariant().Contains(TypedText.ToLowerInvariant()));
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

【讨论】:

  • 我已经试过你的答案,效果很好。现在,正在开发的应用程序将在未来完成后被非常低教育的人使用。你不认为你给出的解决方案对他们来说会比我问的更难吗?我想知道你的意见。
  • 我认为提供视觉反馈比强迫用户做一些事情要好,尤其是你正在使用的笨拙的方法。即使他们受教育程度低,他们仍然是非常明确的智慧生物,如果你告诉他们鲜红色的轮廓是什么意思,他们会明白的。不仅如此,您还可以使用Validation.ErrorTemplate 添加适当的错误原因,在此处告诉他们,然后告诉他们他们做错了什么。这就是MS打算解决这些问题的方式,逆流而上只会让你的生活变得悲惨,用户通常会因为你吃键盘而讨厌你。
  • 谢谢,我喜欢你的建议。
  • 很高兴得到任何帮助 :) 祝你好运!
【解决方案2】:

在您的ComboBox Textbox 上订阅TextChanged 事件,而不是KeyUp 事件。在事件处理程序中,您可以获得发生更改的偏移量。如果它使 Text 无效,您可以在处理程序中使用您的验证逻辑并删除偏移处的字符。

     private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        TextBox textBox = cbEffectOn.Template.FindName("PART_EditableTextBox", cbEffectOn) as TextBox;
        textBox.TextChanged += new TextChangedEventHandler(textBox_TextChanged);
    }

    void textBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        int index = e.Changes.First().Offset;
    }

【讨论】:

  • 它没有按预期工作。 1)它不允许我输入任何合法或非法的字符。 2)如果我从组合框中选择任何项目并将插入符号移动到任何其他位置并尝试输入任何字符,它允许我输入一个非法字符。 3)我可以使用删除或退格从文本组合框中删除任何字符,甚至从文本中间删除。
  • 我刚刚复制粘贴了您的代码尝试一下,现在它变得很奇怪。它给了我一个编译时错误,TextCompositionEventArgs 不包含处理的定义。
  • 我不知道我在想什么..可能是我该睡觉了..更新了答案,为您提供输入字符的索引。
  • @Khushi 如果您发现任何问题,请告诉我
【解决方案3】:

您是否考虑过使用字符串变量来保存组合框文本框部分中的最后一个合法文本值?

最初,该字符串为空,因为用户还没有输入任何内容,然后在处理每个KeyUp 事件时,如果输入了无效字符,则使用之前的字符串值替换文本框;否则之前的字符串值现在用新的完整字符串更新;等待用户输入。

【讨论】:

  • 在这种情况下,我还必须记住插入符号的位置。如果用户长时间按键会发生什么。我的意思是长按。
  • @Khushi - 我想在这种情况下你可以处理KeyDown
  • 上面的commet是什么意思?
  • 对于长按,KeyDown 事件可能比KeyUp 事件更有用,因为KeyDown 发生在KeyUp 事件之前。
  • 我试图记住 KeyDown 事件中组合框的 OldValue 并将新值与组合框中的每个项目进行比较,如果我没有找到匹配项,那么我用 OldValue 替换组合框的文本按键事件。到目前为止,我是成功的。你能帮我做长按吗?