我需要在屏幕上有两组控件:inputs和outputs(因此它们有2种状态:On或Off)。因此
CheckBox
似乎是一个不错的选择。检查任何输出都会对其进行设置。
但是,当显示 inputs 时,将不会有用户与之交互。用户只能看到它的值,不能更改它。
可以考虑可能的解决方案:
CheckBox
。不好:不会有工具提示(可以解决它吗?通过顶部的假面板?)并且视觉禁用CheckBox
不太好(而且我不想让用户认为它是禁用)。Label
没有很好的开/关值占位符。 RadioButton
看起来不同,但它们通常意味着有一个选择,而 inputs 的值是独立的。CheckBox
有点大材小用了(老实说,我不知道如何做到有Win7外观)。是否可以轻松地在盒子部分添加ReadOnly
外观?大家觉得怎么样?
有一个解决方案是现有答案的组合。
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.");
结果是
CheckBox
Checked
值发生更改Tooltip
一切都必须自己画。我认为你应该使用一些布局正确的控件来模仿它。这是给您的演示代码,请注意,它不正确支持
AutoSize
。因为绘制的东西总是比默认的东西宽(AutoSize
使用),实现AutoSize
并不容易,如果你不太关心AutoSize
,这将是一个很好的控制你:
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);
}
}
注意:代码尚未完全实现,一切都尽可能简单。您可以更改
AlwaysShowCheck
属性来选择 ReadOnly
未选中状态,它可以是 灰色刻度线 或 无任何。您可以将 ReadOnly
设置为 true
以使其成为 只读视觉效果。
AlwaysShowCheck
设置为 true
(只读未选中状态由灰色勾号表示)
AlwaysShowCheck
设置为false
(ReadOnly未选中状态什么也没有表示)
这是一篇旧帖子,但仍然有用,所以这是我的解决方案。
为了实现只读行为:
CheckBox
我们可以继承
CheckBox
类并禁用鼠标和键盘交互:
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);
}
}
为了在视觉上明显看出
CheckBox
是只读的,我们可以根据ForColor
属性更改ReadOnly
。
注意:更改
ForColor
仅更改文本颜色,复选标记的颜色只能通过覆盖 OnPaint
方法并重新绘制 CheckBox
来更改(据我所知)。
这是之前代码的扩展版本,根据
ForColor
属性更改 ReadOnly
:
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);
}
}
到目前为止,最简单的解决方案(归功于 ShadowWizard)是设置
ForeColor = Color.Gray
,这让用户思考,CheckBox
被禁用了。
与
Enabled = false
相比,优点是:
ToolTip
正在工作;没有缺点。
不需要写整个控件,只需写
Checkbox
的派生即可。
将
ReadOnly
属性添加到控件中,这会导致控件在可以更改其值时进行处理。
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;
}
}
您可以为单击
CheckBox
的事件提供侦听器,因为可以在运行时取消其通常的流程。
使用不带
CheckBox
的 text
以及旁边的普通 Label
也可以实现“只读”视觉效果(灰色复选框但黑色标签)。
请注意,如果启用了
CheckBox
,甚至需要将 onClick 添加到 Label
以模仿原始行为。
在属性表中将选择模式设置为无。
Visual Studio 现在可以在以下位置使用: 属性 -> 属性 -> 只读:)