2010-01-04 40 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()); 
     } 
    } 
}