C # Автозаполнение - PullRequest
       17

C # Автозаполнение

43 голосов
/ 28 апреля 2009

Я пытаюсь добавить функцию автозаполнения в текстовое поле, результаты поступают из базы данных. Они приходят в формате

[001] Last, First Middle

В настоящее время вы должны ввести [001] ..., чтобы получить записи для отображения. Итак, проблема в том, что я хочу, чтобы он завершился, даже если я сначала наберу имя . Так что, если запись была

[001] Смит, Джон Д

если я начал печатать Джона, то эта запись должна появиться в результатах автоматического завершения.

В настоящее время код выглядит примерно так:

AutoCompleteStringCollection acsc = new AutoCompleteStringCollection();
txtBox1.AutoCompleteCustomSource = acsc;
txtBox1.AutoCompleteMode = AutoCompleteMode.Suggest; 
txtBox1.AutoCompleteSource = AutoCompleteSource.CustomSource; 

....

if (results.Rows.Count > 0)
    for (int i = 0; i < results.Rows.Count && i < 10; i++) 
    {
        row = results.Rows[i];
        acsc.Add(row["Details"].ToString());
    }
}

results - это набор данных, содержащий результаты запроса

Запрос представляет собой простой поисковый запрос, использующий оператор like. Правильные результаты возвращаются, если мы не используем автозаполнение и просто помещаем результаты в массив.

Любой совет?

EDIT:

Вот запрос, который возвращает результаты

SELECT Name from view_customers where Details LIKE '{0}'

С {0} в качестве заполнителя для искомой строки.

Ответы [ 6 ]

47 голосов
/ 29 апреля 2009

Существующая функция автозаполнения поддерживает поиск только по префиксу. Кажется, нет достойного способа изменить поведение.

Некоторые люди реализовали свои собственные функции автозаполнения, переопределив событие OnTextChanged. Это, вероятно, ваш лучший выбор.

Например, вы можете добавить ListBox чуть ниже TextBox и установить для видимости по умолчанию значение false. Затем вы можете использовать событие OnTextChanged TextBox и событие SelectedIndexChanged ListBox для отображения и выбора элементов.

Это, кажется, работает довольно хорошо как элементарный пример:

public Form1()
{
    InitializeComponent();


    acsc = new AutoCompleteStringCollection();
    textBox1.AutoCompleteCustomSource = acsc;
    textBox1.AutoCompleteMode = AutoCompleteMode.None;
    textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;
}

private void button1_Click(object sender, EventArgs e)
{
    acsc.Add("[001] some kind of item");
    acsc.Add("[002] some other item");
    acsc.Add("[003] an orange");
    acsc.Add("[004] i like pickles");
}

void textBox1_TextChanged(object sender, System.EventArgs e)
{
    listBox1.Items.Clear();
    if (textBox1.Text.Length == 0)
    {
    hideResults();
    return;
    }

    foreach (String s in textBox1.AutoCompleteCustomSource)
    {
    if (s.Contains(textBox1.Text))
    {
        Console.WriteLine("Found text in: " + s);
        listBox1.Items.Add(s);
        listBox1.Visible = true;
    }
    }
}

void listBox1_SelectedIndexChanged(object sender, System.EventArgs e)
{
    textBox1.Text = listBox1.Items[listBox1.SelectedIndex].ToString();
    hideResults();
}

void listBox1_LostFocus(object sender, System.EventArgs e)
{
    hideResults();
}

void hideResults()
{
    listBox1.Visible = false;
}

Вы можете сделать гораздо больше без особых усилий: добавить текст в текстовое поле, захватить дополнительные команды клавиатуры и т. Д.

6 голосов
/ 28 апреля 2009

Если вы решили использовать запрос, основанный на пользовательском вводе, убедитесь, что вы используете SqlParameters, чтобы избежать атак SQL-инъекций

SqlCommand sqlCommand = new SqlCommand();
sqlCommand.CommandText = "SELECT Name from view_customers where Details LIKE '%" + @SearchParam + "%'";
sqlCommand.Parameters.AddWithValue("@SearchParam", searchParam);
4 голосов
/ 27 июня 2013

Вот реализация, которая наследует класс элемента управления ComboBox, а не заменяет весь комбинированный список новым элементом управления. Он отображает свой собственный раскрывающийся список при вводе в текстовом поле, но нажатие, чтобы отобразить раскрывающийся список, обрабатывается, как и раньше (т. Е. Не с этим кодом). Таким образом, вы получаете надлежащий контроль и выглядите.

Пожалуйста, используйте его, измените его и отредактируйте ответ, если вы хотите улучшить его!

class ComboListMatcher : ComboBox, IMessageFilter
{
    private Control ComboParentForm; // Or use type "Form" 
    private ListBox listBoxChild;
    private int IgnoreTextChange;
    private bool MsgFilterActive = false;

    public ComboListMatcher()
    {
        // Set up all the events we need to handle
        TextChanged += ComboListMatcher_TextChanged;
        SelectionChangeCommitted += ComboListMatcher_SelectionChangeCommitted;
        LostFocus += ComboListMatcher_LostFocus;
        MouseDown += ComboListMatcher_MouseDown;
        HandleDestroyed += ComboListMatcher_HandleDestroyed;
    }

    void ComboListMatcher_HandleDestroyed(object sender, EventArgs e)
    {
        if (MsgFilterActive)
            Application.RemoveMessageFilter(this);
    }

    ~ComboListMatcher()
    {
    }

    private void ComboListMatcher_MouseDown(object sender, MouseEventArgs e)
    {
        HideTheList();
    }

    void ComboListMatcher_LostFocus(object sender, EventArgs e)
    {
        if (listBoxChild != null && !listBoxChild.Focused)
            HideTheList();
    }

    void ComboListMatcher_SelectionChangeCommitted(object sender, EventArgs e)
    {
        IgnoreTextChange++;
    }

    void InitListControl()
    {
        if (listBoxChild == null)
        {
            // Find parent - or keep going up until you find the parent form
            ComboParentForm = this.Parent;

            if (ComboParentForm != null)
            {
                // Setup a messaage filter so we can listen to the keyboard
                if (!MsgFilterActive)
                {
                    Application.AddMessageFilter(this);
                    MsgFilterActive = true;
                }

                listBoxChild = listBoxChild = new ListBox();
                listBoxChild.Visible = false;
                listBoxChild.Click += listBox1_Click;
                ComboParentForm.Controls.Add(listBoxChild);
                ComboParentForm.Controls.SetChildIndex(listBoxChild, 0); // Put it at the front
            }
        }
    }


    void ComboListMatcher_TextChanged(object sender, EventArgs e)
    {
        if (IgnoreTextChange > 0)
        {
            IgnoreTextChange = 0;
            return;
        }

        InitListControl();

        if (listBoxChild == null)
            return;

        string SearchText = this.Text;

        listBoxChild.Items.Clear();

        // Don't show the list when nothing has been typed
        if (!string.IsNullOrEmpty(SearchText))
        {
            foreach (string Item in this.Items)
            {
                if (Item != null && Item.Contains(SearchText, StringComparison.CurrentCultureIgnoreCase))
                    listBoxChild.Items.Add(Item);
            }
        }

        if (listBoxChild.Items.Count > 0)
        {
            Point PutItHere = new Point(this.Left, this.Bottom);
            Control TheControlToMove = this;

            PutItHere = this.Parent.PointToScreen(PutItHere);

            TheControlToMove = listBoxChild;
            PutItHere = ComboParentForm.PointToClient(PutItHere);

            TheControlToMove.Show();
            TheControlToMove.Left = PutItHere.X;
            TheControlToMove.Top = PutItHere.Y;
            TheControlToMove.Width = this.Width;

            int TotalItemHeight = listBoxChild.ItemHeight * (listBoxChild.Items.Count + 1);
            TheControlToMove.Height = Math.Min(ComboParentForm.ClientSize.Height - TheControlToMove.Top, TotalItemHeight);
        }
        else
            HideTheList();
    }

    /// <summary>
    /// Copy the selection from the list-box into the combo box
    /// </summary>
    private void CopySelection()
    {
        if (listBoxChild.SelectedItem != null)
        {
            this.SelectedItem = listBoxChild.SelectedItem;
            HideTheList();
            this.SelectAll();
        }
    }

    private void listBox1_Click(object sender, EventArgs e)
    {
        var ThisList = sender as ListBox;

        if (ThisList != null)
        {
            // Copy selection to the combo box
            CopySelection();
        }
    }

    private void HideTheList()
    {
        if (listBoxChild != null)
            listBoxChild.Hide();
    }

    public bool PreFilterMessage(ref Message m)
    {
        if (m.Msg == 0x201) // Mouse click: WM_LBUTTONDOWN
        {
            var Pos = new Point((int)(m.LParam.ToInt32() & 0xFFFF), (int)(m.LParam.ToInt32() >> 16));

            var Ctrl = Control.FromHandle(m.HWnd);
            if (Ctrl != null)
            {
                // Convert the point into our parent control's coordinates ...
                Pos = ComboParentForm.PointToClient(Ctrl.PointToScreen(Pos));

                // ... because we need to hide the list if user clicks on something other than the list-box
                if (ComboParentForm != null)
                {
                    if (listBoxChild != null &&
                        (Pos.X < listBoxChild.Left || Pos.X > listBoxChild.Right || Pos.Y < listBoxChild.Top || Pos.Y > listBoxChild.Bottom))
                    {
                        this.HideTheList();
                    }
                }
            }
        }
        else if (m.Msg == 0x100) // WM_KEYDOWN
        {
            if (listBoxChild != null && listBoxChild.Visible)
            {
                switch (m.WParam.ToInt32())
                {
                    case 0x1B: // Escape key
                        this.HideTheList();
                        return true;

                    case 0x26: // up key
                    case 0x28: // right key
                        // Change selection
                        int NewIx = listBoxChild.SelectedIndex + ((m.WParam.ToInt32() == 0x26) ? -1 : 1);

                        // Keep the index valid!
                        if (NewIx >= 0 && NewIx < listBoxChild.Items.Count)
                            listBoxChild.SelectedIndex = NewIx;
                        return true;

                    case 0x0D: // return (use the currently selected item)
                        CopySelection();
                        return true;
                }
            }
        }

        return false;
    }
}
1 голос
/ 11 февраля 2017

ЭТО ДАЕТ ВАМ АВТОКОМПЛЕКТНОЕ ПОВЕДЕНИЕ, КОТОРОЕ ВЫ ИЩЕТЕ.

Прикрепленный пример представляет собой полную рабочую форму, просто нужен ваш источник данных и имена связанных столбцов.

using System;
using System.Data;
using System.Windows.Forms;

public partial class frmTestAutocomplete : Form
{

    private DataTable maoCompleteList; //the data table from your data source
    private string msDisplayCol = "name"; //displayed text
    private string msIDcol = "id"; //ID or primary key

    public frmTestAutocomplete(DataTable aoCompleteList, string sDisplayCol, string sIDcol)
    {
        InitializeComponent();

        maoCompleteList = aoCompleteList
        maoCompleteList.CaseSensitive = false; //turn off case sensitivity for searching
        msDisplayCol = sDisplayCol;
        msIDcol = sIDcol;
    }

    private void frmTestAutocomplete_Load(object sender, EventArgs e)
    {

            testCombo.DisplayMember = msDisplayCol;
            testCombo.ValueMember = msIDcol; 
            testCombo.DataSource = maoCompleteList;
            testCombo.SelectedIndexChanged += testCombo_SelectedIndexChanged;
            testCombo.KeyUp += testCombo_KeyUp; 

    }


    private void testCombo_KeyUp(object sender, KeyEventArgs e)
    {
        //use keyUp event, as text changed traps too many other evengts.

        ComboBox oBox = (ComboBox)sender;
        string sBoxText = oBox.Text;

        DataRow[] oFilteredRows = maoCompleteList.Select(MC_DISPLAY_COL + " Like '%" + sBoxText + "%'");

        DataTable oFilteredDT = oFilteredRows.Length > 0
                                ? oFilteredRows.CopyToDataTable()
                                : maoCompleteList;

        //NOW THAT WE HAVE OUR FILTERED LIST, WE NEED TO RE-BIND IT WIHOUT CHANGING THE TEXT IN THE ComboBox.

        //1).UNREGISTER THE SELECTED EVENT BEFORE RE-BINDING, b/c IT TRIGGERS ON BIND.
        testCombo.SelectedIndexChanged -= testCombo_SelectedIndexChanged; //don't select on typing.
        oBox.DataSource = oFilteredDT; //2).rebind to filtered list.
        testCombo.SelectedIndexChanged += testCombo_SelectedIndexChanged;


        //3).show the user the new filtered list.
        oBox.DroppedDown = true; //do this before repainting the text, as it changes the dropdown text.

        //4).binding data source erases text, so now we need to put the user's text back,
        oBox.Text = sBoxText;
        oBox.SelectionStart = sBoxText.Length; //5). need to put the user's cursor back where it was.


    }

    private void testCombo_SelectedIndexChanged(object sender, EventArgs e)
    {

        ComboBox oBox = (ComboBox)sender;

        if (oBox.SelectedValue != null)
        {
            MessageBox.Show(string.Format(@"Item #{0} was selected.", oBox.SelectedValue));
        }
    }
}

//=====================================================================================================
//      code from frmTestAutocomplete.Designer.cs
//=====================================================================================================
partial class frmTestAutocomplete
{
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        this.testCombo = new System.Windows.Forms.ComboBox();
        this.SuspendLayout();
        // 
        // testCombo
        // 
        this.testCombo.FormattingEnabled = true;
        this.testCombo.Location = new System.Drawing.Point(27, 51);
        this.testCombo.Name = "testCombo";
        this.testCombo.Size = new System.Drawing.Size(224, 21);
        this.testCombo.TabIndex = 0;
        // 
        // frmTestAutocomplete
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(292, 273);
        this.Controls.Add(this.testCombo);
        this.Name = "frmTestAutocomplete";
        this.Text = "frmTestAutocomplete";
        this.Load += new System.EventHandler(this.frmTestAutocomplete_Load);
        this.ResumeLayout(false);

    }

    #endregion

    private System.Windows.Forms.ComboBox testCombo;
}
0 голосов
/ 16 августа 2016

Два метода были успешными в элементе управления textBox autoComplete с SQL:

но вы должны сделать следующее:

a- создать новый проект

b - добавить класс компонента в проект и удалить component1.designer "в соответствии с именем, которое вы даете классу компонента"

c- скачать "Загрузить образец - 144,82 КБ" и откройте его и откройте класс AutoCompleteTextbox из AutoCompleteTextbox.cs
d- выберите все, как показано на рисунке, и скопируйте его в текущий класс компонентов

http://i.stack.imgur.com/oSqCa.png

e- Final - запустить проект и остановить просмотр нового AutoCompleteTextbox в toolBox.

Теперь вы можете добавить следующие два метода, которые вы можете использовать с ними SQL

1- в Form_Load

private void Form1_Load(object sender, EventArgs e)
  {            
   SqlConnection cn = new SqlConnection(@"server=.;database=My_dataBase;integrated security=true");
   SqlDataAdapter da = new SqlDataAdapter(@"SELECT [MyColumn] FROM [my_table]", cn);
   DataTable dt = new DataTable();
   da.Fill(dt);

   List<string> myList = new List<string>();
    foreach (DataRow row in dt.Rows)
       {
          myList.Add((string)row[0]);
       }

   autoCompleteTextbox1.AutoCompleteList = myList;
    }  

2- в TextChanged Event

 private void autoCompleteTextbox_TextChanged(object sender, EventArgs e)
        {           
         SqlConnection cn = new SqlConnection(@"server=.;database=My_dataBase;integrated security=true");
         SqlDataAdapter da = new SqlDataAdapter(@"SELECT [MyColumn] FROM [my_table]", cn);
         DataTable dt = new DataTable();
         da.Fill(dt);

     List<string> myList = new List<string>();
      foreach (DataRow row in dt.Rows)
        {
          myList.Add((string)row[0]);
        }

   autoCompleteTextbox2.AutoCompleteList = myList;

    }
0 голосов
/ 28 апреля 2009

Если вы выполняете этот запрос (при замене {0} на введенную строку), вам может потребоваться:

SELECT Name from view_customers where Details LIKE '%{0}%'

LIKE все еще нужен символ % ... И да, вы должны использовать параметры, а не доверять вводу пользователя:)

Кроме того, вы, похоже, возвращаете столбец Name, но запрашиваете столбец Details. Поэтому, если кто-то введет «Джон Смит», если его нет в столбце Details, вы не получите желаемого обратно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...