具有只读行为的 ComboBox






4.66/5 (23投票s)
一篇关于开发具有与 TextBox 相同只读功能的 ComboBox 的文章。
目录
引言
TextBox 的只读行为
TextBox
控件具有出色的只读功能。将 TextBox
的 ReadOnly
属性设置为 true
后,您就无法再编辑 TextBox
中的文本。用户仍然可以选中该文本并将其复制到其他位置。TextBox
的背景会显示为灰色,以便用户直观地了解 ReadOnly
模式。文本仍然显示为黑色,与普通的可编辑 TextBox
一样。
此行为与 Enabled
= false
完全不同。通过 Enabled
属性,您可以完全禁用 TextBox
。文本不再显示为易读的黑色,而是显示为灰色。并且您无法选中该文本。
ReadOnly
模式非常有用。我经常使用它,当我的窗体中有多个控件,用户可以根据其权限编辑控件内容时。如果用户没有所需的权限,我不会禁用这些控件,而是将它们设置为 ReadOnly
。这样,用户仍然可以很好地读取 TextBox
中的文本,因为它们的文本仍然显示为黑色。
ComboBox 没有只读属性
大多数其他 .NET 控件都没有 ReadOnly
属性。因此,如果您希望它们只读,则必须禁用它们(Enabled
= false
)。我想要的是一个提供 ReadOnly
属性的 ComboBox
,并模拟 TextBox
的只读行为。特别是,将文本显示为易读的黑色,而不是禁用的灰色。
下拉列表样式
许多人在网上争辩说,您不需要 ReadOnlyComboBox
,因为当您将 ComboBox
的 DropDownStyle
设置为 DropDownList
时,ComboBox
就是只读的。当然,这样做后,用户就无法再编辑 ComboBox
中的文本了。但是他仍然可以从 ComboBox
中选择一个值。对我来说,这与 ReadOnly
的概念不同。
解决方案
想法
我尝试了各种方法,例如挂钩 WndProc
来捕获所有键盘和鼠标事件,并在 ComboBox
为 ReadOnly
时自行处理它们。但这不起作用。我肯定是我做错了什么,但不管怎样。我还必须自己处理绘制,在这方面我也遇到了一些问题。
最后,我提出了一个完全不同的想法。每当 ComboBox
被设置为 ReadOnly
= true
时,我就显示一个只读的 TextBox
来代替它。因为它是只读的,所以我根本不需要下拉按钮,因此 TextBox
满足了这里的所有需求。
实现
基础
我找到了两种实现可能性
装饰 ComboBox 和 TextBox 的用户控件
我可以创建一个新的用户控件,在同一位置包含一个 ComboBox
和一个 TextBox
。用户控件充当两个嵌入控件的装饰器,并根据 ReadOnly
属性充当 TextBox
或 ComboBox
。
我决定不这样做。为什么要花费所有精力来装饰 ComboBox
?所以我想到了解决方案二。
继承自 ComboBox 并仅装饰 TextBox
我创建了一个新的控件(ReadOnlyComboBox
),它继承自标准的 ComboBox
控件并包含一个 TextBox
。每当 ReadOnlyComboBox
被设置为只读时,它就充当嵌入式 TextBox
的装饰器,并显示 TextBox
而不是 ComboBox
。
ReadOnly
首先,我添加了一个新的 ReadOnly
属性来处理只读状态。
public bool ReadOnly
{
get { return _isReadOnly; }
set
{
if (value != _isReadOnly)
{
_isReadOnly = value;
ShowControl();
}
}
}
ShowControl()
方法负责根据 ReadOnly
和 Enabled
属性显示 ComboBox
或 TextBox
。
private void ShowControl()
{
if (_isReadOnly)
{
_textbox.Visible = _visible && this.Enabled;
base.Visible = _visible && !this.Enabled;
_textbox.Text = this.Text;
}
else
{
_textbox.Visible = false;
base.Visible = _visible;
}
}
Visible
ComboBox
已经有一个 Visible
属性。但是我们必须实现自己的 Visible
属性来存储控件的可见性。原因很简单:当我们使用 ReadOnly
属性时,我们会改变 ComboBox
的可见性。因此,ComboBox
的 Visible
属性与 ReadOnlyComboBox
的可见性不对应。这就是为什么我声明了一个新的 Visible
属性来隐藏 ComboBox
的原始 Visible
属性。
public new bool Visible
{
get { return _visible; }
set
{
_visible = value;
ShowControl();
}
}
仅仅替换 Visible
属性是不够的。Show()
和 Hide()
方法也需要被替换,以便使用我们的新 Visible
属性。
public new void Show()
{
this.Visible = true;
}
public new void Hide()
{
this.Visible = false;
}
添加 TextBox
虽然我们已经使用了 TextBox
,但还没有创建它。我们将在构造函数中完成。
public ReadOnlyComboBox()
{
_textbox = new TextBox();
}
但这还不够。我们必须将 TextBox
添加到父容器中,并且必须将 ComboBox
的几个属性复制到 TextBox
。要添加 TextBox
,我们重写 ComboBox
的 OnParentChanged()
。
protected override void OnParentChanged(EventArgs e)
{
base.OnParentChanged(e);
if (Parent != null)
AddTextbox();
_textbox.Parent = this.Parent;
}
private void AddTextbox()
{
_textbox.ReadOnly = true;
_textbox.Location = this.Location;
_textbox.Size = this.Size;
_textbox.Dock = this.Dock;
_textbox.Anchor = this.Anchor;
_textbox.Enabled = this.Enabled;
_textbox.Visible = this.Visible;
_textbox.RightToLeft = this.RightToLeft;
_textbox.Font = this.Font;
_textbox.Text = this.Text;
_textbox.TabStop = this.TabStop;
_textbox.TabIndex = this.TabIndex;
}
其他
在 AddTextbox()
方法中,我们确保 TextBox
具有与 ComboBox
相同的行为(如大小、位置、停靠、锚定等)。但是,如果这些属性中的任何一个在运行时发生变化怎么办?我们必须确保这些更改从 ComboBox
传播到 TextBox
。因此,我重写了 ComboBox
的几个 OnXXXX()
方法。仅作为示例,列举其中几个方法。
protected override void OnSelectedIndexChanged(EventArgs e)
{
base.OnSelectedIndexChanged(e);
if (this.SelectedItem == null)
_textbox.Clear();
else
_textbox.Text = this.SelectedItem.ToString();
}
protected override void OnEnabledChanged(EventArgs e)
{
base.OnEnabledChanged(e);
ShowControl();
}
protected override void OnDropDownStyleChanged(EventArgs e)
{
base.OnDropDownStyleChanged(e);
_textbox.Text = this.Text;
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
_textbox.Size = this.Size;
}
protected override void OnLocationChanged(EventArgs e)
{
base.OnLocationChanged(e);
_textbox.Location = this.Location;
}
就这样。我们终于有了 ReadOnlyComboBox
。同样,您也可以创建一个 ReadOnlyDateTimePicker
或 ReadOnlyNumericalUpDown
等。
我将在未来开发更多支持只读功能的控件。您可以在此处找到它们。当然,如果我必须以不同的方式实现其中任何一个,我都会再次将其发布到 Code Project。
历史
- 2005/03/28:初始版本
- 2005/04/21:首次更新
- 已添加:
Show()
和Hide()
方法,因为父ComboBox
的Show()
和Hide()
方法未使用我们的新Visible
属性,而是使用了父控件的Visible
属性。 - 错误修复:当没有选定项目时,
OnSelectedIndexChanged
引发了异常(感谢 Alexandre Cunha 报告此错误)。 - 错误修复:
TextBox
现在在OnParentChanged()
方法中添加。与旧解决方案(在ReadOnly
属性中添加)相比,这解决了几个问题(再次感谢 Alexandre Cunha 报告了其中一个问题)。 - 已更改:当
ComboBox
被禁用时,它会显示一个被禁用的ComboBox
,正如应该的那样。当它被禁用且同时ReadOnly
为true
时,它会显示一个被禁用的TextBox
。现在,当被禁用时,它总是显示一个被禁用的ComboBox
。
- 已添加: