If I want to Filter the items in the DropDown list I simply bind to the FilterString property.
This example can even handle wildcards.
public class ComboBoxExt : ComboBox { #region Filter #region Implementation IList<object> _displayedItems; /// <summary> /// refreshes the filtering of the combobox according to the string in /// <see cref="FilterString"/> /// </summary> private void RefreshFilter() { var source = ItemsSource as ICollectionView; if (source == null) source = Items as ICollectionView; if (source != null) { if (_displayedItems == null) _displayedItems = new ObservableCollection<object>(); _displayedItems.Clear(); source.Filter = new Predicate<object>(Filter); if (_displayedItems.Count > 0) { var focusedElement = Keyboard.FocusedElement; SelectedItem = _displayedItems[0]; if (focusedElement != null) focusedElement.Focus(); } } } /// <summary> /// method that gets called by the predicate in the ICollectionView.Filter that /// filters the entries in the dropdown /// according to the string in <see cref="FilterString"/> /// </summary> /// <param name="obj"></param> /// <returns></returns> private bool Filter(object obj) { // no filter is set if (string.IsNullOrEmpty(FilterString)) return true; // no filterpath is set if (string.IsNullOrEmpty(FilterMemberPath)) return true; // get the value from the property out of the bound object var value = GetValueFromBindingPath(obj, FilterMemberPath); if (value == null) return false; // // matching filterstring and value using regex found on: // http://www.codeproject.com/Articles/11556/Converting-Wildcards-to-Regexes // // accept sql and dos Wildcards // convert all sql wildcards to dos wildcards var filter = FilterString.Replace("%", "*").Replace("_", "?"); // convert all wildcards to regex wildcards and add a * to search all prepending chars filter = "^" + Regex.Escape(filter).Replace("\\*", ".*").Replace("\\?", ".") + ".*$"; // match the value with the regex filter bool isFiltered = Regex.IsMatch(value.ToString(), filter, RegexOptions.IgnoreCase); if (isFiltered) _displayedItems.Add(obj); return isFiltered; } #endregion #region DependcyProperties #region FilterMemberPath // // the filtermemeberpath is used to get the property that the filter should be used on // /// <summary> /// gets or set the object path to the property that is filtered on the bound object /// </summary> public string FilterMemberPath { get { return (string)GetValue(FilterMemberPathProperty); } set { SetValue(FilterMemberPathProperty, value); } } /// <summary> /// the dependencyproperty for the <see cref="FilterMemberPath"/> property /// </summary> public static readonly DependencyProperty FilterMemberPathProperty = DependencyProperty.Register("FilterMemberPath", typeof(string), typeof(ComboBoxExt), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnFilterMemberPathPropertyChanged))); private static void OnFilterMemberPathPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) { } #endregion #region FilterString /// <summary> /// gets or sets the string that is used for filtering the combobox /// </summary> public string FilterString { get { return (string)GetValue(FilterStringProperty); } set { SetValue(FilterStringProperty, value); } } /// <summary> /// dependencyproperty for the <see cref="FilterString"/> property /// </summary> public static readonly DependencyProperty FilterStringProperty = DependencyProperty.Register( "FilterString", typeof(string), typeof(ComboBoxExt), new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsParentMeasure, new PropertyChangedCallback(OnFilterStringPropertyChanged))); private static void OnFilterStringPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) { var dt = sender as ComboBoxExt; if (dt == null) return; dt.RefreshFilter(); } #endregion #endregion #endregion #region Value from Binding / Path // // we create a virtual binding and bind to the property from the // path that was set in the FiltermemberPath property // with the binding we can easily retreave the object that is // contained behind the property // // tests with over 5000 items have shown // that this has no impact on performance // /// <summary> /// gets the object/value from a property from the object according to the path /// </summary> /// <param name="obj"></param> /// <param name="propertyPath"></param> /// <returns></returns> public static object GetValueFromBindingPath(object obj, string propertyPath) { Binding binding = new Binding(propertyPath); binding.Mode = BindingMode.OneTime; binding.Source = obj; BindingOperations.SetBinding(_dummy, Dummy.ValueProperty, binding); return _dummy.GetValue(Dummy.ValueProperty); } // dummy object for getting the value from a path on a object private static readonly Dummy _dummy = new Dummy(); private class Dummy : DependencyObject { public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(Dummy), new UIPropertyMetadata(null)); } #endregion }
Keine Kommentare:
Kommentar veröffentlichen