using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; using System.Drawing; public class EditListBox : ListBox { public EditListBox() { this.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.CommitOnLeave = true; this.CommitOnEnter = true; this.AllowDelete = true; this.ConfirmDelete = true; this.ConfirmDeleteText = "Are you sure you want to delete the selected items?"; // Create a new TextBox and set some properties textBox = new TextBox(); textBox.Visible = false; textBox.AcceptsReturn = false; textBox.AcceptsTab = true; textBox.BorderStyle = System.Windows.Forms.BorderStyle.None; textBox.BackColor = this.BackColor; textBox.ForeColor = this.ForeColor; textBox.Font = this.Font; textBox.Leave += TextBox_Leave; textBox.KeyDown += TextBox_KeyDown; this.Controls.Add(textBox); } #region Private Fields private TextBox textBox; private int editingIndex = -1; #endregion #region Constants private const int WM_VSCROLL = 0x115; private const int WM_MOUSEWHEEL = 0x20A; #endregion #region Events public event EventHandler ItemEdited; #endregion #region Properties /// /// Gets or sets a value determining whether the editing text is committed when the editing textbox loses focus. /// public bool CommitOnLeave { get; set; } /// /// Gets or sets a value determining whether the editing text is committed when the user presses the Enter or Return key. /// public bool CommitOnEnter { get; set; } /// /// Gets or sets a value determining whether the user can delete items using the Delete key. /// public bool AllowDelete { get; set; } /// /// Gets or sets a value determining whether a confirmation MessageBox is shown when the Delete key is pressed to delete items. /// public bool ConfirmDelete { get; set; } /// /// Gets or sets the text displayed in the confirmation MessageBox when the Delete key is pressed to delete items. /// public string ConfirmDeleteText { get; set; } #endregion #region Overrides protected override void OnFontChanged(EventArgs e) { base.OnFontChanged(e); // Match the font of the editing TextBox textBox.Font = this.Font; } protected override void OnBackColorChanged(EventArgs e) { base.OnBackColorChanged(e); // Match the BackColor of the editing TextBox textBox.BackColor = this.BackColor; } protected override void OnForeColorChanged(EventArgs e) { base.OnForeColorChanged(e); // Match the ForeColor of the editing TextBox textBox.ForeColor = this.ForeColor; } protected override void Dispose(bool disposing) { base.Dispose(disposing); // Dispose the TextBox if (disposing) textBox.Dispose(); } protected override void OnDrawItem(DrawItemEventArgs e) { base.OnDrawItem(e); // Draw the default background e.DrawBackground(); if (e.Index > -1 && e.Index < this.Items.Count) { object item = this.Items[e.Index]; string text = this.GetItemText(item); Rectangle itemRect = this.GetItemRectangle(e.Index); Color textColor; // Check if the item is selected // if so, the background and textcolor is different if ((e.State & DrawItemState.Selected) == DrawItemState.Selected) { textColor = SystemColors.HighlightText; using (var b = new SolidBrush(SystemColors.Highlight)) { e.Graphics.FillRectangle(b, itemRect); } } else { textColor = this.ForeColor; } // Draw the text using (var b = new SolidBrush(textColor)) { e.Graphics.DrawString(text, this.Font, b, itemRect.Location); } } // Draw the focus rectangle e.DrawFocusRectangle(); } protected override void OnMouseDoubleClick(MouseEventArgs e) { base.OnMouseDoubleClick(e); if (e.Button == System.Windows.Forms.MouseButtons.Left) { // If the left button is double-clicked, check if the double-click occured in an Item Rectangle for (int i = 0; i < this.Items.Count; i++) { Rectangle itemRect = this.GetItemRectangle(i); if (itemRect.Contains(e.Location)) { // The double-click occured in this item, so display the TextBox in the right place, match its Text to the item's Text and give it focus. editingIndex = i; textBox.Bounds = itemRect; textBox.Visible = true; textBox.Text = this.GetItemText(this.Items[i]); textBox.SelectionStart = 0; textBox.SelectionLength = textBox.TextLength; textBox.Focus(); break; } } } } protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); if (e.KeyCode == Keys.Delete && this.AllowDelete) { this.DeleteSelectedItems(); } } protected override void WndProc(ref Message m) { base.WndProc(ref m); // When the user starts scrolling, commit the edit if (m.Msg == WM_VSCROLL || m.Msg == WM_MOUSEWHEEL) { if (this.CommitOnLeave) this.CommitTextBox(); else this.RollBackTextBox(); } } #endregion #region Textbox Events private void TextBox_Leave(object sender, EventArgs e) { if (this.CommitOnLeave) this.CommitTextBox(); else this.RollBackTextBox(); } private void TextBox_KeyDown(object sender, KeyEventArgs e) { if (this.CommitOnEnter) { if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Return) { this.CommitTextBox(); } } } #endregion #region Methods /// /// Deletes all selected items, after possibly showing a confirmation MessageBox. /// public void DeleteSelectedItems() { // Display confirmation MessageBox if required bool canDelete = true; if (this.ConfirmDelete) canDelete = (MessageBox.Show(this.ConfirmDeleteText, "Delete", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes); if (canDelete) { // Delete all selected items while (this.SelectedItems.Count > 0) { this.Items.Remove(this.SelectedItem); } } } private void CommitTextBox() { if (editingIndex > -1 && editingIndex < this.Items.Count) { object item = this.Items[editingIndex]; if (item.GetType() != typeof(string)) { // Item is not a string, so let the user handle the change of the text via the ItemEdited event EditEventArgs e = new EditEventArgs(item, textBox.Text); if (this.ItemEdited != null) { this.ItemEdited(this, e); this.Items[editingIndex] = e.Item; } else { // User is not handling the event, so it won't work! throw new Exception("You should handle the ItemEdited event to allow for item editing if the items are not strings."); } } else { // Item is a string, so we can handle it manually: this.Items[editingIndex] = textBox.Text; } textBox.Visible = false; } } private void RollBackTextBox() { textBox.Clear(); textBox.Visible = false; } #endregion #region Nested Classes public class EditEventArgs : EventArgs { /// /// The item that is being edited. /// public object Item { get; set; } /// /// The text that the user entered as the new item text. /// public string NewText { get; set; } public EditEventArgs(object item, string newText) { this.Item = item; this.NewText = newText; } } #endregion }