Автозаполнение С#
Я пытаюсь добавить функцию автозаполнения в текстовое поле, результаты поступают из базы данных. Они представлены в формате
[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());
}
}
результат - это набор данных, содержащий результаты запроса
Запрос представляет собой простой поисковый запрос, используя оператор типа. Правильные результаты возвращаются, если мы не используем автозаполнение и просто бросаем результаты в массив.
Любые советы?
EDIT:
Вот запрос, который возвращает результаты
SELECT Name from view_customers where Details LIKE '{0}'
С {0} является заполнителем искомой строки.
Ответы
Ответ 1
Существующие функции AutoComplete поддерживают поиск только по префиксу. Кажется, что нет достойного способа отменить поведение.
Некоторые люди реализовали свои собственные функции автозаполнения, переопределив событие 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;
}
Там вы можете многое сделать без особых усилий: добавить текст в текстовое поле, захватить дополнительные команды клавиатуры и т.д.
Ответ 2
Если вы решили использовать запрос, основанный на пользовательском вводе, убедитесь, что вы используете SqlParameters, чтобы избежать атак SQL Injection
SqlCommand sqlCommand = new SqlCommand();
sqlCommand.CommandText = "SELECT Name from view_customers where Details LIKE '%" + @SearchParam + "%'";
sqlCommand.Parameters.AddWithValue("@SearchParam", searchParam);
Ответ 3
Здесь реализована реализация, которая наследует класс управления 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 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;
}
}
Ответ 4
ЭТО ДАЕТ ВАМ АВТОКОМПЛЕКТНОЕ ПОВЕДЕНИЕ, КОТОРОЕ ВЫ ИЩЕТЕ.
Прикрепленный пример представляет собой полную рабочую форму, просто нужен ваш источник данных и связанные имена столбцов.
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 text back,
oBox.Text = sBoxText;
oBox.SelectionStart = sBoxText.Length; //5). need to put the user 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;
}
Ответ 5
Если вы выполняете этот запрос (при замене {0}
на введенную строку) вам может потребоваться:
SELECT Name from view_customers where Details LIKE '%{0}%'
LIKE
по-прежнему нужен символ %
... И да, вы должны использовать параметры, а не доверять пользовательскому вводу:)
Кроме того, вы возвращаете столбец Name
, но запрашиваете столбцы Details
. Поэтому, если кто-то набирает "Джон Смит", если это не в столбце Details
, вы не получите то, что хотите.
Ответ 6
Два метода были успешными в элементе управления автозаполнением textBox с SQL:
но вы должны сделать следующее:
a- создать новый проект
b- добавить класс компонента для проекта и удалить component1.designer "в соответствии с именем, которое вы указываете классу компонента"
c- скачать "Скачать образец - 144.82 KB"
и откройте его и откройте класс AutoCompleteTextbox из AutoCompleteTextbox.cs
d- выберите все, как показано на изображении, и скопируйте его в текущий класс компонентов
http://i.stack.imgur.com/oSqCa.png
e- Заключительный проект и остановитесь, чтобы просмотреть новый автокомплектTextbox в 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
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;
}