Sub-Classed Groupbox无法正确绘制内容

时间:2014-12-01 19:45:07

标签: c# .net vb.net user-controls gdi+

  

SCENARIO

WinForms 中,我对GroupBox进行了细分,以更改此控件的边框颜色。

  

问题

设计模式(在VisualStudio的可视化构建器中),如果我对Groupbox内的控件执行任何类型的更改,请点击每个控件都要更改textfont,然后我的Groupbox重新绘制控件,如下所示:

enter image description here

注意:在使控件无效后,它会再次正确重绘。

  

问题

当所有者绘制存储像GroupBox这样的控件集合的容器时,这是一个已知问题?

我在OnPaint方法中缺少修复此绘画问题的方法吗?

  

CODE

VB版:

''' <summary>
''' Handles the <see cref="E:Paint"/> event.
''' </summary>
''' <param name="e">A <see cref="T:PaintEventArgs"/> that contains the event data.</param>
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)

    '  MyBase.OnPaint(e)
    Me.DrawBorder(e)

End Sub

''' <summary>
''' Draws a border on the control surface.
''' </summary>
Private Sub DrawBorder(ByVal e As PaintEventArgs)

    ' The groupbox header text size.
    Dim textSize As Size = TextRenderer.MeasureText(Me.Text, Me.Font)

    ' The width of the blankspace drawn at the right side of the text.
    Dim blankWidthSpace As Integer = 3

    ' The thex horizontal offset.
    Dim textOffset As Integer = 7

    ' The rectangle where to draw the border.
    Dim borderRect As Rectangle = e.ClipRectangle
    With borderRect
        .Y = .Y + (textSize.Height \ 2)
        .Height = .Height - (textSize.Height \ 2)
    End With

    ' The rectangle where to draw the header text.
    Dim textRect As Rectangle = e.ClipRectangle
    With textRect
        .X = .X + textOffset
        .Width = (textSize.Width - blankWidthSpace)
        .Height = textSize.Height
    End With

    ' Draw the border.
    ControlPaint.DrawBorder(e.Graphics, borderRect, Me.borderColor1, Me.borderStyle1)

    ' Fill the text rectangle.
    e.Graphics.FillRectangle(New SolidBrush(Me.BackColor), textRect)

    ' Draw the text on the text rectangle.
    textRect.Width = textSize.Width + (blankWidthSpace * 2) ' Fix the right side space.
    e.Graphics.DrawString(Me.Text, Me.Font, New SolidBrush(Me.ForeColor), textRect)

End Sub

C#版本:

/// <summary>
/// Handles the <see cref="E:Paint"/> event.
/// </summary>
/// <param name="e">A <see cref="T:PaintEventArgs"/> that contains the event data.</param>

protected override void OnPaint(PaintEventArgs e)
{
    //  MyBase.OnPaint(e)
    this.DrawBorder(e);

/// <summary>
/// Draws a border on the control surface.
/// </summary>

private void DrawBorder(PaintEventArgs e)
{
    // The groupbox header text size.
    Size textSize = TextRenderer.MeasureText(this.Text, this.Font);

    // The width of the blankspace drawn at the right side of the text.
    int blankWidthSpace = 3;

    // The thex horizontal offset.
    int textOffset = 7;

    // The rectangle where to draw the border.
    Rectangle borderRect = e.ClipRectangle;
    var _with1 = borderRect;
    _with1.Y = _with1.Y + (textSize.Height / 2);
    _with1.Height = _with1.Height - (textSize.Height / 2);

    // The rectangle where to draw the header text.
    Rectangle textRect = e.ClipRectangle;
    var _with2 = textRect;
    _with2.X = _with2.X + textOffset;
    _with2.Width = (textSize.Width - blankWidthSpace);
    _with2.Height = textSize.Height;

    // Draw the border.
    ControlPaint.DrawBorder(e.Graphics, borderRect, this.borderColor1, this.borderStyle1);

    // Fill the text rectangle.
    e.Graphics.FillRectangle(new SolidBrush(this.BackColor), textRect);

    // Draw the text on the text rectangle.
    textRect.Width = textSize.Width + (blankWidthSpace * 2);
    // Fix the right side space.
    e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(this.ForeColor), textRect);

}

//=======================================================
//Service provided by Telerik (www.telerik.com)
//=======================================================

1 个答案:

答案 0 :(得分:1)

您的代码和方法实际上有几个问题:

' The rectangle where to draw the border.
Dim borderRect As Rectangle = e.ClipRectangle
' and:
' The rectangle where to draw the header text.
Dim textRect As Rectangle = e.ClipRectangle

如果您选择一个子控件并稍微移动它,您将遇到图像显示的问题。原因是Windows不会要求控件重新绘制整个自我以进行如此小的更改。相反,它会使子控件周围的小区域无效,并将其作为ClipRectangle传递。在某些情况下,它实际上是不正确的。

因此,基于GroupBox定位e.ClipRectangle边框Rect将绘制边框的一部分,并且刚刚移动的控件周围区域的标题。使用Bounds调整各种事项


那应该摆脱上面提到的问题,但它会发现其他几个问题。例如,如果主题或样式调用3D类型边框,则会绘制2条边框线,并且您的计算更多地依赖于航位推算。

接下来,如果您尝试覆盖默认行为,我不确定Control.DrawBorder是否完全合适。这将尊重由您试图覆盖的主题/样式等确定的某些事物。

我还可以使用TextRenderer来更好地显示标题。

要做你想做的事,你可能不得不接管default GroupBoxRenderer所做的一切。如果你将bordercolor更改为White或Fuschia之类的东西,你会发现正常的渲染仍在进行,你的代码只是试图绘制它的顶部。

即使您正在运行,FlatStyleColor,主题或样式的某些其他组合也会失败,因为您的代码正在运行没有考虑到这一点。

这是我使用过的,但它只是导致了一场有着不同问题的Whack-A-Mole游戏:

Dim textRect As New Rectangle With {.X = 0,
                                    .Y = 2,
                                   .Height = textSize.Height,
                                    .Width = (textSize.Width - blankWidthSpace)
                                   }
Dim borderRect As New Rectangle With {.X = 1, .Y = (textSize.Height \ 2) + 1,
                                 .Width = Bounds.Width - 2,
                                 .Height = Bounds.Height - (textSize.Height \ 2) - 2}

TextRenderer.DrawText(e.Graphics, " " & MyBase.Text & " ", MyBase.Font,
                          textRect, MyBase.ForeColor, MyBase.BackColor)

这似乎与您的other question有关“丑陋的白色边框”有关。尝试通过继承(一堆)控件来修复或覆盖主题并不是最好的方法。

我会考虑提供符合您口味的备用Aero主题。此外,你是谁说所有用户都同意它是“丑陋的”(我认为它使表格看起来很忙但不完全“丑陋”)。