【问题标题】:Read-only (visually) CheckBox只读(可视)复选框
【发布时间】:2013-11-23 01:02:00
【问题描述】:

我需要在屏幕上有 2 组控件:inputsoutputs(所以它们有 2 种状态:On关闭)。因此CheckBox 似乎是一个不错的选择。检查任何输出都会设置它。

但是,当显示 输入 时,不会有用户与之交互。用户只能看到它的值,不能改变它。

问题:如何让checkbos在视觉上显示为只读

可以考虑可能的解决方案:

  • 禁用CheckBox。不好:将没有工具提示(可能解决它?通过顶部的假面板?)并且视觉上禁用CheckBox 不好(我不想让用户认为它是禁用 )。
  • 使用不同的控件。哪一个? Label 没有很好的 On/Off 值占位符。 RadioButton 看起来不同,但它们通常意味着只有一个选择,而 inputs 的值是独立的。
  • 制作自己的组件。绘制整个CheckBox 有点矫枉过正(老实说,我不知道该怎么做才能拥有 Win7 外观)。是否可以轻松地将 ReadOnly 外观添加到框部分?

你们觉得呢?

【问题讨论】:

  • @CodeCaster,我的问题不同。我知道如何防止用户使用该控件,但它在视觉上仍然是相同的准备被单击的复选框。这是我的问题,向用户显示 对不起,但我是只读复选框

标签: c# winforms checkbox


【解决方案1】:

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

【讨论】:

  • 我可以通过设置AutoCheckbox = false轻松实现这一点。问题是关于视觉外观,而不是事件或动作。
  • @Sinatr 你的意思是AutoCheck,但这有什么问题呢?视觉外观将完全相同,用户将无法更改复选框状态,使其成为只读复选框。这不是你想要的吗?
  • @ShadowWizard 我认为 OP 意味着让它看起来像只读的。正如 TextBox 所做的那样,外观几乎没有沉闷
  • @SriramSakthivel,是的。
  • @Sinatr 只是给它不同的背景?只读文本框获取SystemColors.Control 颜色,您可以手动将其设置为复选框以获得相似的外观。
【解决方案2】:

到目前为止,最简单的解决方案(归功于 ShadowWizard)是设置 ForeColor = Color.Gray,这让用户认为,CheckBox 被禁用了。

Enabled = false相比,优点是:

  • ToolTip 正在工作;
  • 框部分看起来很漂亮(它对鼠标悬停有反应,并且无论何时选中或未选中都非常清晰可见)。

没有缺点。

【讨论】:

    【解决方案3】:

    您必须自己绘制所有内容。我认为您应该使用一些具有正确布局的控件来模仿它。这是给您的演示代码,请注意它正确支持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(ReadOnly 未选中状态由灰色勾号表示)

    AlwaysShowCheck 设置为false(ReadOnly 未选中状态由空表示)

    【讨论】:

    • 我有感觉,你只是分享一个你自己用过的组件。谢谢。测试了一下,效果还不错。工具提示正在工作,可以调整样式。 AutoSizeindeed very tricky
    • @Sinatr 实际上它不是我自己使用的组件,我只是在尝试回答您的问题时编写了它。我希望我以后所有的项目都使用WPF,而不是winforms,自定义容易得多,但是如果团队使用winforms,我仍然必须使用它。
    【解决方案4】:

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

    【讨论】:

    • 您能否在回答中提供更多信息?
    【解决方案5】:

    有一个解决方案是现有答案的组合。

    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

    【讨论】:

    • 使用AutoCheck 很好,禁用它以只读CheckBox 是个好主意。
    • 这应该是正确且被接受的答案。 King King 的解决方案将使其看起来受到检查,但潜在价值可能会发生变化。我相信 OP 想要一个不能更改其状态的复选框。
    【解决方案6】:

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

    【讨论】:

      【解决方案7】:

      不必写整个控件,只写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;
          }
      }
      

      【讨论】:

        【解决方案8】:

        这是一个旧帖子,但仍然很有用,所以这是我的解决方案。

        为了做出只读行为:

        • 当光标在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 是只读的在视觉上更明显,我们可以根据ReadOnly 属性更改ForColor

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

        这是前面代码的扩展版本,它根据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);
            }
        }
        

        【讨论】:

        • 您在列表中缺少第 4 点:让它直观地告诉“我是只读的”。解决“带有工具提示的禁用复选框”值得从其他方面进行投票。
        • 我编辑了答案并添加了代码的扩展版本,通过将文本颜色更改为灰色,使 CheckBox 看起来是只读的。
        猜你喜欢
        • 2021-11-19
        • 2010-09-14
        • 1970-01-01
        • 2023-02-06
        • 1970-01-01
        • 2010-10-29
        • 2011-11-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多