.NET 的可编辑多行列表框






4.85/5 (40投票s)
一个支持多行自动换行文本以及就地编辑的自绘列表框。
概述
MultiLineListBox
是一个完全自绘的 ListBox
派生类,它支持多行项目以及项目的就地编辑。它已被封装到一个类库中,以便于部署。您在使用它时无需进行任何特殊操作。只需像使用普通列表框一样使用它。每当您添加的字符串对于单行来说太长时,MultiLineListBox
会将长字符串换成多行。编辑项目也很容易。只需右键单击任何项目或在选择项目时按 F2 键,这将在所选或单击的项目上精确地叠加一个编辑框。您可以进行更改,然后按 Enter 键,或者您可以单击列表框内的其他任何位置以使修改生效。如果您想取消更改并返回到原始文本,只需按 Escape 键。
该类不允许您将项目设置为 null,并且会弹出一个消息框警报,并且不允许您从该项目移开焦点,直到您通过按 Escape 键取消了修改,或者您输入了一些文本。
如果您在项目编辑文本框中输入大量文本,则叠加的文本框本身将允许您垂直滚动。
使用该类
只需将控件添加到您的工具箱。现在,像使用普通列表框一样使用它。尝试输入长字符串。并运行程序。尝试通过按 F2 键或右键单击某个项目来编辑字符串。请参阅当您尝试将项目字符串修改为 null 字符串时,警报消息框是如何弹出的。我添加此功能是因为在我将要使用它的项目中,严格禁止使用 null 字符串。也许您可能想要允许 null 字符串,如果是这样,请随意注释掉源代码中的相关代码。
技术细节
很明显,DrawMode
设置为 DrawMode.OwnerDrawVariable
。我们需要确定每个项目所需的高度。这就是我们在 OnMeasureItem
重写中执行此操作的方式,
//...
string s = Items[e.Index].ToString();
SizeF sf = e.Graphics.MeasureString(s,Font,Width);
int htex = (e.Index==0) ? 15 : 10;
e.ItemHeight = (int)sf.Height + htex;
e.ItemWidth = Width;
//...
现在我们需要实际绘制文本,因为这是一个自绘列表框。我们重写 OnDrawItem
,如下所示。
protected override void OnDrawItem(DrawItemEventArgs e)
{
//...
/*chk if list box has any items*/
if(e.Index > -1)
{
string s = Items[e.Index].ToString();
/*Normal items*/
if((e.State & DrawItemState.Focus)==0)
{
e.Graphics.FillRectangle(
new SolidBrush(SystemColors.Window),
e.Bounds);
e.Graphics.DrawString(s,Font,
new SolidBrush(SystemColors.WindowText),
e.Bounds);
e.Graphics.DrawRectangle(
new Pen(SystemColors.Highlight),e.Bounds);
}
else /*Selected item, needs highlighting*/
{
e.Graphics.FillRectangle(
new SolidBrush(SystemColors.Highlight),
e.Bounds);
e.Graphics.DrawString(s,Font,
new SolidBrush(SystemColors.HighlightText),
e.Bounds);
}
}
}
到目前为止还不错。现在进行就地编辑,我们要做的是从 TextBox
派生一个类并将其添加到我们的列表框中。并且我们处理 OnMouseUp
方法来检查用户是否右键单击了某个项目。
protected override void OnMouseUp(
System.Windows.Forms.MouseEventArgs e)
{
//...
/* Is it a right mouse clk? */
if(e.Button == MouseButtons.Right)
{
string s = Items[index].ToString();
Rectangle rect = GetItemRectangle(index);
//overlay the text box right over
//the list box item to be edited
tbox.Location = new Point(rect.X,rect.Y);
tbox.Size = new Size(rect.Width,rect.Height);
tbox.Text = s;
tbox.index = index;
tbox.SelectAll();
tbox.Show();
tbox.Focus();
}
//...
}
类似地,我们处理 OnKeyDown
来检查用户是否按下了 F2 键。
protected override void OnKeyDown(KeyEventArgs e)
{
if(e.KeyData == Keys.F2)
{
//...
string s = Items[index].ToString();
Rectangle rect = GetItemRectangle(index);
tbox.Location = new Point(rect.X,rect.Y);
tbox.Size = new Size(rect.Width,rect.Height);
//...
}
//...
}
在 TextBox
派生类中,我们重写 OnLostFocus
和 OnKeyPress
(检查 Enter 键)并进行检查以确保用户不会输入 null 字符串。可以检查源代码以了解更精细的细节,但基本上该类所做的就是这些,并且该类是 MultiLineListBox
类的私有内部类。我还检查 OnKeyPress
处理程序中的 Escape 键,以允许用户取消修改。
结论
我希望得到您的反馈 - 无论是好的还是坏的。我还想知道是否有人发现这足够有用,可以在应用程序中使用。或者该类是否可以在任何特定方面进行改进。至于我自己,我花了一整天的时间来处理我遇到的各种问题,主要是由于我在自定义控件编码方面的经验不足,而不是由于我尝试做的任何重大困难。