1

我在这里遇到另一个问题。

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

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

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 。但不知道是什么。

4

3 回答 3

2

而不是KeyUp事件,订阅TextChanged你的事件ComboBox Textbox。在事件处理程序中,您可以获得发生更改的偏移量。您可以在处理程序中使用您的验证逻辑,如果它使 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;
    }
于 2013-10-25T19:33:16.350 回答
2

我不确定这是否是您想要做的,但我觉得您正在尝试做我们通常在视觉工作室 Intellisense 中看到的东西,在我们输入时过滤掉结果。

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

涵盖的场景:

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

完整代码:

主窗口.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>

主窗口.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));
        }
    }
}
于 2013-10-28T17:36:27.190 回答
1

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

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

于 2013-10-25T19:27:09.563 回答