using System; using System.Windows; using System.Windows.Controls; namespace Developpez.Dotnet.Windows.Behaviors { /// /// Fournit des propriétés attachées pour ajouter des fonctionnalités à un ComboBox /// public class ComboBoxBehavior { #region AutoFilter #region Public attached properties /// /// Renvoie la valeur de la propriété attachée AutoFilter pour l'objet spécifié /// /// L'objet pour lequel on veut obtenir la valeur de la propriété /// true si le filtrage est activé, false sinon [AttachedPropertyBrowsableForType(typeof(ComboBox))] public static bool GetAutoFilter(ComboBox comboBox) { return (bool)comboBox.GetValue(AutoFilterProperty); } /// /// Définit la valeur de la propriété attachée AutoFilter pour l'objet spécifié /// /// L'objet pour lequel on veut définir la valeur de la propriété /// true pour activer le filtrage, false pour le désactiver public static void SetAutoFilter(ComboBox comboBox, bool value) { comboBox.SetValue(AutoFilterProperty, value); } /// /// Identifie la propriété attachée AutoFilter /// public static readonly DependencyProperty AutoFilterProperty = DependencyProperty.RegisterAttached( "AutoFilter", typeof(bool), typeof(ComboBoxBehavior), new UIPropertyMetadata(false, AutoFilterChanged) ); private static void AutoFilterChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { ComboBox combo = sender as ComboBox; if (combo != null) { combo.DoWhenLoaded(() => SetTextChangedHandler(combo)); } } /// /// Renvoie la valeur de la propriété attachée FilterComparisonType pour l'objet spécifié /// /// L'objet pour lequel on veut obtenir la valeur de la propriété /// Le type de comparaison utilisé pour le filtrage [AttachedPropertyBrowsableForType(typeof(ComboBox))] public static StringComparison GetFilterComparisonType(ComboBox comboBox) { return (StringComparison)comboBox.GetValue(FilterComparisonTypeProperty); } /// /// Définit la valeur de la propriété attachée FilterComparisonType pour l'objet spécifié /// /// L'objet pour lequel on veut définir la valeur de la propriété /// Le type de comparaison à utiliser pour le filtrage public static void SetFilterComparisonType(ComboBox comboBox, StringComparison value) { comboBox.SetValue(FilterComparisonTypeProperty, value); } /// /// Identifie la propriété attachée FilterComparisonType /// public static readonly DependencyProperty FilterComparisonTypeProperty = DependencyProperty.RegisterAttached("FilterComparisonType", typeof(StringComparison), typeof(ComboBoxBehavior), new UIPropertyMetadata(StringComparison.CurrentCultureIgnoreCase)); /// /// Renvoie la valeur de la propriété attachée FilterMemberPath pour l'objet spécifié /// /// L'objet pour lequel on veut obtenir la valeur de la propriété /// Le membre utilisé pour le filtrage [AttachedPropertyBrowsableForType(typeof(ComboBox))] public static string GetFilterMemberPath(ComboBox comboBox) { return (string)comboBox.GetValue(FilterMemberPathProperty); } /// /// Définit la valeur de la propriété attachée FilterMemberPath pour l'objet spécifié /// /// L'objet pour lequel on veut définir la valeur de la propriété /// Le membre à utiliser pour le filtrage public static void SetFilterMemberPath(ComboBox comboBox, string value) { comboBox.SetValue(FilterMemberPathProperty, value); } /// /// Identifie la propriété attachée FilterMemberPath /// public static readonly DependencyProperty FilterMemberPathProperty = DependencyProperty.RegisterAttached("FilterMemberPath", typeof(string), typeof(ComboBoxBehavior), new UIPropertyMetadata(null)); #endregion #region Private attached properties private static bool GetGotTextInput(DependencyObject obj) { return (bool)obj.GetValue(GotTextInputPropertyKey.DependencyProperty); } private static void SetGotTextInput(DependencyObject obj, bool value) { obj.SetValue(GotTextInputPropertyKey, value); } private static readonly DependencyPropertyKey GotTextInputPropertyKey = DependencyProperty.RegisterAttachedReadOnly("GotTextInput", typeof(bool), typeof(ComboBoxBehavior), new UIPropertyMetadata(false)); #endregion private static void combo_Loaded(object sender, RoutedEventArgs e) { ComboBox combo = sender as ComboBox; if (combo == null) return; combo.Loaded -= combo_Loaded; if (combo.Template != null) SetTextChangedHandler(combo); } private static void SetTextChangedHandler(ComboBox combo) { TextBox textBox = combo.Template.FindName("PART_EditableTextBox", combo) as TextBox; if (textBox != null) { bool enabled = GetAutoFilter(combo); if (enabled) { textBox.PreviewTextInput += textBox_PreviewTextInput; textBox.TextChanged += textBox_TextChanged; } else { textBox.PreviewTextInput -= textBox_PreviewTextInput; textBox.TextChanged -= textBox_TextChanged; } } } private static void textBox_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e) { TextBox textBox = sender as TextBox; if (textBox == null) return; SetGotTextInput(textBox, true); } private static void textBox_TextChanged(object sender, RoutedEventArgs e) { TextBox textBox = sender as TextBox; if (textBox == null) return; ComboBox combo = textBox.TemplatedParent as ComboBox; if (combo == null) return; if (combo.IsTextSearchEnabled && GetGotTextInput(textBox)) { SetGotTextInput(textBox, false); return; } int selStart = textBox.SelectionStart; int selLength = textBox.SelectionLength; combo.IsDropDownOpen = true; string text = textBox.Text.Substring(0, selStart); // Restore selection that has been changed by opening the dropdown textBox.Select(selStart, selLength); combo.Items.Filter = GetFilter(combo, text); } private static Predicate GetFilter(ComboBox combo, string text) { var comparisonType = GetFilterComparisonType(combo); return value => GetTextFromValue(combo, value).StartsWith(text, comparisonType); } private static string GetTextFromValue(ComboBox combo, object value) { string propertyPath = GetFilterMemberPath(combo); if (propertyPath.IsNullOrEmpty()) propertyPath = combo.DisplayMemberPath; if (!propertyPath.IsNullOrEmpty()) { object r = PropertyPathHelper.GetValue(value, propertyPath); return r != null ? r.ToString() : string.Empty; } else { return value != null ? value.ToString() : string.Empty; } } #endregion } }