在按钮单击事件中绘制控件的边框

5

当用户点击我的验证按钮(在我的C#,WinForm,.net 3.5应用程序中),如果某个控件为空,我想在其周围绘制边框。例如,一个名为tbxLastName的文本框。我认为我需要像这样做 ->

ControlPaint.DrawBorder(Graphics.FromHwnd(this.Handle), 
    tbxLastName.ClientRectangle, Color.Firebrick, ButtonBorderStyle.Solid);

很遗憾,我不知道该将图形对象放在哪里,因为我的代码什么也没做。
我查看了所有的示例,包括这个MSDN - HERE,都在Paint事件中使用此代码。如下所示:
private void panel1_Paint(object sender, PaintEventArgs e)
{    
    ControlPaint.DrawBorder(e.Graphics, this.panel1.ClientRectangle, 
        Color.DarkBlue, ButtonBorderStyle.Solid);
}

然而,我只想在满足特定条件时让边框出现,这是由Button_Click触发的。


许多建议建议使用容器对象来保存文本框并调用它的Paint_Event。我做到了这一点,一个框出现了,但不是在控件周围。它出现在容器控件的左上角。这是我正在做的-->

    private void grpImmunizationCntrl_Paint(object sender, PaintEventArgs e)
    {
        if (lkuNOImmunizationReason.Text.Equals(string.Empty)
        {
           ControlPaint.DrawBorder(
                    e.Graphics, lkuNOImmunizationReason.ClientRectangle,
                        Color.Firebrick, ButtonBorderStyle.Solid);
        }
    }

编辑

这是我结合这里的建议和我的经验得出的结果。

    public static void HighlightRequiredFields(Control container, Graphics graphics, Boolean isVisible)
    {
        Rectangle rect = default(Rectangle);
        foreach (Control control in container.Controls)
        {
            if (control.Tag is string && control.Tag.ToString() == "required")
            {
                rect = control.Bounds;
                rect.Inflate(3, 3);
                if (isVisible && control.Text.Equals(string.Empty))
                {
                    ControlPaint.DrawBorder(graphics, rect, Color.FromArgb(173,216,230), ButtonBorderStyle.Solid);
                }
                else
                {
                    ControlPaint.DrawBorder(graphics, rect, container.BackColor, ButtonBorderStyle.None);
                }
            }

            if (control.HasChildren)
            {
                foreach (Control ctrl in control.Controls)
                {
                    HighlightRequiredFields(ctrl, graphics, isVisible);
                }
            }
        }
    }

我将这个函数从任何需要的容器的Paint_Event中调用。


图形界面是 Win32 DC 的等效物。http://msdn.microsoft.com/zh-cn/library/dd162467(VS.85).aspx - i_am_jorf
5个回答

5

您可以在表单中使用动作列表字段,并添加或删除自定义绘图:

// field
List<Action<Graphics>> drawings = new List<Action<Graphics>>();

// on click event:
drawings.Add(delegate(Graphics g) {
    var rect = tbxLastName.Bounds;
    rect.Inflate(1, 1); // make rectange a bit larger than textbox
    ControlPaint.DrawBorder(g, rect, 
    Color.DarkBlue, ButtonBorderStyle.Solid);
});
// make sure you added only once or clear before
panel1.Refresh(); // refresh panel to force painting


// Paint method:
foreach (var draw in drawings) {
    draw(e.Graphics);
}

这样您就可以添加多个边框。

2
+1 指出 .Inflate 方法,我以前从未注意到,非常方便! - Shawn Steward

2
我刚刚用VB.Net做了类似的事情,参考了这个线程。我在每个控件组周围放置了一个Panel容器,在PanelOnPaint处理程序中,我循环遍历Panel中的所有子控件,如果它们具有tag="required",则绘制其周围的边框。仅在编辑或新建时显示边框,因此我创建了fVisible参数来切换这些边框的开和关。当我想触发此操作时,我调用Panel.Refresh()以触发OnPaint事件。这样,我只需要在设计时设置所需控件上的标签,并添加一个容器Panel的处理程序,就可以动态地使其全部工作。
这是我从所有面板的OnPaint事件处理程序中调用的共享函数。(对不起,我知道您正在使用C#,但这很基本。)
Friend Sub HighlightRequiredFields(ByVal pnlContainer As Panel, ByVal gr As Graphics, ByVal fVisible As Boolean)
    Dim rect As Rectangle
    For Each oControl As Control In pnlContainer.Controls
        If TypeOf oControl.Tag Is String AndAlso oControl.Tag.ToString = "required" Then
            rect = oControl.Bounds
            rect.Inflate(1, 1)
            If fVisible Then
                ControlPaint.DrawBorder(gr, rect, Color.Red, ButtonBorderStyle.Solid)
            Else
                ControlPaint.DrawBorder(gr, rect, pnlContainer.BackColor, ButtonBorderStyle.None)
            End If
        End If
        If TypeOf oControl Is Panel Then HighlightRequiredFields(DirectCast(oControl, Panel), gr, fVisible)
    Next
End Sub

有趣的方法,谢谢。我想我会将您的方法与我下面发布的内容结合起来,因为如果输入文本,我需要取消高亮显示。不幸的是,我们的模型非常不寻常,他们不会先点击“编辑”或“新建”,而是在输入数据后再进行操作。很奇怪,我知道! - Refracted Paladin
哈哈,那绝对很奇怪!很高兴能帮忙。 - Shawn Steward

1

文本框不会调用OnPaint方法(请参见this pageNote部分)。解决方法是将文本框放置在稍大的面板中。然后,每当单击按钮时更改背景颜色。MSDN论坛上有一些解决方案。

编辑 为了澄清面板解决方案,只需创建一个面板并将文本框添加到其中: 例如:

private void MyForm_Load(object sender, EventArgs e)
{
     myPanel.Controls.Add(tbxLastName); //Make sure the panel size is slightly bigger than the text box (so that it looks like a border)
}

然后,处理您的按钮点击事件:

private void myButton_Click(object sender, EventArgs e)
    {
        if (tbxLastName.Text == "")
        {
            myPanel.BackColor = Color.Red;
        }
        else
        {
            myPanel.BackColor = Color.Transparent;
        }
    }

如果它是一个具有OnPaint方法的控件怎么办?此外,当我将其放置在GroupBox中时,为什么会在绘制Rect时丢失控件(请参见OP底部的编辑)?谢谢。 - Refracted Paladin
除非你有使用OnPaint方法的理由,否则你可以尝试一下我更新后答案中的方法。 - keyboardP
哦,我现在明白你在做什么了。我得考虑一下这个问题。我有大约30到40个控件可能会被画上一个框。我在想如何在这个规模上实现它。 - Refracted Paladin
啊,我明白了。我以为只是几个控件。如果您在 ControlPaint.DrawBorder( e.Graphics, lkuNOImmunizationReason.ClientRectangle... 之后设置一个断点,那么 ClientRectangle 属性的值是多少? - keyboardP

1
    protected override void OnTextChanged(EventArgs e)
    {
        base.OnTextChanged(e);
        if (string.IsNullOrEmpty(Text))
        {
            this.BorderStyle = BorderStyle.FixedSingle;
        }
        else 
        {
            this.BorderStyle = BorderStyle.Fixed3D;
        }
    }

1
你的矩形"缺失"文本框的原因是ClientRectangle仅包含控件的大小,而不包括位置。请尝试使用以下代码:
private void grpImmunizationCntrl_Paint(object sender, PaintEventArgs e)
{
    if (lkuNOImmunizationReason.Text.Equals(string.Empty)
    {
       ControlPaint.DrawBorder(
                e.Graphics, new Rectangle(lkuNOImmunizationReason.Left, lkuNOImmunizationReason.Top, lkuNOImmunizationReason.ClientRectangle.Width, lkuNOImmunizationReason.ClientRectangle.Height),
                    Color.Firebrick, ButtonBorderStyle.Solid);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接