WinForms | C#|文本框中间自动完成?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WinForms | C#|文本框中间自动完成?相关的知识,希望对你有一定的参考价值。
我有一个自动完成的文本框,如下所示:
txtName.AutoCompleteMode = AutoCompleteMode.Suggest;
txtName.AutoCompleteSource = AutoCompleteSource.CustomSource;
txtName.AutoCompleteCustomSource = namesCollection;
它可以工作,但只在文本框的开头。我希望自动完成功能可以在文本框中的任何位置输入用户输入的任何字词。
答案
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace TubeUploader
{
public class AutoCompleteTextBox : TextBox
{
private ListBox _listBox;
private bool _isAdded;
private String[] _values;
private String _formerValue = String.Empty;
public AutoCompleteTextBox()
{
InitializeComponent();
ResetListBox();
}
private void InitializeComponent()
{
_listBox = new ListBox();
KeyDown += this_KeyDown;
KeyUp += this_KeyUp;
}
private void ShowListBox()
{
if (!_isAdded)
{
Parent.Controls.Add(_listBox);
_listBox.Left = Left;
_listBox.Top = Top + Height;
_isAdded = true;
}
_listBox.Visible = true;
_listBox.BringToFront();
}
private void ResetListBox()
{
_listBox.Visible = false;
}
private void this_KeyUp(object sender, KeyEventArgs e)
{
UpdateListBox();
}
private void this_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Tab:
{
if (_listBox.Visible)
{
InsertWord((String)_listBox.SelectedItem);
ResetListBox();
_formerValue = Text;
}
break;
}
case Keys.Down:
{
if ((_listBox.Visible) && (_listBox.SelectedIndex < _listBox.Items.Count - 1))
_listBox.SelectedIndex++;
break;
}
case Keys.Up:
{
if ((_listBox.Visible) && (_listBox.SelectedIndex > 0))
_listBox.SelectedIndex--;
break;
}
}
}
protected override bool IsInputKey(Keys keyData)
{
switch (keyData)
{
case Keys.Tab:
return true;
default:
return base.IsInputKey(keyData);
}
}
private void UpdateListBox()
{
if (Text == _formerValue) return;
_formerValue = Text;
String word = GetWord();
if (_values != null && word.Length > 0)
{
String[] matches = Array.FindAll(_values,
x => (x.StartsWith(word, StringComparison.OrdinalIgnoreCase) && !SelectedValues.Contains(x)));
if (matches.Length > 0)
{
ShowListBox();
_listBox.Items.Clear();
Array.ForEach(matches, x => _listBox.Items.Add(x));
_listBox.SelectedIndex = 0;
_listBox.Height = 0;
_listBox.Width = 0;
Focus();
using (Graphics graphics = _listBox.CreateGraphics())
{
for (int i = 0; i < _listBox.Items.Count; i++)
{
_listBox.Height += _listBox.GetItemHeight(i);
// it item width is larger than the current one
// set it to the new max item width
// GetItemRectangle does not work for me
// we add a little extra space by using '_'
int itemWidth = (int)graphics.MeasureString(((String)_listBox.Items[i]) + "_", _listBox.Font).Width;
_listBox.Width = (_listBox.Width < itemWidth) ? itemWidth : _listBox.Width;
}
}
}
else
{
ResetListBox();
}
}
else
{
ResetListBox();
}
}
private String GetWord()
{
String text = Text;
int pos = SelectionStart;
int posStart = text.LastIndexOf(' ', (pos < 1) ? 0 : pos - 1);
posStart = (posStart == -1) ? 0 : posStart + 1;
int posEnd = text.IndexOf(' ', pos);
posEnd = (posEnd == -1) ? text.Length : posEnd;
int length = ((posEnd - posStart) < 0) ? 0 : posEnd - posStart;
return text.Substring(posStart, length);
}
private void InsertWord(String newTag)
{
String text = Text;
int pos = SelectionStart;
int posStart = text.LastIndexOf(' ', (pos < 1) ? 0 : pos - 1);
posStart = (posStart == -1) ? 0 : posStart + 1;
int posEnd = text.IndexOf(' ', pos);
String firstPart = text.Substring(0, posStart) + newTag;
String updatedText = firstPart + ((posEnd == -1) ? "" : text.Substring(posEnd, text.Length - posEnd));
Text = updatedText;
SelectionStart = firstPart.Length;
}
public String[] Values
{
get
{
return _values;
}
set
{
_values = value;
}
}
public List<String> SelectedValues
{
get
{
String[] result = Text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
return new List<String>(result);
}
}
}
}
样本用法
using System;
using System.Windows.Forms;
namespace AutoComplete
{
public partial class TestForm : Form
{
private readonly String[] _values = { "one", "two", "three", "tree", "four", "fivee" };
public TestForm()
{
InitializeComponent();
// AutoComplete is our special textbox control on the form
AutoComplete.Values = _values;
}
}
}
另一答案
我对@PaRiMaL RaJ提出的解决方案做了一些更改,因为当文本框位于不够高的UserControl内时,没有显示列表框。基本上,我没有将列表框添加到文本框的父级,而是添加到表单中,然后计算表单中的绝对位置。
public class AutoCompleteTextBox : TextBox
{
private ListBox _listBox;
private bool _isAdded;
private String[] _values;
private String _formerValue = String.Empty;
public AutoCompleteTextBox()
{
InitializeComponent();
ResetListBox();
}
private void InitializeComponent()
{
_listBox = new ListBox();
this.KeyDown += this_KeyDown;
this.KeyUp += this_KeyUp;
}
private void ShowListBox()
{
if (!_isAdded)
{
Form parentForm = this.FindForm(); // new line added
parentForm.Controls.Add(_listBox); // adds it to the form
Point positionOnForm = parentForm.PointToClient(this.Parent.PointToScreen(this.Location)); // absolute position in the form
_listBox.Left = positionOnForm.X;
_listBox.Top = positionOnForm.Y + Height;
_isAdded = true;
}
_listBox.Visible = true;
_listBox.BringToFront();
}
private void ResetListBox()
{
_listBox.Visible = false;
}
private void this_KeyUp(object sender, KeyEventArgs e)
{
UpdateListBox();
}
private void this_KeyDown(object sender, KeyEventArgs e)
以上是关于WinForms | C#|文本框中间自动完成?的主要内容,如果未能解决你的问题,请参考以下文章
C# - Winforms - Combobox - 避免选择更新数据源的第一项