经过许多调试,我终于找到了一个完整、可行的解决方案(至少看起来是这样的)。
步骤1. 修改XAML标记
您需要像下面这样修改ComboBox:
<ComboBox
...
IsTextSearchEnabled="False"
...
PreviewTextInput="PreviewTextInput_EnhanceComboSearch"
PreviewKeyUp="PreviewKeyUp_EnhanceComboSearch"
DataObject.Pasting="Pasting_EnhanceComboSearch" />
例如,要禁用默认文本搜索,并添加事件处理程序以处理用户添加、删除和粘贴文本。
步骤2. 添加一个辅助函数,用于获取ComboBox的内部TextBox(因为WPF)
为了使PreviewTextInput_EnhanceComboSearch
和Pasting_EnhanceComboSearch
正常工作,您需要访问ComboBox的插入符号。不幸的是,要做到这一点,您需要遍历视觉树(感谢Matt Hamilton)。您可以在扩展方法中执行此操作,但我在我的Page
类中使用了静态方法:
public static T GetChildOfType<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj == null) return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child);
if (result != null) return result;
}
return null;
}
步骤三. 实现事件处理程序
请注意,我使用了
s => s.IndexOf(e.Text, StringComparison.InvariantCultureIgnoreCase) != -1
步骤三.a 在ComboBox内输入时触发搜索
PreviewTextInput
处理程序运行时,ComboBox中的.Text
属性包含修改之前的文本。因此,我们需要使用GetChildOfType
方法获取ComboBox的内部TextBox,以便获取其插入光标的确切位置。
为了实现不区分大小写的检查,可以使用以下代码:s => s.ToLower().Contains(e.Text.ToLower())
。记得根据需要更改这一部分内容。
private void PreviewTextInput_EnhanceComboSearch(object sender, TextCompositionEventArgs e)
{
ComboBox cmb = (ComboBox)sender;
cmb.IsDropDownOpen = true;
if (!string.IsNullOrEmpty(cmb.Text))
{
string fullText = cmb.Text.Insert(GetChildOfType<TextBox>(cmb).CaretIndex, e.Text);
cmb.ItemsSource = Names.Where(s => s.IndexOf(fullText, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
}
else if (!string.IsNullOrEmpty(e.Text))
{
cmb.ItemsSource = Names.Where(s => s.IndexOf(e.Text, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
}
else
{
cmb.ItemsSource = Names;
}
}
步骤 3.b:用户在组合框中粘贴时触发搜索
DataObject.Pasting
处理程序的行为类似于 PreviewTextInput
处理程序,因此我们需要再次使用插入符号。
private void Pasting_EnhanceComboSearch(object sender, DataObjectPastingEventArgs e)
{
ComboBox cmb = (ComboBox)sender;
cmb.IsDropDownOpen = true;
string pastedText = (string)e.DataObject.GetData(typeof(string));
string fullText = cmb.Text.Insert(GetChildOfType<TextBox>(cmb).CaretIndex, pastedText);
if (!string.IsNullOrEmpty(fullText))
{
cmb.ItemsSource = Names.Where(s => s.IndexOf(fullText, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
}
else
{
cmb.ItemsSource = Names;
}
}
第三步c:当用户删除ComboBox中的文本(以及按下空格,因为WPF)时触发搜索
当用户按下Delete或Backspace键时,将触发此操作。
同时也会触发Space键,因为PreviewTextInput
会忽略它,所以在示例中过滤出“John Doe”和“John Richards”中的“John”会很困难。
private void PreviewKeyUp_EnhanceComboSearch(object sender, KeyEventArgs e)
{
if (e.Key == Key.Back || e.Key == Key.Delete)
{
ComboBox cmb = (ComboBox)sender;
cmb.IsDropDownOpen = true;
if (!string.IsNullOrEmpty(cmb.Text))
{
cmb.ItemsSource = Names.Where(s => s.IndexOf(cmb.Text, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
}
else
{
cmb.ItemsSource = Names;
}
}
}
...那应该就足够了。