web-dev-qa-db-ja.com

WPFのオートコンボコンボボックス

WPF C#のオートコンボコンボボックスが必要です。私はいくつかのアプローチを試しましたが、何も機能しません。たとえば、私はコンボボックスを試しました:

<ComboBox  Width="200"
      IsEditable="True"
      ItemsSource="{Binding Names}"
      IsTextSearchEnabled="True"
      HorizontalAlignment="Left"/>

Namesは文字列のリストです:ピータージョン、ジョン、ジョンドー、キャシー、ハワード、ジョンリチャーズなど

名前を入力すると、 John the comboboxが展開され、表示されるはずです

  • ジョン
  • ジョン・ドウ
  • ジョン・リチャーズ
  • ピーター・ジョン

しかし、それはうまくいきません。どうやってやるの?

7
Struct

何度もいじった後、私はなんとかして完全に機能する解決策にたどり着きました。 (またはそう思われます。)

手順1. XAMLマークアップを変更する

ComboBoxを次のように変更する必要があります。

_<ComboBox
    ...
    IsTextSearchEnabled="False"
    ...
    PreviewTextInput="PreviewTextInput_EnhanceComboSearch"
    PreviewKeyUp="PreviewKeyUp_EnhanceComboSearch"
    DataObject.Pasting="Pasting_EnhanceComboSearch" />
_

すなわち。 todisableデフォルトのテキスト検索、およびユーザーの追加、削除、テキストを貼り付けます。

ステップ2. ComboBoxの内部TextBoxを取得するヘルパー関数を追加する(WPFのため)

_PreviewTextInput_EnhanceComboSearch_と_Pasting_EnhanceComboSearch_がまったく機能するためには、ComboBoxのキャレットにアクセスする必要があります。残念ながら、これを行うには、ビジュアルツリー( Matt Hamiltonへのヒント )をトラバースする必要があります。拡張メソッドでそれを行うことができますが、私はPageクラスで静的メソッドを使用しました:

_public static T GetChildOfType<T>(DependencyObject depObj) where T : DependencyObject
{
    if (depObj == null) return null;

    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
    {
        var child = VisualTreeHelper.GetChild(depObj, i);

        var result = (child as T) ?? GetChildOfType<T>(child);
        if (result != null) return result;
    }
    return null;
}
_

ステップ3.イベントハンドラーを実装する

使用したことに注意してください

_s => s.IndexOf(e.Text, StringComparison.InvariantCultureIgnoreCase) != -1 
_

これは、大文字と小文字を区別しないs => s.Contains(e.Text)チェックと同等です。必要に応じてその部分を変更することを忘れないでください。

ステップ3.a ComboBox内でユーザーが入力したときに検索をトリガーする

PreviewTextInputハンドラーが実行されると、ComboBox内の_.Text_プロパティには、変更されたbeforeからのテキストが含まれます。したがって、キャレットを取得するには、GetChildOfTypeメソッドを使用してComboBoxの内部TextBoxを取得する必要があるため、入力した文字が正確にどこに挿入されたかがわかります。

_private void PreviewTextInput_EnhanceComboSearch(object sender, TextCompositionEventArgs e)
{
    ComboBox cmb = (ComboBox)sender;

    cmb.IsDropDownOpen = true;

    if (!string.IsNullOrEmpty(cmb.Text))
    {
        string fullText = cmb.Text.Insert(GetChildOfType<TextBox>(cmb).CaretIndex, e.Text);
        cmb.ItemsSource = Names.Where(s => s.IndexOf(fullText, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
    }
    else if (!string.IsNullOrEmpty(e.Text))
    {
        cmb.ItemsSource = Names.Where(s => s.IndexOf(e.Text, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
    }
    else
    {
        cmb.ItemsSource = Names;
    }
}
_

ステップ3.bユーザーがComboBoxに貼り付けたときに検索をトリガーする

_DataObject.Pasting_ハンドラーはPreviewTextInputハンドラーと同様に動作するため、キャレットが再度必要になります。

_private void Pasting_EnhanceComboSearch(object sender, DataObjectPastingEventArgs e)
{
    ComboBox cmb = (ComboBox)sender;

    cmb.IsDropDownOpen = true;

    string pastedText = (string)e.DataObject.GetData(typeof(string));
    string fullText = cmb.Text.Insert(GetChildOfType<TextBox>(cmb).CaretIndex, pastedText);

    if (!string.IsNullOrEmpty(fullText))
    {
        cmb.ItemsSource = Names.Where(s => s.IndexOf(fullText, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
    }
    else
    {
        cmb.ItemsSource = Names;
    }
}
_

ステップ3.cユーザーがComboBox内のテキストを削除するときに検索をトリガーします(WPFのためスペースも押します)

これは、ユーザーがDeleteまたはBackspaceを押すとトリガーされます。

また、スペースもPreviewTextInputによって無視されるため、この例では、「John Doe」と「John Richards」から「John」を除外することは困難です。

_private void PreviewKeyUp_EnhanceComboSearch(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Back || e.Key == Key.Delete)
    {
        ComboBox cmb = (ComboBox)sender;

        cmb.IsDropDownOpen = true;

        if (!string.IsNullOrEmpty(cmb.Text))
        {
            cmb.ItemsSource = Names.Where(s => s.IndexOf(cmb.Text, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
        }
        else
        {
            cmb.ItemsSource = Names;
        }
    }
}
_

...そしておそらくそれで十分でしょう。

7
Dragomok

doTextFilterにPreviewTextInputイベントを使用して、次のようにドロップダウンを表示します。

private void ComboBox_TextInput_1(object sender, TextCompositionEventArgs e)
    {           
        cmbperson.IsDropDownOpen = true;
        cmbperson.ItemsSource = DataBase.Persons.Where(p => p.Name.Contains(e.Text)).ToList();
    }
6
Mahdi Hasanpour

コンボボックスの代わりに、オートコンプリート用に作成されたコントロールを使用することをお勧めします。多くの企業がそのようなコントロールを提供しています this 1つは無料であり、良いと考えられています。

3
Moti Azu

これは私のために働く実装です:

<ComboBox
    Name="ItemsControl"
    IsEditable="True"
    KeyUp="OnItemsControlKeyUp"

最後にフィルターが適用されてからテキストが変更されているかどうかを確認します(英数字以外のキーが押されたときにフィルター処理を回避するため)。

private string _textBeforeFilter;

private void OnItemsControlKeyUp(object sender, KeyEventArgs e)
{
    var arrowKey = e.Key >= Key.Left && e.Key <= Key.Down;

    // if arrow key (navigating) or the text hasn't changed, then a we don't need to filter
    if (arrowKey || ItemsControl.Text.EqualsIgnoreCase(_textBeforeFilter)) return;

    _textBeforeFilter = ItemsControl.Text;

    var textIsEmpty = string.IsNullOrWhiteSpace(ItemsControl.Text);

    var itemsViewOriginal = (CollectionView) CollectionViewSource.GetDefaultView(ItemsControl.ItemsSource);
    // if the text is empty, then we show everything, otherwise filter based on the text 
    itemsViewOriginal.Filter = o => textIsEmpty || ((string) o).ContainsIgnoreCase(ItemsControl.Text);
}

注:EqualsIgnoreCaseおよびContainsIgnoreCaseは拡張メソッドです。

public static bool EqualsIgnoreCase(this string source, string value)
{
    return source.Equals(value, StringComparison.OrdinalIgnoreCase);
}

public static bool ContainsIgnoreCase(this string source, string value)
{
    return source.Contains(value, StringComparison.OrdinalIgnoreCase);
}
0
Shahzad B

WPFに役立つオートコンプリートを作成しました。以下のリンクに従ってgithubにアクセスしてください: https://github.com/rhpontes/AutocompleteWpf

お役に立てば幸いです。

0
Robson Pontes

XAMLでは、IsEditable = Trueを設定し、PreviewKeyDownイベントのハンドラーを追加する必要があります。

private void ComboBox_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        var cmb = (ComboBox)sender;
        cmb.IsDropDownOpen = true;
        var textbox = cmb.Template.FindName("PART_EditableTextBox", cmb) as TextBox;
        cmb.ItemsSource = CurrentStorage.Organisations.Where(p => string.IsNullOrEmpty(cmb.Text) || p.Name.ToLower().Contains(textbox.Text.ToLower())).ToList();
    }
0