wpf 自动完成comboxBox
样式:
View Code
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:MCE.Gems.Common.Controls.CustomControls" xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero" > <Style x:Key="ComboBoxFocusVisual"> <Setter Property="Control.Template"> <Setter.Value> <ControlTemplate> <Rectangle Margin="4,4,21,4" SnapsToDevicePixels="true" Stroke="Black" StrokeThickness="1" StrokeDashArray="1 2"/> </ControlTemplate> </Setter.Value> </Setter> </Style> <LinearGradientBrush x:Key="ButtonNormalBackground" EndPoint="0,1" StartPoint="0,0"> <GradientStop Color="#F3F3F3" Offset="0"/> <GradientStop Color="#EBEBEB" Offset="0.5"/> <GradientStop Color="#DDDDDD" Offset="0.5"/> <GradientStop Color="#CDCDCD" Offset="1"/> </LinearGradientBrush> <SolidColorBrush x:Key="ButtonNormalBorder" Color="#FF707070"/> <Geometry x:Key="DownArrowGeometry">M 0 0 L 3.5 4 L 7 0 Z</Geometry> <Style x:Key="ComboBoxReadonlyToggleButton" TargetType="{x:Type ToggleButton}"> <Setter Property="OverridesDefaultStyle" Value="true"/> <Setter Property="IsTabStop" Value="false"/> <Setter Property="Focusable" Value="false"/> <Setter Property="ClickMode" Value="Press"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ToggleButton}"> <Themes:ButtonChrome x:Name="Chrome" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}" SnapsToDevicePixels="true"> <Grid HorizontalAlignment="Right" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}"> <Path x:Name="Arrow" Data="{StaticResource DownArrowGeometry}" Fill="Black" HorizontalAlignment="Center" Margin="3,1,0,0" VerticalAlignment="Center"/> </Grid> </Themes:ButtonChrome> <ControlTemplate.Triggers> <Trigger Property="IsChecked" Value="true"> <Setter Property="RenderPressed" TargetName="Chrome" Value="true"/> </Trigger> <Trigger Property="IsEnabled" Value="false"> <Setter Property="Fill" TargetName="Arrow" Value="#AFAFAF"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <LinearGradientBrush x:Key="TextBoxBorder" EndPoint="0,20" MappingMode="Absolute" StartPoint="0,0"> <GradientStop Color="#ABADB3" Offset="0.05"/> <GradientStop Color="#E2E3EA" Offset="0.07"/> <GradientStop Color="#E3E9EF" Offset="1"/> </LinearGradientBrush> <Style x:Key="ComboBoxEditableTextBox" TargetType="{x:Type TextBox}"> <Setter Property="OverridesDefaultStyle" Value="true"/> <Setter Property="AllowDrop" Value="true"/> <Setter Property="MinWidth" Value="0"/> <Setter Property="MinHeight" Value="0"/> <Setter Property="FocusVisualStyle" Value="{x:Null}"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type TextBox}"> <ScrollViewer x:Name="PART_ContentHost" Background="Transparent" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="ComboBoxToggleButton1" TargetType="{x:Type ToggleButton}"> <Setter Property="OverridesDefaultStyle" Value="true"/> <Setter Property="IsTabStop" Value="false"/> <Setter Property="Focusable" Value="false"/> <Setter Property="ClickMode" Value="Press"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ToggleButton}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition Width="20" /> </Grid.ColumnDefinitions> <Border x:Name="Border" Grid.ColumnSpan="2" CornerRadius="5" Background="#ffffff" BorderBrush="#5d7fad" BorderThickness="0" SnapsToDevicePixels="True" /> <!-- here the Background color #e3edfd used for the righthad side Arrow background --> <Border x:Name="Border1" Grid.Column="0" CornerRadius="5" Margin="1,1,1,1" Background="Transparent" BorderBrush="#5d7fad" BorderThickness="0,0,0,0" SnapsToDevicePixels="True" /> <!-- here the Background color #ffffff used for the Combobx --> <Path x:Name="Arrow" Grid.Column="1" Fill="#5d7fad" HorizontalAlignment="Center" VerticalAlignment="Center" Data="M 0 0 L 4 4 L 8 0 Z" SnapsToDevicePixels="True"/> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsEnabled" Value="False"> <Setter TargetName="Border" Property="Background" Value="#F4F4F4" /> <Setter TargetName="Border1" Property="Background" Value="#F4F4F4" /> <Setter TargetName="Border" Property="BorderBrush" Value="#5d7fad" /> <Setter Property="Foreground" Value="#8f8f8f"/> <Setter TargetName="Arrow" Property="Fill" Value="#9F9F9F" /> </Trigger> <Trigger Property="IsFocused" Value="True"> <Setter TargetName="Border" Property="BorderBrush" Value="#5d7fad" /> <!-- here the Background color #d4dbe3 used for the righthad side Arrow background --> <Setter TargetName="Border" Property="BorderThickness" Value="1" /> </Trigger> <Trigger Property="Validation.HasError" Value="True"> <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/> <Setter Property="Control.BorderBrush" Value="Red" /> <Setter Property="Control.BorderThickness" Value="1" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <ControlTemplate x:Key="ComboBoxEditableTemplate" TargetType="{x:Type local:AutoCompleteComboBox}"> <Border CornerRadius="5" Background="#FFFFFF" BorderBrush="#5d7fad" BorderThickness="1" SnapsToDevicePixels="True" > <Grid x:Name="Placement" SnapsToDevicePixels="true"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom"> <Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=Placement}"> <Border x:Name="DropDownBorder" Background="#FFFFFF" BorderThickness="1" CornerRadius="5" BorderBrush="#5d7fad"> <ScrollViewer Margin="4" SnapsToDevicePixels="True"> <ItemsPresenter KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/> </ScrollViewer> </Border> </Themes:SystemDropShadowChrome> </Popup> <Themes:ListBoxChrome x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderFocused="{TemplateBinding IsKeyboardFocusWithin}"/> <TextBox x:Name="PART_EditableTextBox" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}" Margin="{TemplateBinding Padding}" Style="{StaticResource ComboBoxEditableTextBox}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/> <ToggleButton Grid.Column="1" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ComboBoxToggleButton1}"/> </Grid> </Border> <ControlTemplate.Triggers> <Trigger Property="IsKeyboardFocusWithin" Value="true"> <Setter Property="Foreground" Value="Black"/> </Trigger> <Trigger Property="IsDropDownOpen" Value="true"> <Setter Property="RenderFocused" TargetName="Border" Value="true"/> </Trigger> <Trigger Property="HasItems" Value="false"> <Setter Property="Height" TargetName="DropDownBorder" Value="95"/> </Trigger> <Trigger Property="IsEnabled" Value="false"> <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/> <Setter Property="Background" Value="#FFF4F4F4"/> </Trigger> <Trigger Property="IsGrouping" Value="true"> <Setter Property="ScrollViewer.CanContentScroll" Value="false"/> </Trigger> <Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true"> <Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5"/> <Setter Property="Color" TargetName="Shdw" Value="#71000000"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> <Style TargetType="{x:Type local:AutoCompleteComboBox}"> <Setter Property="FontSize" Value="14"/> <Setter Property="FontWeight" Value="Normal"/> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="VerticalContentAlignment" Value="Center"/> <Setter Property="SnapsToDevicePixels" Value="true"/> <Setter Property="OverridesDefaultStyle" Value="true"/> <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/> <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/> <Setter Property="ScrollViewer.CanContentScroll" Value="true"/> <Setter Property="Height" Value="28"></Setter> <Setter Property="Margin" Value="0"/> <Setter Property="Padding" Value="5,0,5,2"></Setter> <Setter Property="Foreground" Value="#0c223a"/> <Setter Property="FontFamily" Value="Arial" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:AutoCompleteComboBox}"> <Grid x:Name="MainGrid" SnapsToDevicePixels="true"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/> </Grid.ColumnDefinitions> <Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom"> <Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=MainGrid}"> <Border x:Name="DropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"> <ScrollViewer CanContentScroll="true"> <ItemsPresenter KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/> </ScrollViewer> </Border> </Themes:SystemDropShadowChrome> </Popup> <ToggleButton BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ComboBoxReadonlyToggleButton}"/> <ContentPresenter ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" Content="{TemplateBinding SelectionBoxItem}" ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsHitTestVisible="false" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> </Grid> <ControlTemplate.Triggers> <Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true"> <Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5"/> <Setter Property="Color" TargetName="Shdw" Value="#71000000"/> </Trigger> <Trigger Property="HasItems" Value="false"> <Setter Property="Height" TargetName="DropDownBorder" Value="95"/> </Trigger> <Trigger Property="IsEnabled" Value="false"> <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/> <Setter Property="Background" Value="#FFF4F4F4"/> </Trigger> <Trigger Property="IsGrouping" Value="true"> <Setter Property="ScrollViewer.CanContentScroll" Value="false"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> <Setter Property="Validation.ErrorTemplate"> <Setter.Value> <ControlTemplate> <AdornedElementPlaceholder> <Border BorderBrush="Red" BorderThickness="1" CornerRadius="5" SnapsToDevicePixels="True" /> </AdornedElementPlaceholder> </ControlTemplate> </Setter.Value> </Setter> <Style.Triggers> <Trigger Property="IsEditable" Value="true"> <!--<Setter Property="BorderBrush" Value="{StaticResource TextBoxBorder}"/> <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>--> <Setter Property="IsTabStop" Value="false"/> <!--<Setter Property="Padding" Value="3"/>--> <Setter Property="Template" Value="{StaticResource ComboBoxEditableTemplate}"/> </Trigger> </Style.Triggers> </Style> </ResourceDictionary>
三个属性注意
this.StaysOpenOnEdit = true
this.IsEditable = true
this.IsTextSearchEnabled = false//自带的自动完成
第一种:简单的。
View Code
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace GEMS.Windows.Controls.CustomControls { public class SimpleAutoCompleteComboBox : ComboBox { static SimpleAutoCompleteComboBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(SimpleAutoCompleteComboBox), new FrameworkPropertyMetadata(typeof(SimpleAutoCompleteComboBox))); } public SimpleAutoCompleteComboBox() { this.StaysOpenOnEdit = true; this.IsEditable = true; this.IsTextSearchEnabled = true; this.LostFocus += SimpleAutoCompleteComboBox_LostFocus; } public override void OnApplyTemplate() { base.OnApplyTemplate(); //load the text box control if (this.EditableTextBox != null) { // this.EditableTextBox.PreviewKeyDown += new KeyEventHandler(EditableTextBox_PreviewKeyDown); this.EditableTextBox.TextChanged += new TextChangedEventHandler(EditableTextBox_TextChanged); } } //void EditableTextBox_PreviewKeyDown(object sender, KeyEventArgs e) //{ // this.IsKeyEvent = true; //} void EditableTextBox_TextChanged(object sender, TextChangedEventArgs e) { if ( this.EditableTextBox.IsFocused) { this.IsDropDownOpen = true; } } /// <summary> /// Gets the text box in charge of the editable portion of the combo box. /// </summary> protected TextBox EditableTextBox { get { return base.GetTemplateChild("PART_EditableTextBox") as TextBox; } } private string SelectedText { get { if (this.SelectedIndex == -1) return string.Empty; return this.SelectedItem.GetType().GetProperty(this.DisplayMemberPath).GetValue(this.SelectedItem, null).ToString(); } } void SimpleAutoCompleteComboBox_LostFocus(object sender, RoutedEventArgs e) { this.IsDropDownOpen = false; // to prevent misunderstanding that user has entered some information if (this.SelectedIndex == -1) { this.Text = null; this.SelectedItem = null; this.SelectedValue = null; } // syncronize text else this.Text = this.SelectedText; // release timer resources try { this.EditableTextBox.CaretIndex = 0; } catch { } } } }
第2种:下拉只显示关联的
View Code
using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Timers; using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace GEMS.Windows.Controls.CustomControls { public class AutoCompleteComboBox : ComboBox { #region Fields private Timer _interval; private int _maxRecords = 10; private bool _typeAhead = false; private bool IsKeyEvent = false; private bool _clearOnEmpty = true; private int _delay = DEFAULT_DELAY; private const int DEFAULT_DELAY = 500; private ObservableCollection<dynamic> CurrentItems; #endregion #region properties [DefaultValue(DEFAULT_DELAY)] public int Delay { get { return this._delay; } set { this._delay = value; } } public bool ClearOnEmpty { get { return this._clearOnEmpty; } set { this._clearOnEmpty = true; } } /// <summary> /// Gets the text box in charge of the editable portion of the combo box. /// </summary> protected TextBox EditableTextBox { get { return base.GetTemplateChild("PART_EditableTextBox") as TextBox; } } private string SelectedText { get { if (this.SelectedIndex == -1) return string.Empty; return this.SelectedItem.GetType().GetProperty((string.IsNullOrWhiteSpace(this.SearchBy) ? this.DisplayMemberPath : this.SearchBy)).GetValue(this.SelectedItem, null).ToString(); } } #endregion #region DependencyProperty public static readonly DependencyProperty DataSourceProperty = DependencyProperty.Register("DataSource", typeof(IEnumerable), typeof(AutoCompleteComboBox), new PropertyMetadata(null, new PropertyChangedCallback(OnDataSourceChanged))); private static void OnDataSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { AutoCompleteComboBox autoCompleteComboBox = (AutoCompleteComboBox)d; autoCompleteComboBox.ItemsSource = (IEnumerable)e.NewValue; } public IEnumerable DataSource { get { return base.GetValue(AutoCompleteComboBox.DataSourceProperty) as IEnumerable; } set { base.SetValue(AutoCompleteComboBox.DataSourceProperty, value); } } public string SearchBy { get { return (string)GetValue(SearchByProperty); } set { SetValue(SearchByProperty, value); } } // Using a DependencyProperty as the backing store for SearchBy. This enables animation, styling, binding, etc... public static readonly DependencyProperty SearchByProperty = DependencyProperty.Register("SearchBy", typeof(string), typeof(AutoCompleteComboBox), new PropertyMetadata(null)); #endregion public override void OnApplyTemplate() { base.OnApplyTemplate(); //load the text box control if (this.EditableTextBox != null) { this.EditableTextBox.PreviewKeyDown += new KeyEventHandler(EditableTextBox_PreviewKeyDown); this.EditableTextBox.TextChanged += new TextChangedEventHandler(EditableTextBox_TextChanged); } } static AutoCompleteComboBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(typeof(AutoCompleteComboBox))); } public AutoCompleteComboBox() { CurrentItems = new ObservableCollection<dynamic>(); if (!DesignerProperties.GetIsInDesignMode(this)) { this._interval = new Timer(this.Delay); this._interval.AutoReset = true; this._interval.Elapsed += new ElapsedEventHandler(_interval_Elapsed); } this.Loaded += AutoCompleteComboBox_Loaded; this.LostFocus += AutoCompleteComboBox_LostFocus; this.SelectionChanged += AutoCompleteComboBox_SelectionChanged; this.StaysOpenOnEdit = true; this.IsEditable = true; this.IsTextSearchEnabled = false; } private void AutoCompleteComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { this._interval.Stop(); } void AutoCompleteComboBox_LostFocus(object sender, RoutedEventArgs e) { this.IsKeyEvent = false; this.IsDropDownOpen = false; // to prevent misunderstanding that user has entered some information if (this.SelectedIndex == -1) { this.Text = null; this.SelectedItem = null; this.SelectedValue = null; } // syncronize text else this.Text = this.SelectedText; // release timer resources this._interval.Close(); try { this.EditableTextBox.CaretIndex = 0; } catch { } } void AutoCompleteComboBox_Loaded(object sender, RoutedEventArgs e) { this.ItemsSource = this.DataSource; } #region MyRegion void _interval_Elapsed(object sender, ElapsedEventArgs e) { IsKeyEvent = false; // pause the timer this._interval.Stop(); // show the drop down when user starts typing then stops this.Dispatcher.BeginInvoke((Action)delegate { // load from event source this.CurrentItems = new ObservableCollection<dynamic>(); if (this.Text.Length >= 0) { foreach (var entry in DataSource) { string SearchName = entry.GetType().GetProperty((string.IsNullOrWhiteSpace(this.SearchBy) ? this.DisplayMemberPath : this.SearchBy)).GetValue(entry, null).ToString().ToUpper(); if (SearchName.StartsWith(this.Text.ToUpper(), StringComparison.CurrentCultureIgnoreCase)) { CurrentItems.Add(entry); } } this.ItemsSource = CurrentItems; this.IsDropDownOpen = this.HasItems; } else { this.IsDropDownOpen = false; } }, System.Windows.Threading.DispatcherPriority.ApplicationIdle); } #endregion #region EditableTextBoxEvent void EditableTextBox_PreviewKeyDown(object sender, KeyEventArgs e) { this.IsKeyEvent = true; } void EditableTextBox_TextChanged(object sender, TextChangedEventArgs e) { if ( this.EditableTextBox.IsFocused) { if (this.ClearOnEmpty && string.IsNullOrEmpty(this.EditableTextBox.Text.Trim())) this.ItemsSource = this.DataSource; else if (IsKeyEvent) this.ResetTimer(); } } protected void ResetTimer() { this._interval.Stop(); this._interval.Start(); } #endregion } }