简体   繁体   English

只读(视觉)复选框

[英]Read-only (visually) CheckBox

I need to have 2 groups of controls on the screen: inputs and outputs (so they have 2 states: On or Off ).我需要在屏幕上有 2 组控件:输入输出(因此它们有 2 个状态: OnOff )。 Thus CheckBox seems to be a good choice.因此CheckBox似乎是一个不错的选择。 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.用户只能看到它的值,不能改变它。

Question: how to make checkbos visually appears as read-only ?问题:如何使 checkbos 在视觉上显示为只读

Could think about possible solutions:可以考虑一下可能的解决方案:

  • Make CheckBox disabled.禁用CheckBox 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 ).不好:将没有工具提示(可能解决它?通过顶部的假面板?)并且视觉上禁用的CheckBox不好(我不想让用户认为它已禁用)。
  • Use different control.使用不同的控件。 Which one?哪一个? Label doesn't have nice placeholder for the On/Off value. Label没有很好的开/关值占位符。 RadioButton look differently, but they usually means there is a single choice out of many, while values of inputs are independent. RadioButton看起来不同,但它们通常意味着只有一个选择,而输入的值是独立的。
  • Making own component.制作自己的组件。 Drawing the whole CheckBox is a bit overkill (and honestly, I don't know how to do it to have Win7 appearance).绘制整个CheckBox有点矫枉过正(老实说,我不知道如何做到这一点才能拥有 Win7 外观)。 Would it be possible to add only ReadOnly appearance to the box part easily?是否可以轻松地将ReadOnly外观添加到盒子部分?

What do you guys think?你们有什么感想?

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结果是一个CheckBox

  • appears disabled with gray text显示为灰色文本禁用
  • prevents the Checked value from changing when clicked防止Checked值在单击时发生变化
  • supports a Tooltip支持Tooltip

This is an old post but still can be usefull so here is my solution.这是一篇旧帖子,但仍然有用,所以这是我的解决方案。

In order to make a read-only behavior:为了使只读行为:

  • Disable highlight when the cursor is over the CheckBox当光标在CheckBox时禁用突出显示
  • Disable reacting (logically or visibly) to a mouse click禁用对鼠标点击的反应(逻辑或可见)
  • Have tooltips enabled启用工具提示

We can inherit the CheckBox class and disable mouse and keyboard interaction:我们可以继承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);
    }
}

In order to make it visually apparent that the CheckBox is read-only, we can change the ForColor according to the ReadOnly property.为了使CheckBox是只读的,在视觉上显而易见,我们可以根据ReadOnly属性更改ForColor

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).注意:更改ForColor只会更改文本颜色,复选标记的颜色只能通过覆盖OnPaint方法并重新绘制CheckBox来更改(据我所知)。

Here is an extended version of the previous code that changes the ForColor according to the ReadOnly property:这是先前代码的扩展版本, ForColor根据ReadOnly属性更改ForColor

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);
    }
}

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.这是您的演示代码,请注意它正确支持AutoSize 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:因为绘制的东西总是比默认的东西( 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);
    }        
}

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 .您可以更改AlwaysShowCheck属性以选择ReadOnly未选中状态,它可以是灰色刻度线没有 You can set the ReadOnly to true to make it Read-only visual .您可以将ReadOnly设置为true以使其成为Read-only visual

AlwaysShowCheck is set to true (the ReadOnly unchecked state is indicated by a gray tick mark) AlwaysShowCheck设置为true (ReadOnly 未选中状态由灰色勾号指示)

在此处输入图片说明

AlwaysShowCheck is set to false (the ReadOnly unchecked state is indicated by nothing) AlwaysShowCheck设置为false (ReadOnly 未选中状态由AlwaysShowCheck指示)

在此处输入图片说明

您可以为单击CheckBox的事件提供侦听器,因为可以在运行时取消其通常的流程。

So far the easiest solution (credits go to ShadowWizard) is to set ForeColor = Color.Gray , this makes user think, what CheckBox is disabled.到目前为止,最简单的解决方案(学分转到 ShadowWizard)是设置ForeColor = Color.Gray ,这让用户思考,什么CheckBox被禁用。

Compared to Enabled = false , pluses are:Enabled = false相比,优点是:

  • ToolTip is working; ToolTip正在工作;
  • box part looks pretty (it react on mouse hovering and is very clearly seen whenever is checked or unchecked).框部分看起来很漂亮(它对鼠标悬停做出反应,无论何时选中或取消选中都非常清楚)。

No minuses.没有缺点。

It is not necessary to write the entire control, just write a derivative of Checkbox .没必要写整个控件,只写一个Checkbox的派生类即可。

A ReadOnly property is added to the control, this causes the control to handle when it can change its value. 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;
    }
}

在属性表中,只需将选择模式设为无。

Visual Studio 现在可以在以下位置使用它:属性 -> 属性 -> 只读 :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM