Google提示文本框(自动完成)
开发一个文本框以记住上次输入的最后x个条目的最佳方法是什么。这是一个用C#编写的独立应用程序。
解决方案
回答
这实际上是相当容易的,特别是在显示其中的"自动完成"部分方面。就记住最后的x个条目而言,我们只需要确定一个或者多个特定事件(将其视为已完成的条目)并将该条目写到列表中即可...精确的。
TextBox类具有我们需要的以下3个属性:
- AutoCompleteCustomSource
- 自动完成模式
- 自动完成源
将"自动完成模式"设置为" SuggestAppend",将"自动完成源"设置为" CustomSource"。
然后在运行时,每次创建新条目时,请使用AutoCompleteStringCollection的Add()方法将该条目添加到列表中(并根据需要弹出所有旧条目)。实际上,只要我们已初始化它,就可以直接在TextBox的AutoCompleteCustomSource属性上执行此操作。
现在,每次我们在文本框中键入内容时,都会提示以前的输入内容:)
请参阅本文以获得更完整的示例:http://www.c-sharpcorner.com/UploadFile/mahesh/AutoCompletion02012006113508AM/AutoCompletion.aspx
AutoComplete还具有一些内置功能,例如FileSystem和URL(尽管它只处理键入IE的内容...)
回答
@Ethan
我忘了一个事实,那就是我们要保存它,所以这不是每个会话唯一的事情:P但是,是的,我们是完全正确的。
这很容易做到,特别是因为它只是基本字符串,只需将AutoCompleteCustomSource的内容从TextBox写到文本文件中的不同行上即可。
我花了几分钟时间,所以我写了一个完整的代码示例……我以前总是尝试显示代码,但是没有时间。无论如何,这就是整个过程(减去设计者代码)。
namespace AutoComplete { public partial class Main : Form { //so you don't have to address "txtMain.AutoCompleteCustomSource" every time AutoCompleteStringCollection acsc; public Main() { InitializeComponent(); //Set to use a Custom source txtMain.AutoCompleteSource = AutoCompleteSource.CustomSource; //Set to show drop down *and* append current suggestion to end txtMain.AutoCompleteMode = AutoCompleteMode.SuggestAppend; //Init string collection. acsc = new AutoCompleteStringCollection(); //Set txtMain's AutoComplete Source to acsc txtMain.AutoCompleteCustomSource = acsc; } private void txtMain_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { //Only keep 10 AutoComplete strings if (acsc.Count < 10) { //Add to collection acsc.Add(txtMain.Text); } else { //remove oldest acsc.RemoveAt(0); //Add to collection acsc.Add(txtMain.Text); } } } private void Main_FormClosed(object sender, FormClosedEventArgs e) { //open stream to AutoComplete save file StreamWriter sw = new StreamWriter("AutoComplete.acs"); //Write AutoCompleteStringCollection to stream foreach (string s in acsc) sw.WriteLine(s); //Flush to file sw.Flush(); //Clean up sw.Close(); sw.Dispose(); } private void Main_Load(object sender, EventArgs e) { //open stream to AutoComplete save file StreamReader sr = new StreamReader("AutoComplete.acs"); //initial read string line = sr.ReadLine(); //loop until end while (line != null) { //add to AutoCompleteStringCollection acsc.Add(line); //read again line = sr.ReadLine(); } //Clean up sr.Close(); sr.Dispose(); } } }
此代码将完全按原样工作,我们只需要使用名为txtMain的TextBox创建GUI,并将KeyDown,Closed和Load事件连接到TextBox和Main窗体。
还要注意,对于这个示例,为了简化起见,我只是选择检测按下Enter键作为触发,以将字符串保存到集合中。根据需求,可能会有更多/不同的事件会更好。
同样,用于填充集合的模型也不是很"智能"。当集合达到10的限制时,它仅删除最旧的字符串。这可能不理想,但适用于该示例。我们可能需要某种评级系统(尤其是如果我们确实希望它像Google一样)
最后要注意的是,这些建议实际上将按照它们在集合中的顺序显示。如果出于某些原因我们希望它们以不同的方式显示,请根据需要对列表进行排序。
希望对我们有所帮助!
回答
我将完成列表存储在注册表中。
我使用的代码如下。它可重复使用,分三个步骤:
- 用任何我们使用的代码替换此代码中的名称空间和类名。
- 在窗体的Load事件上调用FillFormFromRegistry(),在Closing事件上调用SaveFormToRegistry。
- 将其编译到项目中。
我们需要使用以下两个属性来装饰装配:[assembly:AssemblyProduct(" ...")]
和`[assembly:AssemblyCompany(" ...")]]。 (这些属性通常是在Visual Studio中创建的项目中自动设置的,因此我不认为这是一个步骤。)
以这种方式管理状态是完全自动的,并且对用户透明。
我们可以使用相同的模式为WPF或者WinForms应用程序存储任何状态。像文本框,复选框,下拉菜单的状态。此外,我们还可以在用户下次运行应用程序时真正方便地存储/恢复窗口的大小,该窗口在关闭窗口的位置和大小相同。我们可以存储应用程序已运行的次数。很多可能性。
namespace Ionic.ExampleCode { public partial class NameOfYourForm { private void SaveFormToRegistry() { if (AppCuKey != null) { // the completion list var converted = _completions.ToList().ConvertAll(x => x.XmlEscapeIexcl()); string completionString = String.Join("?", converted.ToArray()); AppCuKey.SetValue(_rvn_Completions, completionString); } } private void FillFormFromRegistry() { if (!stateLoaded) { if (AppCuKey != null) { // get the MRU list of .... whatever _completions = new System.Windows.Forms.AutoCompleteStringCollection(); string c = (string)AppCuKey.GetValue(_rvn_Completions, ""); if (!String.IsNullOrEmpty(c)) { string[] items = c.Split('?'); if (items != null && items.Length > 0) { //_completions.AddRange(items); foreach (string item in items) _completions.Add(item.XmlUnescapeIexcl()); } } // Can also store/retrieve items in the registry for // - textbox contents // - checkbox state // - splitter state // - and so on // stateLoaded = true; } } } private Microsoft.Win32.RegistryKey AppCuKey { get { if (_appCuKey == null) { _appCuKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(AppRegistryPath, true); if (_appCuKey == null) _appCuKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(AppRegistryPath); } return _appCuKey; } set { _appCuKey = null; } } private string _appRegistryPath; private string AppRegistryPath { get { if (_appRegistryPath == null) { // Use a registry path that depends on the assembly attributes, // that are presumed to be elsewhere. Example: // // [assembly: AssemblyCompany("Dino Chiesa")] // [assembly: AssemblyProduct("XPathVisualizer")] var a = System.Reflection.Assembly.GetExecutingAssembly(); object[] attr = a.GetCustomAttributes(typeof(System.Reflection.AssemblyProductAttribute), true); var p = attr[0] as System.Reflection.AssemblyProductAttribute; attr = a.GetCustomAttributes(typeof(System.Reflection.AssemblyCompanyAttribute), true); var c = attr[0] as System.Reflection.AssemblyCompanyAttribute; _appRegistryPath = String.Format("Software\{0}\{1}", p.Product, c.Company); } return _appRegistryPath; } } private Microsoft.Win32.RegistryKey _appCuKey; private string _rvn_Completions = "Completions"; private readonly int _MaxMruListSize = 14; private System.Windows.Forms.AutoCompleteStringCollection _completions; private bool stateLoaded; } public static class Extensions { public static string XmlEscapeIexcl(this String s) { while (s.Contains("?")) { s = s.Replace("?", "¡"); } return s; } public static string XmlUnescapeIexcl(this String s) { while (s.Contains("¡")) { s = s.Replace("¡", "?"); } return s; } public static List<String> ToList(this System.Windows.Forms.AutoCompleteStringCollection coll) { var list = new List<String>(); foreach (string item in coll) { list.Add(item); } return list; } } }
有些人回避使用注册表来存储状态,但是我发现它确实非常容易和方便。如果愿意,我们可以很容易地构建一个安装程序,以在卸载时删除所有注册表项。