Contents
TextBox
controls have the nice read only feature. When you set the ReadOnly
property of a TextBox
to true
, you can't edit the text in the TextBox
anymore. The user can still select the text and copy it to another place. The background of the TextBox
is painted gray so the user has a visual indication of the ReadOnly
mode. The text is still painted in black, as in a normal editable TextBox
.
This behavior is completely different to Enabled
= false
. Via the Enabled
property, you can completely disable the TextBox
. The text is not painted in good readable black anymore but in a gray color. And you can't select the text anymore.
The ReadOnly
mode is very usable. I often use it, when I have a form with several controls where the user can edit the content of controls depending on his privileges. If the user doesn't have the required privileges, I do not disable the controls but set them to ReadOnly
. This way, the user can still very well read the texts in the TextBox
es, because their text is still painted in black.
Most other .NET controls don't have a ReadOnly
property. So if you want them to be read only, you have to disable them (Enabled
= false
). What I wanted was a ComboBox
that offers a ReadOnly
property and mimics the read only behavior of the TextBox
. Especially, paint the text in good readable black instead of the disabled color gray.
Several people on the net argue, you don't need a ReadOnlyComboBox
, because a ComboBox
is read only when you set the DropDownStyle
of the ComboBox
to DropDownList
. Well, of course, doing this, the user cannot edit the text in the ComboBox
anymore. But he can still select a value from the ComboBox
. For me, that's not the same thing as ReadOnly
.
I tried different things like hooking into the WndProc
to capture all the keyboard and mouse events and handle them myself, if the ComboBox
is ReadOnly
. But it just did not work out. I'm sure I did something wrong, but anyway. I'd also had to do the painting myself, where I had some problems as well.
Finally, I came up with a completely different idea. Whenever the ComboBox
is set to ReadOnly
= true
, I display a TextBox
instead that is read only. Because when it's read only, I don't need the dropdown button at all so a TextBox
fits everything needed here.
I found two implementation possibilities:
I could have created a new user control having a ComboBox
and a TextBox
at the same position. The user control acts as a decorator to the two embedded controls, and depending on the ReadOnly
property, acts as a TextBox
or a ComboBox
.
I decided not to do that. Why should I do all the work to decorate the ComboBox
? So I came up with solution two.
I create a new control (ReadOnlyComboBox
) that inherits from the standard ComboBox
control and contains a TextBox
. Whenever the ReadOnlyComboBox
is set to read only, it acts as a decorator for the embedded TextBox
and displays the TextBox
instead of the ComboBox
.
First, I added a new property ReadOnly
that handles the read only state.
public bool ReadOnly
{
get { return _isReadOnly; }
set
{
if (value != _isReadOnly)
{
_isReadOnly = value;
ShowControl();
}
}
}
The ShowControl()
method is responsible to show either the ComboBox
or the TextBox
depending on the ReadOnly
and the Enabled
properties.
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;
}
}
The ComboBox
already has a Visible
property. But we have to implement our own Visible
property to store the control's visibility. The reason is simple: when we use the ReadOnly
property, we change the visibility of the ComboBox
. Thus the Visible
property of the ComboBox
does not correspond to the visibility of the ReadOnlyComboBox
. That's why I declare a new Visible
property that hides the original Visible
property of the ComboBox
.
public new bool Visible
{
get { return _visible; }
set
{
_visible = value;
ShowControl();
}
}
But just replacing the Visible
property is not enough. The two methods Show()
and Hide()
need to be replaced as well, to use our new Visible
property.
public new void Show()
{
this.Visible = true;
}
public new void Hide()
{
this.Visible = false;
}
While we have already used our TextBox
, we have not yet created it. We'll do that in the constructor.
public ReadOnlyComboBox()
{
_textbox = new TextBox();
}
But that's not enough. We have to add the TextBox
to the parent container and we have to copy several properties of the ComboBox
to the TextBox
. To add the TextBox
, we override OnParentChanged()
of the ComboBox
.
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;
}
In the AddTextbox()
method, we make sure that the TextBox
has the same behavior (like size, location, docking, anchor, etc.) like the ComboBox
. But what if one of these properties changes during runtime? We have to make sure that such changes are propagated from the ComboBox
to the TextBox
. Therefore, I override several of the OnXXXX()
methods of the ComboBox
. Just a few of the overridden methods as example:
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;
}
That's it. We finally have our ReadOnlyComboBox
. In the same way, you could do a ReadOnlyDateTimePicker
or a ReadOnlyNumericalUpDown
and so on.
I will develop more read only enabled controls in the future. You'll find them here. Of course, if I have to implement one of them in a different way, I'll put it to Code Project again.
- 03/28/2005: Initial version
- 04/21/2005: First update
- Added:
Show()
and Hide()
methods because the Show()
and Hide()
methods of the parent ComboBox
did not use our new Visible
property but the Visible
property of the parent control. - Bugfix:
OnSelectedIndexChanged
raised an exception when no item was selected (thanks to Alexandre Cunha for reporting this bug). - Bugfix: The
TextBox
is now added in the OnParentChanged()
method. That solves several problems compared to the old solution where it was added in the ReadOnly
property (thanks again to Alexandre Cunha for reporting one of these problems). - Changed: When the
ComboBox
was disabled, it showed a disabled ComboBox
as it should be. When it was disabled and at the same time ReadOnly
was true
, it showed a disabled TextBox
. When disabled, it now always shows a disabled ComboBox
.