我只是做这个前几天使用的代码的修改版本从这个网站:Credit where credit is due
我完整的代码如下:
using System.Collections;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
namespace MyControls
{
public class FilteredComboBox : ComboBox
{
private string oldFilter = string.Empty;
private string currentFilter = string.Empty;
protected TextBox EditableTextBox => GetTemplateChild("PART_EditableTextBox") as TextBox;
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
if (newValue != null)
{
var view = CollectionViewSource.GetDefaultView(newValue);
view.Filter += FilterItem;
}
if (oldValue != null)
{
var view = CollectionViewSource.GetDefaultView(oldValue);
if (view != null) view.Filter -= FilterItem;
}
base.OnItemsSourceChanged(oldValue, newValue);
}
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
switch (e.Key)
{
case Key.Tab:
case Key.Enter:
IsDropDownOpen = false;
break;
case Key.Escape:
IsDropDownOpen = false;
SelectedIndex = -1;
Text = currentFilter;
break;
default:
if (e.Key == Key.Down) IsDropDownOpen = true;
base.OnPreviewKeyDown(e);
break;
}
// Cache text
oldFilter = Text;
}
protected override void OnKeyUp(KeyEventArgs e)
{
switch (e.Key)
{
case Key.Up:
case Key.Down:
break;
case Key.Tab:
case Key.Enter:
ClearFilter();
break;
default:
if (Text != oldFilter)
{
RefreshFilter();
IsDropDownOpen = true;
EditableTextBox.SelectionStart = int.MaxValue;
}
base.OnKeyUp(e);
currentFilter = Text;
break;
}
}
protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
ClearFilter();
var temp = SelectedIndex;
SelectedIndex = -1;
Text = string.Empty;
SelectedIndex = temp;
base.OnPreviewLostKeyboardFocus(e);
}
private void RefreshFilter()
{
if (ItemsSource == null) return;
var view = CollectionViewSource.GetDefaultView(ItemsSource);
view.Refresh();
}
private void ClearFilter()
{
currentFilter = string.Empty;
RefreshFilter();
}
private bool FilterItem(object value)
{
if (value == null) return false;
if (Text.Length == 0) return true;
return value.ToString().ToLower().Contains(Text.ToLower());
}
}
}
而且WPF应该像这样:
<MyControls:FilteredComboBox ItemsSource="{Binding MyItemsSource}"
SelectedItem="{Binding MySelectedItem}"
DisplayMemberPath="Name"
IsEditable="True"
IsTextSearchEnabled="False"
StaysOpenOnEdit="True">
<MyControls:FilteredComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel VirtualizationMode="Recycling" />
</ItemsPanelTemplate>
</MyControls:FilteredComboBox.ItemsPanel>
</MyControls:FilteredComboBox>
这里有几件事要注意。您会注意到FilterItem实现在对象上执行ToString()。这意味着你想要显示的对象的属性应该返回到你的object.ToString()实现中。 (或者是一个字符串的话)。换句话说像这样:
public class Customer
{
public string Name { get; set; }
public string Address { get; set; }
public string PhoneNumber { get; set; }
public override string ToString()
{
return Name;
}
}
如果这不适合你的需求我想你可以得到的DisplayMemberPath和使用反射的值来获得属性设置为使用它的工作,但那会更慢,所以我不会推荐这样做,除非必要。
此外,此实现不会停止用户在ComboBox的TextBox部分中键入任何他们喜欢的内容。如果他们在那里输入了一些愚蠢的东西,SelectedItem将会恢复为NULL,所以准备在你的代码中处理它。
此外,如果你有很多项目我会强烈建议使用VirtualizingStackPanel像我上面的例子,因为它使在加载时间相当差
这看起来很酷,也许我还没有正确实施它。如果他们不想使用搜索功能,我需要整个列表可用。如何从整个列表中选择? 此外,虽然我可以将ItemsSource绑定到您的控件(使用上面示例中的绑定),但我实际上无法选择列表中的某个东西......文本区域始终为空。最后,这种控制是一组控制的核心。我需要的SelectedItem在相邻的控制,如:的SelectedValue =“{结合SelectedItem.SomeOtherProperty,的ElementName = DiagnosisComboBox ...}” /> – Bob 2010-01-04 21:36:02
请查看文章再次,它提供了你需要的一切。如果要使整个列表可用,请清除“MaxCompletions”属性,并让过滤器谓词始终返回true。要从列表中实际选择某些内容,您需要将'Binding'属性设置为列表中数据对象的属性之一。 – 2010-01-04 22:07:04