2010-01-04 55 views
0

我似乎无法找到一种实现过滤文本输入到WPF组合框中的项目列表的直接方法。
通过将IsTextSearchEnabled设置为true,comboBox下拉将跳转到任何第一个匹配项目。我需要的是将列表过滤为与文本字符串匹配的任何内容(例如,如果我关注我的组合框并键入'abc',我希望看到ItemsSource集合中以(或包含) )'abc'作为下拉列表的成员)。基于文本输入的WPF组合框的动态过滤器

我怀疑它是否有差别,但我的显示项目模板复杂类型的属性:

<ComboBox x:Name="DiagnosisComboBox" Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="3" 
      ItemsSource="{Binding Path = ApacheDxList, 
           UpdateSourceTrigger=PropertyChanged, 
           Mode=OneWay}" 
      IsTextSearchEnabled="True" 
      ItemTemplate="{StaticResource DxDescriptionTemplate}" 
      SelectedValue="{Binding Path = SelectedEncounterDetails.Diagnosis, 
            Mode=TwoWay, 
            UpdateSourceTrigger=PropertyChanged}"/> 

感谢。

回答

-1

这听起来像你真正在寻找的东西类似于自动完成的文本框,它提供了类似于组合框弹出框的弹出框中的完成建议。

您可能会发现这CodeProject上的文章有用:

A Reusable WPF Autocomplete TextBox

+0

这看起来很酷,也许我还没有正确实施它。如果他们不想使用搜索功能,我需要整个列表可用。如何从整个列表中选择? 此外,虽然我可以将ItemsSource绑定到您的控件(使用上面示例中的绑定),但我实际上无法选择列表中的某个东西......文本区域始终为空。最后,这种控制是一组控制的核心。我需要的SelectedItem在相邻的控制,如:的SelectedValue =“{结合SelectedItem.SomeOtherProperty,的ElementName = DiagnosisComboBox ...}” /> – Bob 2010-01-04 21:36:02

+1

请查看文章再次,它提供了你需要的一切。如果要使整个列表可用,请清除“MaxCompletions”属性,并让过滤器谓词始终返回true。要从列表中实际选择某些内容,您需要将'Binding'属性设置为列表中数据对象的属性之一。 – 2010-01-04 22:07:04

3

我只是做这个前几天使用的代码的修改版本从这个网站:Credit where credit is due

我完整的代码如下:

using System.Collections; 
using System.Windows.Controls; 
using System.Windows.Data; 
using System.Windows.Input; 

    namespace MyControls 
    { 
     public class FilteredComboBox : ComboBox 
     { 
      private string oldFilter = string.Empty; 

      private string currentFilter = string.Empty; 

      protected TextBox EditableTextBox => GetTemplateChild("PART_EditableTextBox") as TextBox; 


      protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) 
      { 
       if (newValue != null) 
       { 
        var view = CollectionViewSource.GetDefaultView(newValue); 
        view.Filter += FilterItem; 
       } 

       if (oldValue != null) 
       { 
        var view = CollectionViewSource.GetDefaultView(oldValue); 
        if (view != null) view.Filter -= FilterItem; 
       } 

       base.OnItemsSourceChanged(oldValue, newValue); 
      } 

      protected override void OnPreviewKeyDown(KeyEventArgs e) 
      { 
       switch (e.Key) 
       { 
        case Key.Tab: 
        case Key.Enter: 
         IsDropDownOpen = false; 
         break; 
        case Key.Escape: 
         IsDropDownOpen = false; 
         SelectedIndex = -1; 
         Text = currentFilter; 
         break; 
        default: 
         if (e.Key == Key.Down) IsDropDownOpen = true; 

         base.OnPreviewKeyDown(e); 
         break; 
       } 

       // Cache text 
       oldFilter = Text; 
      } 

      protected override void OnKeyUp(KeyEventArgs e) 
      { 
       switch (e.Key) 
       { 
        case Key.Up: 
        case Key.Down: 
         break; 
        case Key.Tab: 
        case Key.Enter: 

         ClearFilter(); 
         break; 
        default: 
         if (Text != oldFilter) 
         { 
          RefreshFilter(); 
          IsDropDownOpen = true; 

          EditableTextBox.SelectionStart = int.MaxValue; 
         } 

         base.OnKeyUp(e); 
         currentFilter = Text; 
         break; 
       } 
      } 

      protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e) 
      { 
       ClearFilter(); 
       var temp = SelectedIndex; 
       SelectedIndex = -1; 
       Text = string.Empty; 
       SelectedIndex = temp; 
       base.OnPreviewLostKeyboardFocus(e); 
      } 

      private void RefreshFilter() 
      { 
       if (ItemsSource == null) return; 

       var view = CollectionViewSource.GetDefaultView(ItemsSource); 
       view.Refresh(); 
      } 

      private void ClearFilter() 
      { 
       currentFilter = string.Empty; 
       RefreshFilter(); 
      } 

      private bool FilterItem(object value) 
      { 
       if (value == null) return false; 
       if (Text.Length == 0) return true; 

       return value.ToString().ToLower().Contains(Text.ToLower()); 
      } 
     } 
    } 

而且WPF应该像这样:

<MyControls:FilteredComboBox ItemsSource="{Binding MyItemsSource}" 
    SelectedItem="{Binding MySelectedItem}" 
    DisplayMemberPath="Name" 
    IsEditable="True" 
    IsTextSearchEnabled="False" 
    StaysOpenOnEdit="True"> 

    <MyControls:FilteredComboBox.ItemsPanel> 
     <ItemsPanelTemplate> 
      <VirtualizingStackPanel VirtualizationMode="Recycling" /> 
     </ItemsPanelTemplate> 
    </MyControls:FilteredComboBox.ItemsPanel> 
</MyControls:FilteredComboBox> 

这里有几件事要注意。您会注意到FilterItem实现在对象上执行ToString()。这意味着你想要显示的对象的属性应该返回到你的object.ToString()实现中。 (或者是一个字符串的话)。换句话说像这样:

public class Customer 
{ 
    public string Name { get; set; } 
    public string Address { get; set; } 
    public string PhoneNumber { get; set; } 

    public override string ToString() 
    { 
     return Name; 
    } 
} 

如果这不适合你的需求我想你可以得到的DisplayMemberPath和使用反射的值来获得属性设置为使用它的工作,但那会更慢,所以我不会推荐这样做,除非必要。

此外,此实现不会停止用户在ComboBox的TextBox部分中键入任何他们喜欢的内容。如果他们在那里输入了一些愚蠢的东西,SelectedItem将会恢复为NULL,所以准备在你的代码中处理它。

此外,如果你有很多项目我会强烈建议使用VirtualizingStackPanel像我上面的例子,因为它使在加载时间相当差

0

凯利的答案是伟大的。但是,如果您在列表中选择一个项目(突出显示输入文本),然后按BackSpace,则输入文本将恢复为所选项目,并且组合框的SelectedItem属性仍然是您之前选择的项目。

下面是修复错误并添加在输入文本匹配时自动选择项目的功能的代码。

using System.Collections; 
using System.Diagnostics; 
using System.Windows.Controls; 
using System.Windows.Data; 
using System.Windows.Input; 

namespace MyControls 
{ 
    public class FilteredComboBox : ComboBox 
    { 
     private string oldFilter = string.Empty; 

     private string currentFilter = string.Empty; 

     protected TextBox EditableTextBox => GetTemplateChild("PART_EditableTextBox") as TextBox; 


     protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) 
     { 
      if (newValue != null) 
      { 
       var view = CollectionViewSource.GetDefaultView(newValue); 
       view.Filter += FilterItem; 
      } 

      if (oldValue != null) 
      { 
       var view = CollectionViewSource.GetDefaultView(oldValue); 
       if (view != null) view.Filter -= FilterItem; 
      } 

      base.OnItemsSourceChanged(oldValue, newValue); 
     } 

     protected override void OnPreviewKeyDown(KeyEventArgs e) 
     { 
      switch (e.Key) 
      { 
       case Key.Tab: 
       case Key.Enter: 
        IsDropDownOpen = false; 
        break; 
       case Key.Escape: 
        IsDropDownOpen = false; 
        SelectedIndex = -1; 
        Text = currentFilter; 
        break; 
       default: 
        if (e.Key == Key.Down) IsDropDownOpen = true; 

        base.OnPreviewKeyDown(e); 
        break; 
      } 

      // Cache text 
      oldFilter = Text; 
     } 

     protected override void OnKeyUp(KeyEventArgs e) 
     { 
      switch (e.Key) 
      { 
       case Key.Up: 
       case Key.Down: 
        break; 
       case Key.Tab: 
       case Key.Enter: 

        ClearFilter(); 
        break; 
       default:           
        if (Text != oldFilter) 
        { 
         var temp = Text; 
         RefreshFilter(); //RefreshFilter will change Text property 
         Text = temp; 

         if (SelectedIndex != -1 && Text != Items[SelectedIndex].ToString()) 
         { 
          SelectedIndex = -1; //Clear selection. This line will also clear Text property 
          Text = temp; 
         } 


         IsDropDownOpen = true; 

         EditableTextBox.SelectionStart = int.MaxValue; 
        } 

        //automatically select the item when the input text matches it 
        for (int i = 0; i < Items.Count; i++) 
        { 
         if (Text == Items[i].ToString()) 
          SelectedIndex = i; 
        } 

        base.OnKeyUp(e);      
        currentFilter = Text;      
        break; 
      } 
     } 

     protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e) 
     { 
      ClearFilter(); 
      var temp = SelectedIndex; 
      SelectedIndex = -1; 
      Text = string.Empty; 
      SelectedIndex = temp; 
      base.OnPreviewLostKeyboardFocus(e); 
     } 

     private void RefreshFilter() 
     { 
      if (ItemsSource == null) return; 

      var view = CollectionViewSource.GetDefaultView(ItemsSource); 
      view.Refresh(); 
     } 

     private void ClearFilter() 
     { 
      currentFilter = string.Empty; 
      RefreshFilter(); 
     } 

     private bool FilterItem(object value) 
     { 
      if (value == null) return false; 
      if (Text.Length == 0) return true; 

      return value.ToString().ToLower().Contains(Text.ToLower()); 
     } 
    } 
}