Reputation: 22052
I need to have 2 groups of controls on the screen: inputs and outputs (so they have 2 states: On or Off). Thus CheckBox
seems to be a good choice. Checking any output will set it.
However, when displaying inputs there will be no user interaction with it. User is only allowed to see its value, not to change it.
Could think about possible solutions:
CheckBox
disabled. Bad: there will be no tooltip (possible to solve it? by fake panel on top?) and visually disabled CheckBox
is not nice (and I don't want to make user think it is disabled).Label
doesn't have nice placeholder for the On/Off value. RadioButton
look differently, but they usually means there is a single choice out of many, while values of inputs are independent.CheckBox
is a bit overkill (and honestly, I don't know how to do it to have Win7 appearance). Would it be possible to add only ReadOnly
appearance to the box part easily?What do you guys think?
Upvotes: 12
Views: 18491
Reputation: 33
A "read-only" visual effect (grey checkbox but black label) can also be achieved using a CheckBox
without text
along with a normal Label
next to it.
Mind that if the CheckBox
is ever enabled, an onClick
event would have to be added to the Label
to mimic the original behaviour.
Upvotes: 0
Reputation: 4582
This is an old post but still can be usefull so here is my solution.
In order to make a read-only behavior:
CheckBox
We can inherit the CheckBox
class and disable mouse and keyboard interaction:
public class ReadOnlyCheckBox : CheckBox
{
[System.ComponentModel.Category("Behavior")]
[System.ComponentModel.DefaultValue(false)]
public bool ReadOnly { get; set; } = false;
protected override void OnMouseEnter(EventArgs e)
{
// Disable highlight when the cursor is over the CheckBox
if (!ReadOnly) base.OnMouseEnter(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
// Disable reacting (logically or visibly) to a mouse click
if (!ReadOnly) base.OnMouseDown(e);
}
protected override void OnKeyDown(KeyEventArgs e)
{
// Suppress space key to disable checking/unchecking
if (!ReadOnly || e.KeyData != Keys.Space) base.OnKeyDown(e);
}
}
In order to make it visually apparent that the CheckBox
is read-only, we can change the ForColor
according to the ReadOnly
property.
Note: changing the ForColor
only changes the text color, the colors of the checkmark can only be changed by overriding the OnPaint
method and repainting the CheckBox
(as far as I know).
Here is an extended version of the previous code that changes the ForColor
according to the ReadOnly
property:
public class ReadOnlyCheckBox : CheckBox
{
private bool _readOnly = false;
private Color _readOnlyForeColor = Color.Gray;
private Color _normalForeColor = Color.Black;
[System.ComponentModel.Category("Behavior")]
[System.ComponentModel.DefaultValue(false)]
public bool ReadOnly
{
get => _readOnly;
set
{
if (_readOnly != value)
{
_readOnly = value;
UpdateForColor();
}
}
}
[System.ComponentModel.Category("Appearance")]
[System.ComponentModel.DefaultValue(typeof(Color), "Black")]
public Color NormalForeColor
{
get => _normalForeColor;
set
{
if (_normalForeColor != value)
{
_normalForeColor = value;
UpdateForColor();
}
}
}
[System.ComponentModel.Category("Appearance")]
[System.ComponentModel.DefaultValue(typeof(Color), "Gray")]
public Color ReadOnlyForeColor
{
get => _readOnlyForeColor;
set
{
if (_readOnlyForeColor != value)
{
_readOnlyForeColor = value;
UpdateForColor();
}
}
}
// Hide ForeColor from the editor
[System.ComponentModel.Browsable(false)]
[System.ComponentModel.EditorBrowsable(
System.ComponentModel.EditorBrowsableState.Never)]
public override Color ForeColor
{
get => base.ForeColor;
set => base.ForeColor = value;
}
public ReadOnlyCheckBox()
{
UpdateForColor();
}
private void UpdateForColor()
{
ForeColor = ReadOnly ? ReadOnlyForeColor : NormalForeColor;
}
protected override void OnMouseEnter(EventArgs e)
{
// Disable highlight when the cursor is over the CheckBox
if (!ReadOnly) base.OnMouseEnter(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
// Disable reacting (logically or visibly) to a mouse click
if (!ReadOnly) base.OnMouseDown(e);
}
protected override void OnKeyDown(KeyEventArgs e)
{
// Suppress space key to disable checking/unchecking
if (!ReadOnly || e.KeyData != Keys.Space) base.OnKeyDown(e);
}
}
Upvotes: 1
Reputation:
You may provide a listener for an event of clicking on CheckBox
, as there's ability to cancel its usual flow at runtime.
Upvotes: 0
Reputation: 51
It is not necessary to write the entire control, just write a derivative of Checkbox
.
A ReadOnly
property is added to the control, this causes the control to handle when it can change its value.
public class CheckBoxReadOnly : CheckBox
{
private bool _readOnly = false;
[DefaultValue(false)]
public bool ReadOnly
{
get { return _readOnly; }
set
{
if (_readOnly != value)
{
_readOnly = value;
OnReadOnlyChanged(new EventArgs());
}
}
}
int _flag = 0;
public event EventHandler ReadOnlyChanged;
protected void OnReadOnlyChanged(EventArgs e)
{
ReadOnlyChanged?.Invoke(this, e);
}
protected override void OnCheckedChanged(EventArgs e)
{
if (ReadOnly)
{
_flag++;
if (_flag == 1)
Checked = !Checked;
}
else
{
base.OnCheckedChanged(e);
}
_flag = 0;
}
}
Upvotes: 0
Reputation: 35
Visual studio has now it available under: Properties -> Properties -> ReadOnly :)
Upvotes: -1
Reputation: 6952
There is a solution that is combination of the existing answers.
checkBox.ForeColor = Color.Gray; // Read-only appearance
checkBox.AutoCheck = false; // Read-only behavior
// Tooltip is possible because the checkbox is Enabled
var toolTip = new ToolTip();
toolTip.SetToolTip(checkBox, "This checkbox is read-only.");
The result is a CheckBox
that
Checked
value from changing when clickedTooltip
Upvotes: 17
Reputation: 63387
You have to draw everything yourself. I think you should use some controls with correct layout to mimic it. Here is the demo code for you, note that it does not support AutoSize
correctly. Because the drawn stuff is always wider than the default stuff (which the AutoSize
works with), implementing the AutoSize
is not easy, If you don't care too much about AutoSize
, this would be the great control for you:
public class XCheckBox : CheckBox
{
public XCheckBox()
{
SetStyle(ControlStyles.Opaque, false);
ReadOnlyCheckedColor = Color.Green;
ReadOnlyUncheckedColor = Color.Gray;
}
public bool ReadOnly { get; set; }
public bool AlwaysShowCheck { get; set; }
public Color ReadOnlyCheckedColor { get; set; }
public Color ReadOnlyUncheckedColor { get; set; }
protected override void OnPaint(PaintEventArgs pevent)
{
if (ReadOnly)
{
pevent.Graphics.SmoothingMode = SmoothingMode.HighQuality;
pevent.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
if (AlwaysShowCheck || Checked)
{
RenderCheck(pevent.Graphics);
}
RenderText(pevent.Graphics);
}
else base.OnPaint(pevent);
}
private void RenderCheck(Graphics g)
{
float fontScale = Font.Size / 8.25f;
Size glyphSize = CheckBoxRenderer.GetGlyphSize(g, System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal);
glyphSize.Width = (int) (glyphSize.Width * fontScale);
glyphSize.Height = (int)(glyphSize.Height * fontScale);
string checkAlign = CheckAlign.ToString();
using (GraphicsPath gp = new GraphicsPath())
using (Pen pen = new Pen(Checked ? ReadOnlyCheckedColor : ReadOnlyUncheckedColor, 1.5f)
{
LineJoin = LineJoin.Round,
EndCap = LineCap.Round,
StartCap = LineCap.Round
})
{
gp.AddLine(new Point(3, 7), new Point(5, 10));
gp.AddLine(new Point(5, 10), new Point(8, 3));
float dx = checkAlign.EndsWith("Right") ? Math.Max(-4*fontScale, ClientSize.Width - glyphSize.Width - 4 * fontScale) :
checkAlign.EndsWith("Center") ? Math.Max(-4*fontScale, (ClientSize.Width - glyphSize.Width) / 2 - 4 * fontScale) : -4;
float dy = checkAlign.StartsWith("Bottom") ? Math.Max(-4*fontScale, ClientSize.Height - glyphSize.Height - 4*fontScale) :
checkAlign.StartsWith("Middle") ? Math.Max(-4*fontScale, (ClientSize.Height - glyphSize.Height) / 2 - 4*fontScale) : 0;
g.TranslateTransform(dx, dy);
g.ScaleTransform(1.5f*fontScale, 1.5f*fontScale);
g.DrawPath(pen, gp);
g.ResetTransform();
}
}
private void RenderText(Graphics g)
{
Size glyphSize = CheckBoxRenderer.GetGlyphSize(g, System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal);
float fontScale = Font.Size / 8.25f;
glyphSize.Width = (int)(glyphSize.Width * fontScale);
glyphSize.Height = (int)(glyphSize.Height * fontScale);
string checkAlign = CheckAlign.ToString();
using (StringFormat sf = new StringFormat())
{
string alignment = TextAlign.ToString();
sf.LineAlignment = alignment.StartsWith("Top") ? StringAlignment.Near :
alignment.StartsWith("Middle") ? StringAlignment.Center : StringAlignment.Far;
sf.Alignment = alignment.EndsWith("Left") ? StringAlignment.Near :
alignment.EndsWith("Center") ? StringAlignment.Center : StringAlignment.Far;
sf.FormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.NoClip;
Rectangle textRectangle = ClientRectangle;
if (checkAlign.EndsWith("Left"))
{
textRectangle.Width -= glyphSize.Width;
textRectangle.Offset(glyphSize.Width, 0);
}
else if (checkAlign.EndsWith("Right"))
{
textRectangle.Width -= glyphSize.Width;
textRectangle.X = 0;
}
g.DrawString(Text, Font, new SolidBrush(ForeColor), textRectangle, sf);
}
}
bool suppressCheckedChanged;
protected override void OnClick(EventArgs e)
{
if (ReadOnly) {
suppressCheckedChanged = true;
Checked = !Checked;
suppressCheckedChanged = false;
}
base.OnClick(e);
}
protected override void OnCheckedChanged(EventArgs e)
{
if (suppressCheckedChanged) return;
base.OnCheckedChanged(e);
}
}
NOTE: The code is not fully implemented, everything is kept as simple as possible. You can change the AlwaysShowCheck
property to choose the ReadOnly
unchecked state, it can be a gray tick mark or nothing. You can set the ReadOnly
to true
to make it Read-only visual.
AlwaysShowCheck
is set to true
(the ReadOnly unchecked state is indicated by a gray tick mark)
AlwaysShowCheck
is set to false
(the ReadOnly unchecked state is indicated by nothing)
Upvotes: 3
Reputation: 22052
So far the easiest solution (credits go to ShadowWizard) is to set ForeColor = Color.Gray
, this makes user think, what CheckBox
is disabled.
Compared to Enabled = false
, pluses are:
ToolTip
is working;No minuses.
Upvotes: 0