using System;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Collections;
using System.Diagnostics;
namespace RR.Windows.Forms
{
/// <summary>
/// Provides a standard Winform ListBox with editing capabilities.
/// </summary>
[DefaultEvent("ButtonClick"),]
public class ListBoxExEdit : System.ComponentModel.Component
{
private System.ComponentModel.IContainer components;
public ListBoxExEdit(System.ComponentModel.IContainer container)
{
///
/// Required for Windows.Forms Class Composition Designer support
///
container.Add(this);
InitializeComponent();
}
public ListBoxExEdit()
{
///
/// Required for Windows.Forms Class Composition Designer support
///
InitializeComponent();
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Component 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.components = new System.ComponentModel.Container();
this.textBox = new RR.Windows.Forms.ListBoxExEdit.TextBoxEx();
this.button = new System.Windows.Forms.Button();
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
//
// textBox
//
this.textBox.Location = new System.Drawing.Point(13, 11);
this.textBox.Name = "textBox";
this.textBox.TabIndex = 0;
this.textBox.Text = "";
this.textBox.Visible = false;
this.textBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.textBox_KeyDown);
this.textBox.Leave += new System.EventHandler(this.textBox_Leave);
//
// button
//
this.button.FlatStyle = System.Windows.Forms.FlatStyle.System;
this.button.Location = new System.Drawing.Point(106, 11);
this.button.Name = "button";
this.button.Size = new System.Drawing.Size(30, 20);
this.button.TabIndex = 0;
this.button.TabStop = false;
this.button.Text = "...";
this.button.Visible = false;
this.button.Click += new System.EventHandler(this.button_Click);
}
#endregion
private RR.Windows.Forms.ListBoxExEdit.TextBoxEx textBox;
private System.Windows.Forms.ToolTip toolTip;
private System.Windows.Forms.Button button;
#region public
[Description("The listbox this extender works with. If the ListBox is not set, the extender has no function.")]
public ListBox ListBox
{
get
{
return listBox;
}
set
{
// unhook events from old listbox
if (listBox!=null)
{
this.listBox.MouseDown -= new System.Windows.Forms.MouseEventHandler(this.listBox_MouseDown);
this.listBox.MouseMove -= new System.Windows.Forms.MouseEventHandler(this.listBox_MouseMove);
this.listBox.KeyDown -= new System.Windows.Forms.KeyEventHandler(this.listBox_KeyDown);
this.ListBox.SelectedIndexChanged -= new System.EventHandler(this.listBox_SelectedIndexChanged);
this.listBox.Controls.Remove(this.textBox);
this.listBox.Controls.Remove(this.button);
}
listBox=value;
// hookup events and add to parents control collection
if (listBox!=null)
{
this.listBox.MouseDown += new System.Windows.Forms.MouseEventHandler(this.listBox_MouseDown);
this.listBox.MouseMove += new System.Windows.Forms.MouseEventHandler(this.listBox_MouseMove);
this.listBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.listBox_KeyDown);
this.ListBox.SelectedIndexChanged += new System.EventHandler(this.listBox_SelectedIndexChanged);
this.listBox.Controls.Add(this.textBox);
this.listBox.Controls.Add(this.button);
}
}
}
[Description("If true, a button will be showed while in edit mode."), Category("Behavior"), DefaultValue(false)]
public bool DisplayButton
{
get
{
return displayButton;
}
set
{
displayButton=value;
}
}
public enum TipBehavior
{
Never,
Always,
LongLinesOnly,
}
private TipBehavior showTip=TipBehavior.LongLinesOnly;
[Description("Indicates how tooltips are displayed"), Category("Behavior"), DefaultValue(TipBehavior.LongLinesOnly)]
public TipBehavior ShowTip
{
get
{
return showTip;
}
set
{
showTip=value;
}
}
[Description("The embedded button that will show when the listbox is in editmode and DisplayButton is true")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Button Button
{
get
{
return this.button;
}
}
[Description("The embedded textbox that will show when the listbox is in editmode")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public TextBox TextBox
{
get
{
return this.textBox;
}
}
[Description("If true, the control handles navigation keys directly."), Category("Behavior"), DefaultValue(false)]
public bool KeyNavigation
{
get
{
return keyNavigation;
}
set
{
keyNavigation=value;
}
}
/// <summary>
/// insert a new line and display the textbox.
/// </summary>
public void NewLine()
{
if (this.listBox==null) return;
int index=(this.listBox.SelectedIndex>=0)?this.listBox.SelectedIndex:0;
this.listBox.Items.Insert(index, "");
this.listBox.SelectedIndex=index;
this.DisplayEditBox(index);
}
/// <summary>
/// remove the selected line from the editbox.
/// </summary>
public void CutLine()
{
if (this.listBox==null) return;
int index=this.listBox.SelectedIndex;
if (index<0) return;
this.listBox.Items.RemoveAt(index);
if (index>=this.listBox.Items.Count)
{
this.listBox.SelectedIndex=this.listBox.Items.Count-1;
}
else
{
this.listBox.SelectedIndex=index;
}
}
/// <summary>
/// move the selected line up
/// </summary>
public void MoveLineDown()
{
if (this.listBox==null) return;
int index=(this.listBox.SelectedIndex>=0)?this.listBox.SelectedIndex:0;
// give up if already on bottom
if (index+2>this.listBox.Items.Count) return;
this.listBox.Items.Insert(index+2, this.listBox.Items[index]);
this.listBox.Items.RemoveAt(index);
this.listBox.SelectedIndex=index+1;
}
/// <summary>
/// move the selected line down.
/// </summary>
public void MoveLineUp()
{
if (this.listBox==null) return;
int index=this.listBox.SelectedIndex;
if (index<1) return;
this.listBox.Items.Insert(index-1, this.listBox.Items[index]);
this.listBox.Items.RemoveAt(index+1);
this.listBox.SelectedIndex=index-1;
}
// private Keys lineUpShortcut=Keys. RShiftKey;
// public Keys LineUpShortcut
// {
// get
// {
// return lineUpShortcut;
// }
// set
// {
// lineUpShortcut=value;
// }
// }
#endregion
#region private
private ListBox listBox;
private int previousSelectedIndex=-1;
private int lastTipIndex = -1;
private bool displayButton=false;
private bool keyNavigation=false;
/// <summary>
/// start editing text at given index.
/// </summary>
/// <param name="index"></param>
public void DisplayEditBox(int index)
{
Rectangle rect=listBox.GetItemRectangle(index);
if (displayButton)
{
this.textBox.Bounds=new Rectangle(rect.X, rect.Y, rect.Width-this.button.Bounds.Width, rect.Height);
this.button.Bounds=new Rectangle(rect.X+this.textBox.Width, this.textBox.Bounds.Y, this.button.Bounds.Width, this.textBox.Bounds.Height);
}
else
{
this.textBox.Bounds=rect;
}
this.button.Tag=this.textBox.Tag=index;
this.textBox.Text=(string)listBox.Items[index];
this.textBox.SelectionStart=this.textBox.Text.Length;
// make it visible on give focus
this.textBox.Visible=true;
this.button.Visible=displayButton;
this.textBox.Focus();
}
private void HandleCommonKeys(System.Windows.Forms.KeyEventArgs e)
{
if (!keyNavigation) return;
if (e.KeyCode==Keys.Insert)
{
if (e.Control&&(!(e.Alt||e.Shift)))
{
if (this.textBox.Visible) this.listBox.Focus();
this.NewLine();
e.Handled=true;
}
}
else if (e.KeyCode==Keys.Delete)
{
if (e.Control&&(!(e.Alt||e.Shift)))
{
if (this.textBox.Visible) this.listBox.Focus();
this.CutLine();
e.Handled=true;
}
}
else if (e.KeyCode==Keys.Down)
{
if (e.Control&&(!(e.Alt||e.Shift)))
{
if (this.textBox.Visible) this.listBox.Focus();
this.MoveLineDown();
e.Handled=true;
}
}
else if (e.KeyCode==Keys.Up)
{
if (e.Control&&(!(e.Alt||e.Shift)))
{
if (this.textBox.Visible) this.listBox.Focus();
this.MoveLineUp();
e.Handled=true;
}
}
}
#endregion
#region delegate stuff
public class ButtonClickedEventArgs : System.EventArgs
{
public ButtonClickedEventArgs(string text, int index)
{
this.Text=text;
this.Index=index;
}
public readonly string Text;
public readonly int Index;
}
public delegate void ButtonClickedEventHandler(object sender, ButtonClickedEventArgs e);
[Description("Occurs when the embedded button is clicked."), Category("Action")]
public event ButtonClickedEventHandler ButtonClick;
protected virtual void OnButtonClick(ButtonClickedEventArgs e)
{
if (ButtonClick!=null)
{
ButtonClick(this, e);
}
}
public class ModifiedChangedEventArgs : System.EventArgs
{
public ModifiedChangedEventArgs(string oldText, string newText, int index)
{
this.OldText=oldText;
this.NewText=newText;
this.Index=index;
}
public readonly string OldText;
public readonly string NewText;
public readonly int Index;
}
public delegate void ModifiedChangedEventHandler(object sender, ModifiedChangedEventArgs e);
[Description("Event fired when the value of the embedded textbox is changed."), Category("Property Changed")]
public event ModifiedChangedEventHandler ModifiedChanged;
protected virtual void OnModifiedChanged(ModifiedChangedEventArgs e)
{
if (ModifiedChanged!=null)
{
ModifiedChanged(this, e);
}
}
//OnModifiedChanged(new ModifiedChangedEventArgs("old", "new", 1));
#endregion
#region listview event handlers
private void listBox_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
int index=listBox.IndexFromPoint(e.X, e.Y);
// start edit if index is valid and same as previous index
if ((index>=0)&&(index==previousSelectedIndex))
{
DisplayEditBox(index);
}
}
private void listBox_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (showTip==TipBehavior.Never) return;
// don't show tips if edit is active
if (this.textBox.Visible) return;
int index=this.listBox.IndexFromPoint(e.X,e.Y);
if (index==lastTipIndex) return;
lastTipIndex=index;
string tipText=null;
if (index>=0)
{
tipText=this.listBox.Items[index].ToString();
if (showTip==TipBehavior.LongLinesOnly)
{
Graphics graphics = this.listBox.CreateGraphics();
// Add one character to be on the save side
int iWidth = (int)(graphics.MeasureString(tipText+"X", this.listBox.Font).Width);
if (iWidth<this.listBox.Width)
{
tipText=null;
}
}
}
toolTip.Active = false;
toolTip.SetToolTip(this.listBox, tipText);
toolTip.Active = true; //make it active so it can show itself
}
private void listBox_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
if (!(e.Alt||e.Control||e.Shift))
{
DisplayEditBox(listBox.SelectedIndex);
e.Handled=true;
}
}
if (!e.Handled)
{
HandleCommonKeys(e);
}
}
private void listBox_SelectedIndexChanged(object sender, System.EventArgs e)
{
previousSelectedIndex=listBox.SelectedIndex;
}
#endregion
#region textbox event handlers
private void textBox_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
{
if (!(e.Alt||e.Control||e.Shift))
{
// clear tag so leave will not copy text
this.button.Tag=this.textBox.Tag=null;
this.listBox.Focus();
e.Handled=true;
}
}
else if (e.KeyCode == Keys.Enter)
{
if (!(e.Alt||e.Control||e.Shift))
{
this.listBox.Focus();
e.Handled=true;
}
}
else if (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down || e.KeyCode == Keys.Tab)
{
if (!(e.Alt||e.Control))
{
bool up=(e.KeyCode==Keys.Up)||((e.KeyCode == Keys.Tab)&&e.Shift);
int index=(int)(this.textBox.Tag);
// copy data from textbox into list
string oldText=(string)listBox.Items[index];
listBox.Items[index]=this.textBox.Text;
OnModifiedChanged(new ModifiedChangedEventArgs(oldText, this.textBox.Text, index));
// decide where to move the editbox
index+=up?-1:1;
if (index>=listBox.Items.Count) index=0;
if (index<0) index=listBox.Items.Count-1;
// display the editbox at new index
listBox.SelectedIndex=index;
DisplayEditBox(index);
e.Handled=true;
}
}
if (!e.Handled)
{
HandleCommonKeys(e);
}
}
private void textBox_Leave(object sender, System.EventArgs e)
{
if (this.textBox.Tag!=null)
{
// don't hide editbox and button if focus is on button
if (button.Visible && button.Bounds.Contains(this.listBox.PointToClient(Cursor.Position)))
{
return;
}
this.button.Visible=this.textBox.Visible=false;
int index=(int)(this.textBox.Tag);
string oldText=(string)listBox.Items[index];
listBox.Items[index]=this.textBox.Text;
OnModifiedChanged(new ModifiedChangedEventArgs(oldText, this.textBox.Text, index));
this.textBox.Tag=null;
}
else
{
this.button.Visible=this.textBox.Visible=false;
}
}
#endregion
#region button event handlers
private void button_Click(object sender, System.EventArgs e)
{
if (((Control)sender).Tag==null) return;
int index=(int)(((Control)sender).Tag);
OnButtonClick(new ButtonClickedEventArgs(textBox.Text, index));
textBox.Focus();
}
#endregion
#region private classes
private class TextBoxEx : System.Windows.Forms.TextBox
{
#region overide from control
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if ((keyData==Keys.Tab)||(keyData == (Keys.Shift|Keys.Tab)))
{
// grab Tab and Shift-Tab before the framework takes them
this.OnKeyDown(new KeyEventArgs(keyData));
return true;
}
else if (keyData==Keys.Escape)
{
// grab escape key before framework takes it
this.OnKeyDown(new KeyEventArgs(keyData));
return true;
}
else if (keyData==Keys.Enter)
{
// grab enter before someone sounds the bell
this.OnKeyDown(new KeyEventArgs(keyData));
return true;
}
else
{
// normal processing for all other keys
return base.ProcessCmdKey (ref msg, keyData);
}
}
#endregion
}
#endregion
}
}