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