我只是做這個前幾天使用的代碼的修改版本從這個網站: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