65.9K
CodeProject 正在变化。 阅读更多。
Home

具有只读行为的 ComboBox

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.66/5 (23投票s)

2005年3月28日

CPOL

5分钟阅读

viewsIcon

411863

downloadIcon

5116

一篇关于开发具有与 TextBox 相同只读功能的 ComboBox 的文章。

目录

引言

TextBox 的只读行为

TextBox 控件具有出色的只读功能。将 TextBoxReadOnly 属性设置为 true 后,您就无法再编辑 TextBox 中的文本。用户仍然可以选中该文本并将其复制到其他位置。TextBox 的背景会显示为灰色,以便用户直观地了解 ReadOnly 模式。文本仍然显示为黑色,与普通的可编辑 TextBox 一样。

此行为与 Enabled = false 完全不同。通过 Enabled 属性,您可以完全禁用 TextBox。文本不再显示为易读的黑色,而是显示为灰色。并且您无法选中该文本。

Read only mode of TextBox

ReadOnly 模式非常有用。我经常使用它,当我的窗体中有多个控件,用户可以根据其权限编辑控件内容时。如果用户没有所需的权限,我不会禁用这些控件,而是将它们设置为 ReadOnly。这样,用户仍然可以很好地读取 TextBox 中的文本,因为它们的文本仍然显示为黑色。

ComboBox 没有只读属性

大多数其他 .NET 控件都没有 ReadOnly 属性。因此,如果您希望它们只读,则必须禁用它们(Enabled = false)。我想要的是一个提供 ReadOnly 属性的 ComboBox,并模拟 TextBox 的只读行为。特别是,将文本显示为易读的黑色,而不是禁用的灰色。

下拉列表样式

许多人在网上争辩说,您不需要 ReadOnlyComboBox,因为当您将 ComboBoxDropDownStyle 设置为 DropDownList 时,ComboBox 就是只读的。当然,这样做后,用户就无法再编辑 ComboBox 中的文本了。但是他仍然可以从 ComboBox 中选择一个值。对我来说,这与 ReadOnly 的概念不同。

解决方案

想法

我尝试了各种方法,例如挂钩 WndProc 来捕获所有键盘和鼠标事件,并在 ComboBoxReadOnly 时自行处理它们。但这不起作用。我肯定是我做错了什么,但不管怎样。我还必须自己处理绘制,在这方面我也遇到了一些问题。

最后,我提出了一个完全不同的想法。每当 ComboBox 被设置为 ReadOnly = true 时,我就显示一个只读的 TextBox 来代替它。因为它是只读的,所以我根本不需要下拉按钮,因此 TextBox 满足了这里的所有需求。

Read only mode of ComboBox

实现

基础

我找到了两种实现可能性

装饰 ComboBox 和 TextBox 的用户控件

我可以创建一个新的用户控件,在同一位置包含一个 ComboBox 和一个 TextBox。用户控件充当两个嵌入控件的装饰器,并根据 ReadOnly 属性充当 TextBoxComboBox

我决定不这样做。为什么要花费所有精力来装饰 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() 方法负责根据 ReadOnlyEnabled 属性显示 ComboBoxTextBox

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 的可见性。因此,ComboBoxVisible 属性与 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,我们重写 ComboBoxOnParentChanged()

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。同样,您也可以创建一个 ReadOnlyDateTimePickerReadOnlyNumericalUpDown 等。

我将在未来开发更多支持只读功能的控件。您可以在此处找到它们。当然,如果我必须以不同的方式实现其中任何一个,我都会再次将其发布到 Code Project。

历史

  • 2005/03/28:初始版本
  • 2005/04/21:首次更新
    • 已添加:Show()Hide() 方法,因为父 ComboBoxShow()Hide() 方法未使用我们的新 Visible 属性,而是使用了父控件的 Visible 属性。
    • 错误修复:当没有选定项目时,OnSelectedIndexChanged 引发了异常(感谢 Alexandre Cunha 报告此错误)。
    • 错误修复:TextBox 现在在 OnParentChanged() 方法中添加。与旧解决方案(在 ReadOnly 属性中添加)相比,这解决了几个问题(再次感谢 Alexandre Cunha 报告了其中一个问题)。
    • 已更改:当 ComboBox 被禁用时,它会显示一个被禁用的 ComboBox,正如应该的那样。当它被禁用且同时 ReadOnlytrue 时,它会显示一个被禁用的 TextBox。现在,当被禁用时,它总是显示一个被禁用的 ComboBox
© . All rights reserved.