Thursday, 19 April 2012

.Net – Type and Search ListBox

The Type and Search feature in not available out of the box for System.Windows.Forms.CheckedListBox or System.Windows.Forms.ListBox controls in .net. So, I thought to just share a simple trick with which this could be easily managed.

We can take two approaches to achieve this -

  1. Either we can go for creating a Custom control for this.
  2. And if this feature is not required at a general level, we can implement the functionality at the form/user control level.

Now, without talking much on this, I will take you straight away to the implementation -

The below is the code generated by the designer followed by the code to be written to make the type and search work -

partial class FrmTypeAndSearchListBox
  {
    /// <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.lstBxTypeNSearch = new System.Windows.Forms.ListBox();
      this.SuspendLayout();
      // 
      // lstBxTypeNSearch
      // 
      this.lstBxTypeNSearch.FormattingEnabled = true;
      this.lstBxTypeNSearch.Items.AddRange(new object[] {
            "Aashay",
            "Aashish",
            "Abdul-Azeez",
            "Abdul-Baari",
            "Abdullah",
            "Abeer",
            "Achal",
            "Achalendra",
            "Achintya",
            "Achyut",
            "Acyutaraya",
            "Adalarasu",
            "Adarsh",
            "Agrim",
            "Agriya",
            "Ahmad",
            "Ahsan",
            "Aja",
            "Ajaat",
            "Ajaatshatru",
            "Ajamil",
            "Akhil",
            "Akhilesh",
            "Akmal",
            "Akram",
            "Akroor",
            "Akshan",
            "Aloke",
            "Amal",
            "Amalendu",
            "Angad",
            "Angamuthu",
            "Anil",
            "Arivunambi",
            "Avinash",
            "Azhagar",
            "Azhar",
            "Azzam"});
      this.lstBxTypeNSearch.Location = new System.Drawing.Point(52, 47);
      this.lstBxTypeNSearch.Name = "lstBxTypeNSearch";
      this.lstBxTypeNSearch.Size = new System.Drawing.Size(146, 108);
      this.lstBxTypeNSearch.TabIndex = 14;
      this.lstBxTypeNSearch.KeyDown += new System.Windows.Forms.KeyEventHandler(this.lstBxTypeNSearch_KeyDown);
      // 
      // FrmTypeAndSearchListBox
      // 
      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
      this.ClientSize = new System.Drawing.Size(292, 266);
      this.Controls.Add(this.lstBxTypeNSearch);
      this.Name = "FrmTypeAndSearchListBox";
      this.Text = "FrmTypeAndSearchListBox";
      this.ResumeLayout(false);
 
    }
 
    #endregion
 
    private System.Windows.Forms.ListBox lstBxTypeNSearch;
  }


public partial class FrmTypeAndSearchListBox : Form
  {
    DateTime lastKeyDownOnListBox; //DateTime of the last KeyDown on the ListBox.
    StringBuilder listBoxFindString; //String to find in the ListBox.
    int resetListBoxFindStringAfter; //Milliseconds after which the FindString should be reset.
    int findStartIndex; //Starting index from where to find from.
 
    public FrmTypeAndSearchListBox()
    {
      InitializeComponent();
 
      //Initializing with current DateTime.
      lastKeyDownOnListBox = DateTime.Now;
 
      //Initialising the FindString with Empty.
      listBoxFindString = new StringBuilder(string.Empty);
 
      //Setting 250ms as the reset interval.
      resetListBoxFindStringAfter = 250;
 
      //Setting to -1 to start from the begining.
      findStartIndex = -1;
    }
 
    private void lstBxTypeNSearch_KeyDown(object sender, KeyEventArgs e)
    {
      //Just incase the same handler is bound to multiple controls
      //inheriting from ListBox class.
      ListBox senderListBox = sender as ListBox;
 
      if (senderListBox != null)
      {
        switch (e.KeyData.ToString())
        {
          case "Down":
          case "Up":
          case "Right":
          case "Left":
            //Add on the cases for the keys which we do not want to handle.
            break;
          default:
            //Reset the FindString if the gap between two key strokes exceeds the specified limit.
            if (DateTime.Now.Subtract(lastKeyDownOnListBox).Milliseconds > resetListBoxFindStringAfter)
            { listBoxFindString.Clear(); }
 
            //Set the DateTime of the current key stroke.
            lastKeyDownOnListBox = DateTime.Now;
 
            //Prepare the Find String.
            listBoxFindString.Append(e.KeyData.ToString());
 
            //Find the matching index if any.
            int matchedIndex = senderListBox.FindString(listBoxFindString.ToString(),
                                                        findStartIndex);
 
            //Select the Item if a match is found.
            if (matchedIndex > 0)
            { senderListBox.SelectedIndex = matchedIndex; }
            break;
        } 
      }
    }
  }


The above code is well commented. Still, if I have missed anything please feel free to revert with your comment. Hope, this small trick will prove to be helpful.