using System; using System.Collections; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; using System.Linq; using System.Windows.Media; using System.Windows.Threading; using Tango.Core.ExtensionMethods; namespace Tango.SharedUI.Controls { public class SearchComboBox : ComboBox { private TextBox _textBox; private ListBox _listBox; private ICollectionView _view; public bool IsOpened { get { return (bool)GetValue(IsOpenedProperty); } set { SetValue(IsOpenedProperty, value); } } public static readonly DependencyProperty IsOpenedProperty = DependencyProperty.Register("IsOpened", typeof(bool), typeof(SearchComboBox), new PropertyMetadata(false, (d, e) => (d as SearchComboBox).OnIsOpenedChanged())); public String SearchProperty { get { return (String)GetValue(SearchPropertyProperty); } set { SetValue(SearchPropertyProperty, value); } } public static readonly DependencyProperty SearchPropertyProperty = DependencyProperty.Register("SearchProperty", typeof(String), typeof(SearchComboBox), new PropertyMetadata(null)); public String SearchFilter { get { return (String)GetValue(SearchFilterProperty); } set { SetValue(SearchFilterProperty, value); } } public static readonly DependencyProperty SearchFilterProperty = DependencyProperty.Register("SearchFilter", typeof(String), typeof(SearchComboBox), new PropertyMetadata(null, (d, e) => (d as SearchComboBox).OnFilterChanged())); public IEnumerable ListItemsSource { get { return (IEnumerable)GetValue(ListItemsSourceProperty); } set { SetValue(ListItemsSourceProperty, value); } } public static readonly DependencyProperty ListItemsSourceProperty = DependencyProperty.Register("ListItemsSource", typeof(IEnumerable), typeof(SearchComboBox), new PropertyMetadata(null)); public override void OnApplyTemplate() { base.OnApplyTemplate(); _textBox = GetTemplateChild("txt") as TextBox; _listBox = GetTemplateChild("list") as ListBox; _textBox.PreviewKeyDown += _textBox_KeyDown; _listBox.PreviewKeyDown += _listBox_PreviewKeyDown; _listBox.PreviewMouseLeftButtonUp += _listBox_PreviewMouseLeftButtonUp; KeyboardNavigation.SetDirectionalNavigation(this, KeyboardNavigationMode.Once); } private void _listBox_PreviewKeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Return) { IsOpened = false; SelectedItem = _listBox.SelectedItem; if (SelectedValuePath.IsNotNullOrEmpty() && SelectedItem != null) { SelectedValue = SelectedItem.GetPropertyValueByPath(SelectedValuePath); } } else if (e.Key == Key.Up && _listBox.SelectedIndex == 0) { _textBox.Focus(); Keyboard.Focus(_textBox); } } private void _textBox_KeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Down) { e.Handled = true; FocusList(); } } private void FocusList() { if (!String.IsNullOrWhiteSpace(SearchFilter)) { _listBox.SelectedIndex = 0; } Keyboard.Focus(_listBox); if (_listBox.SelectedIndex != -1) { var container = _listBox.ItemContainerGenerator.ContainerFromIndex(_listBox.SelectedIndex) as UIElement; if (container != null) { container.Focus(); Keyboard.Focus(container); } } _listBox.ScrollIntoView(_listBox.SelectedItem); } private void _listBox_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (_listBox.SelectedItem != null) { if (SelectedItem != _listBox.SelectedItem) { IsOpened = false; SelectedItem = _listBox.SelectedItem; if (SelectedValuePath.IsNotNullOrEmpty() && SelectedItem != null) { SelectedValue = SelectedItem.GetPropertyValueByPath(SelectedValuePath); } } } } private async void OnIsOpenedChanged() { if (IsOpened) { SearchFilter = String.Empty; _listBox.SelectedItem = SelectedItem; _listBox.ScrollIntoView(_listBox.SelectedItem); await Task.Delay(100); _textBox.Focus(); Keyboard.Focus(_textBox); } } protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) { base.OnItemsSourceChanged(oldValue, newValue); if (ItemsSource != null) { ListItemsSource = ItemsSource.Cast().ToList(); _view = CollectionViewSource.GetDefaultView(ListItemsSource); _view.Filter = (x) => { if (String.IsNullOrWhiteSpace(SearchFilter) || SearchFilter == null) return true; if (x != null) { if (!String.IsNullOrWhiteSpace(SearchProperty)) { var prop = x.GetType().GetProperty(SearchProperty, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); if (prop != null) { String propValue = prop.GetValue(x).ToString(); return propValue.ToLower().Contains(SearchFilter.ToLower()); } } return x.ToStringSafe().ToLower().Contains(SearchFilter.ToLower()); } return false; }; } } private void OnFilterChanged() { _view?.Refresh(); if (_listBox != null && _listBox.Items.Count > 0) { _listBox.ScrollIntoView(_listBox.Items[0]); } } protected override void OnPreviewKeyDown(KeyEventArgs e) { //base.OnPreviewKeyDown(e); e.Handled = false; } protected override void OnPreviewKeyUp(KeyEventArgs e) { //base.OnPreviewKeyUp(e); e.Handled = false; } protected override void OnKeyDown(KeyEventArgs e) { //base.OnKeyDown(e); e.Handled = false; } protected override void OnKeyUp(KeyEventArgs e) { //base.OnKeyUp(e); e.Handled = false; } protected override void OnPreviewMouseWheel(MouseWheelEventArgs e) { e.Handled = true; var presentationSource = PresentationSource.FromVisual(_listBox); if (_listBox == null || System.Windows.Input.Keyboard.PrimaryDevice == null || presentationSource == null) return; if (!_listBox.IsFocused) { FocusList(); } if (e.Delta > 0) { _listBox.RaiseEvent( new KeyEventArgs( System.Windows.Input.Keyboard.PrimaryDevice, presentationSource, 0, Key.Up) { RoutedEvent = System.Windows.Input.Keyboard.KeyDownEvent }); _listBox.RaiseEvent( new KeyEventArgs( System.Windows.Input.Keyboard.PrimaryDevice, presentationSource, 0, Key.Up) { RoutedEvent = System.Windows.Input.Keyboard.KeyUpEvent }); } else { _listBox.RaiseEvent( new KeyEventArgs( System.Windows.Input.Keyboard.PrimaryDevice, presentationSource, 0, Key.Down) { RoutedEvent = System.Windows.Input.Keyboard.KeyDownEvent }); _listBox.RaiseEvent( new KeyEventArgs( System.Windows.Input.Keyboard.PrimaryDevice, presentationSource, 0, Key.Down) { RoutedEvent = System.Windows.Input.Keyboard.KeyUpEvent }); } //base.OnPreviewMouseWheel(e); } protected override void OnMouseWheel(MouseWheelEventArgs e) { e.Handled = true; //base.OnMouseWheel(e); } static SearchComboBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(SearchComboBox), new FrameworkPropertyMetadata(typeof(SearchComboBox))); } } }