我正在创建一个WPF应用程序,其中包含显示一些数据的ComboBox。我想使用combobox集成的文本搜索。但是问题是,如果用户搜索“ llo”,则列表应显示包含此文本片段的所有项目,例如“ Hallo”,“ Hello”,“ Rollo”...。但是搜索不会返回任何结果,因为该属性无项目的名称以“ llo”开头。有人知道如何实现这一目标吗?
我正在使用MVVM模式。视图绑定到DTO的集合(视图模型的属性),在DTO中,有两个与搜索相关的属性。
<ComboBox
ItemsSource="{Binding Path=Agencies}"
SelectedItem="{Binding Path=SelectedAgency}"
IsTextSearchEnabled="True"
DisplayMemberPath="ComboText"
IsEnabled="{Binding IsReady}"
IsEditable="True"
Grid.Column="0"
Grid.Row="0"
IsTextSearchCaseSensitive="False"
HorizontalAlignment="Stretch">
</ComboBox>
public class Agency
{
public int AgencyNumber { get; set; }
public string Title { get; set; }
public string Name { get; set; }
public string ContactPerson { get; set; }
public string ComboText => $"{this.AgencyNumber}\t{this.Name}";
}
答案 0 :(得分:2)
姜忍者|凯利| Diederik Krols肯定提供了一种不错的多合一解决方案,但是对于简单的用例而言,它可能在繁重的方面有点不足。例如,派生的ComboBox
获得对内部可编辑文本框的引用。正如Diederik指出的,“ “我们需要使用它才能访问选区。” 。可能根本不是必需的。相反,我们可以简单地绑定到Text
属性。
<ComboBox
ItemsSource="{Binding Agencies}"
SelectedItem="{Binding SelectedAgency}"
Text="{Binding SearchText}"
IsTextSearchEnabled="False"
DisplayMemberPath="ComboText"
IsEditable="True"
StaysOpenOnEdit="True"
MinWidth="200" />
另一个可能的改进是公开过滤器,因此开发人员可以轻松地对其进行更改。事实证明,这一切都可以通过viewmodel完成。为了使事情有趣,我选择对Agency
使用ComboText
的{{1}}属性,但对自定义过滤器使用DisplayMemberPath
属性。您当然可以随意调整它。
Name
主要陷阱:
public class MainViewModel : ViewModelBase
{
private readonly ObservableCollection<Agency> _agencies;
public MainViewModel()
{
_agencies = GetAgencies();
Agencies = (CollectionView)new CollectionViewSource { Source = _agencies }.View;
Agencies.Filter = DropDownFilter;
}
#region ComboBox
public CollectionView Agencies { get; }
private Agency selectedAgency;
public Agency SelectedAgency
{
get { return selectedAgency; }
set
{
if (value != null)
{
selectedAgency = value;
OnPropertyChanged();
SearchText = selectedAgency.ComboText;
}
}
}
private string searchText;
public string SearchText
{
get { return searchText; }
set
{
if (value != null)
{
searchText = value;
OnPropertyChanged();
if(searchText != SelectedAgency.ComboText) Agencies.Refresh();
}
}
}
private bool DropDownFilter(object item)
{
var agency = item as Agency;
if (agency == null) return false;
// No filter
if (string.IsNullOrEmpty(SearchText)) return true;
// Filtered prop here is Name != DisplayMemberPath ComboText
return agency.Name.ToLower().Contains(SearchText.ToLower());
}
#endregion ComboBox
private static ObservableCollection<Agency> GetAgencies()
{
var agencies = new ObservableCollection<Agency>
{
new Agency { AgencyNumber = 1, Name = "Foo", Title = "A" },
new Agency { AgencyNumber = 2, Name = "Bar", Title = "C" },
new Agency { AgencyNumber = 3, Name = "Elo", Title = "B" },
new Agency { AgencyNumber = 4, Name = "Baz", Title = "D" },
new Agency { AgencyNumber = 5, Name = "Hello", Title = "E" },
};
return agencies;
}
}
得到相应的更新。SearchText
和自定义过滤器使用了不同的属性。因此,如果我们让过滤器刷新,则过滤后的列表将为空(找不到匹配项),并且选中的项目也将被清除。最后一点,如果您指定DisplayMemberPath
的{{1}},则需要设置ComboBox
而不是ItemTemplate
。
答案 1 :(得分:1)
如果您引用this answer
这应该使您的方向正确。它以我相信您在测试时需要的方式运行。为了完整性,请添加代码:
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;
}
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());
}
}
我用来测试的XAML:
<Window x:Class="CustomComboBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CustomComboBox"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainWindowVM/>
</Window.DataContext>
<Grid>
<local:FilteredComboBox IsEditable="True" x:Name="MyThing" HorizontalAlignment="Center" VerticalAlignment="Center"
Height="25" Width="200"
ItemsSource="{Binding MyThings}"
IsTextSearchEnabled="True"
IsEnabled="True"
StaysOpenOnEdit="True">
</local:FilteredComboBox>
</Grid>
我的ViewModel:
public class MainWindowVM : INotifyPropertyChanged
{
private ObservableCollection<string> _myThings;
public ObservableCollection<string> MyThings { get { return _myThings;} set { _myThings = value; RaisePropertyChanged(); } }
public MainWindowVM()
{
MyThings = new ObservableCollection<string>();
MyThings.Add("Hallo");
MyThings.Add("Jello");
MyThings.Add("Rollo");
MyThings.Add("Hella");
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
如果它不能满足您的确切需求,请确保您可以对其进行编辑。希望这可以帮助。
答案 2 :(得分:0)
使用.Contains方法。
如果字符串包含您作为参数传递的字符串,则此方法将返回true。 否则它将返回false。
if(agency.Title.Contains(combobox.Text))
{
//add this object to the List/Array that contains the object which will be shown in the combobox
}