我需要一个用于WPF C#的自动完成组合框。我尝试了几种方法,但没有任何效果。例如,我尝试过一个组合框:
<ComboBox Width="200"
IsEditable="True"
ItemsSource="{Binding Names}"
IsTextSearchEnabled="True"
HorizontalAlignment="Left"/>
Names
是一个字符串列表:Peter John,John,John Doe,Cathy,Howard,John Richards等等
如果您输入名称,例如组合框的John应该扩展,我应该看到
但这不起作用。我怎么能这样做?
答案 0 :(得分:6)
使用PreviewTextInput事件进行过滤并显示下拉列表:
private void ComboBox_TextInput_1(object sender, TextCompositionEventArgs e)
{
cmbperson.IsDropDownOpen = true;
cmbperson.ItemsSource = DataBase.Persons.Where(p => p.Name.Contains(e.Text)).ToList();
}
答案 1 :(得分:4)
经过大量的摆弄,我已经设法找到了一个完整的,有效的解决方案。 (或者似乎是这样。)
您需要像这样修改您的ComboBox:
<ComboBox
...
IsTextSearchEnabled="False"
...
PreviewTextInput="PreviewTextInput_EnhanceComboSearch"
PreviewKeyUp="PreviewKeyUp_EnhanceComboSearch"
DataObject.Pasting="Pasting_EnhanceComboSearch" />
即。 禁用 默认文本搜索,并添加将负责用户添加,删除和粘贴文本的事件处理程序。
要使PreviewTextInput_EnhanceComboSearch
和Pasting_EnhanceComboSearch
完全正常工作,您需要访问ComboBox的插入符号。不幸的是,要做到这一点,你需要遍历,呃,可视树(hat tip to 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
这相当于不区分大小写的s => s.Contains(e.Text)
检查。请记住改变那部分以满足您的需求。
运行PreviewTextInput
处理程序时,ComboBox中的.Text
属性包含之前的文本。因此,我们需要使用GetChildOfType
方法获取ComboBox的内部TextBox以获取其插入符号,因此我们知道插入的键入字符的确切位置。
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;
}
}
DataObject.Pasting
处理程序的行为方式与PreviewTextInput
hanlder类似,因此我们需要再次使用插入符号。
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;
}
}
当用户按下Delete或Backspace时,将触发此操作。
还有Space,因为PreviewTextInput
忽略了Space,所以很难过滤掉&#34; John&#34;来自&#34; John Doe&#34;和#34; John Richards&#34;在示例中。
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;
}
}
}
......这应该足够了。
答案 2 :(得分:3)
我建议您使用自动完成的控件而不是组合框。许多公司提供这样的控制,this一个是免费的并被认为是好的。
答案 3 :(得分:0)
我为WPF
创建了一个可以帮助您的自动填充功能。
点击下面的链接到github:
https://github.com/rhpontes/AutocompleteWpf
我希望它可以帮到你。
答案 4 :(得分:0)
在XAML中,您应该设置 IsEditable = True 并为PreviewKeyDown事件添加处理程序:
private void ComboBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
var cmb = (ComboBox)sender;
cmb.IsDropDownOpen = true;
var textbox = cmb.Template.FindName("PART_EditableTextBox", cmb) as TextBox;
cmb.ItemsSource = CurrentStorage.Organisations.Where(p => string.IsNullOrEmpty(cmb.Text) || p.Name.ToLower().Contains(textbox.Text.ToLower())).ToList();
}
答案 5 :(得分:0)
以下是对我有用的实现:
<ComboBox
Name="ItemsControl"
IsEditable="True"
KeyUp="OnItemsControlKeyUp"
我检查自上次应用过滤器以来文本是否已更改(以避免在按下非字母数字键时进行过滤)。
private string _textBeforeFilter;
private void OnItemsControlKeyUp(object sender, KeyEventArgs e)
{
var arrowKey = e.Key >= Key.Left && e.Key <= Key.Down;
// if arrow key (navigating) or the text hasn't changed, then a we don't need to filter
if (arrowKey || ItemsControl.Text.EqualsIgnoreCase(_textBeforeFilter)) return;
_textBeforeFilter = ItemsControl.Text;
var textIsEmpty = string.IsNullOrWhiteSpace(ItemsControl.Text);
var itemsViewOriginal = (CollectionView) CollectionViewSource.GetDefaultView(ItemsControl.ItemsSource);
// if the text is empty, then we show everything, otherwise filter based on the text
itemsViewOriginal.Filter = o => textIsEmpty || ((string) o).ContainsIgnoreCase(ItemsControl.Text);
}
注意::EqualsIgnoreCase
和ContainsIgnoreCase
是扩展方法:
public static bool EqualsIgnoreCase(this string source, string value)
{
return source.Equals(value, StringComparison.OrdinalIgnoreCase);
}
public static bool ContainsIgnoreCase(this string source, string value)
{
return source.Contains(value, StringComparison.OrdinalIgnoreCase);
}
答案 6 :(得分:0)
使用ComboBox.Items.Filter来显示适合文本框中写入文本的项目。这是一个示例:
If cmb.Text = "" Then
cmb.Items.Filter = Nothing
Else
Dim T = cmb.Text
cmb.Items.Filter = Nothing
Dim I = cmb.Items.IndexOf(T)
cmb.Items.Filter = Function(x As String)
If x.StartsWith(T) Then Return True
If x.Contains(" " & T) Then Return True
Return False
End Function
If I = -1 Then
cmb.SelectedIndex = -1
cmb.Text = T
Dim txt As TextBox = cmb.Template.FindName("PART_EditableTextBox", cmb)
txt.SelectionStart = T.Length
Else
cmb.SelectedIndex = 0
End If
End If