vbAccelerator - Contents of code file: CancellableEditPopupCS_EditableListBox.cs

using System;
using System.Drawing;
using System.Windows.Forms;

namespace vbAccelerator.Components.Controls
{
   /// <summary>
   /// A derived ListBox control allows items to be edited in place
   /// </summary>
   public class EditableListBox : ListBox
   {
      private bool readOnly = false;
      private int lastSelectedIndex = -1;
      private PopupCancelNotifier popupNotifier = null;
      private TextBox txtPopupEdit = null;


      /// <summary>
      /// Gets/sets whether the ListBox can be edited or not.
      /// </summary>
      public bool ReadOnly
      {
         get
         {
            return this.readOnly;
         }
         set
         {
            this.readOnly = value;
         }
      }

      /// <summary>
      /// Starts editing the specified item.
      /// </summary>
      /// <param name="index">0-based index of item to edit</param>
      public void StartItemEdit(int index)
      {
         // Stop editing if we are:
         EndTextEdit(false);

         // Select the item:
         this.lastSelectedIndex = index;
         this.SelectedIndex = index;
         // is the item visible?

         // start editing:
         StartTextEdit();
      }

      /// <summary>
      /// Ends editing if we are editing, setting whether to commit
      /// changes or not.
      /// </summary>
      public void EndItemEdit(bool commit)
      {
         if (txtPopupEdit.Visible)
         {
            EndTextEdit(commit);
         }
      }

      /// <summary>
      /// Raises the <see cref="SelectedIndexChanged"/>
      /// event and performs processing to ensure items can be edited
      /// </summary>
      /// <param name="e">Not used</param>
      protected override void OnSelectedIndexChanged(EventArgs e)
      {
         base.OnSelectedIndexChanged(e);
         lastSelectedIndex = base.SelectedIndex;
      }

      /// <summary>
      /// Raises the <see cref="MouseDown"/>
      /// event and starts editing in place if appropriate.
      /// </summary>
      /// <param name="e">Mouse information associated with the event.</param>
      protected override void OnMouseDown(MouseEventArgs e)
      {
         base.OnMouseDown(e);

         if ((!ReadOnly) && (e.Button == MouseButtons.Left))
         {
            if (!txtPopupEdit.Visible)
            {
               if ((base.SelectedIndex == lastSelectedIndex) &&
                (lastSelectedIndex > -1))
               {
                  StartTextEdit();
               }
            }
         }
      }

      /// <summary>
      /// Responds to the <see cref="PopupCancelNotifier"/> <see
       cref="PopupCancel"/>
      /// event.
      /// </summary>
      /// <param name="sender">PopupCancelNotifier object which sent the
       event</param>
      /// <param name="e"><see cref="PopupCanceEventArgs"/> describing the
       cancel event.</param>
      private void popupNotifier_PopupCancel(object sender,
       PopupCancelEventArgs e)
      {
         EndTextEdit(true);         
      }
      
      /// <summary>
      /// Responds to the <see cref="KeyDown"/> event on the popup edit text
      /// control to allow end and cancel edit on Return and Escape keys
      /// </summary>
      /// <param name="sender">TextBox which sent the event</param>
      /// <param name="e"><see cref="KeyEventArgs"/> describing the key
       event.</param>
      private void txtPopupEdit_KeyDown(object sender, KeyEventArgs e)
      {
         switch (e.KeyData)         
         {
            case Keys.Return:
               // end editing:
               EndTextEdit(true);
               e.Handled = true;
               break;

            case Keys.Escape:
               // cancel editing:
               EndTextEdit(false);
               e.Handled = true;
               break;            
         }
      }

      
      /// <summary>
      /// Internal method to start editing.
      /// </summary>
      private void StartTextEdit()
      {
         this.Focus();
         
         txtPopupEdit.Text = (String) base.SelectedItem;
         if (txtPopupEdit.Text.Length > 0)
         {
            txtPopupEdit.SelectionStart = 0;
            txtPopupEdit.SelectionLength = txtPopupEdit.Text.Length;
         }
         txtPopupEdit.Tag = base.SelectedItem;
         Rectangle bounds = GetListBoxItemBounds();
         txtPopupEdit.Location = bounds.Location;
         txtPopupEdit.Size = bounds.Size;
         txtPopupEdit.Visible = true;            
         txtPopupEdit.BringToFront();
         txtPopupEdit.Focus();

         popupNotifier.StartTracking(txtPopupEdit);

      }

      /// <summary>
      /// Gets the bounding rectangle of the selected item in the ListBox
      /// </summary>
      /// <returns><see cref="System.Drawing.Rectangle"/> containing the
      /// bounds of the item on screen.</returns>
      protected virtual Rectangle GetListBoxItemBounds()
      {         
         int itemHeight = base.ItemHeight;
         int selectedIndex = base.SelectedIndex;
         int top = (selectedIndex - base.TopIndex) * itemHeight;
         return new Rectangle(0, top, base.ClientRectangle.Width, itemHeight);
      }

      /// <summary>
      /// Internal method to end text editing and optionally commit changes.
      /// </summary>
      /// <param name="commit">Whether changes should be committed or
       not.</param>
      private void EndTextEdit(bool commit)
      {
         if (commit)
         {
            base.Items[base.SelectedIndex] = txtPopupEdit.Text;
         }
         else
         {
            // do nothing
         }
         txtPopupEdit.Visible = false;
         popupNotifier.StopTracking();
      }


      /// <summary>
      /// Constructs a new instance of the editable ListBox
      /// </summary>
      public EditableListBox() : base()
      {
         popupNotifier = new PopupCancelNotifier();
         popupNotifier.PopupCancel += new
          PopupCancelEventHandler(popupNotifier_PopupCancel);

         txtPopupEdit = new System.Windows.Forms.TextBox();
         txtPopupEdit.KeyDown += new KeyEventHandler(txtPopupEdit_KeyDown);
         // 
         // txtPopupEdit
         // 
         this.txtPopupEdit.AcceptsReturn = true;
         this.txtPopupEdit.BorderStyle = System.Windows.Forms.BorderStyle.None;
         this.txtPopupEdit.Location = new System.Drawing.Point(128, 216);
         this.txtPopupEdit.Name = "txtPopupEdit";
         this.txtPopupEdit.Multiline = true;
         this.txtPopupEdit.Size = new System.Drawing.Size(144, 14);
         this.txtPopupEdit.TabIndex = 0;
         this.txtPopupEdit.Text = "";
         this.txtPopupEdit.Visible = false;

         this.Controls.AddRange( new Control[] { txtPopupEdit });
      }
   }
}