【问题标题】:Sub-Classed Groupbox does not properly draw the contentsSub-Classed Groupbox 未正确绘制内容
【发布时间】:2014-12-01 19:45:07
【问题描述】:

场景

WinForms 中,我将 GroupBox 子类化以更改此控件的边框颜色。

问题

设计模式(在 VisualStudio 的 visual builder 中),如果我对 Groupbox 中的控件进行任何类型的更改,可以说单击每个控件要更改文本字体,然后我的 Groupbox 会像这样重绘控件:

注意:在使控件无效后,它会重新正确地重新绘制。

问题

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

我在 OnPaint 方法中缺少什么来解决这个绘画问题?

代码

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)
//=======================================================

【问题讨论】:

  • 什么是borderStyle1?这不是我期望在那里看到的 arg。确保子控件也没有边框。图片中的黄色是什么 - 是转载的标题吗?
  • @Plutonix 感谢您的评论。 1) arg 是 ButtonBorderStyle: msdn.microsoft.com/en-us/library/… 2) 是的,似乎黄色标题在内部控件的位置被重新绘制(并且组框边框也被重新绘制),我不知道为什么会这样:(
  • 我不认为这是显示代码的结果。
  • @Plutonix 应该是因为这是我试图管理的唯一功能(bordercolor)没有其他重要的东西包含完整的源代码,无论如何这里是完整的源代码:stackoverflow.com/questions/27230168/…(见我的回答,有)

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


【解决方案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 传递。在某些情况下,它实际上是不正确的。

因此,基于e.ClipRectangle 定位GroupBox 边框矩形将在刚刚移动的控件周围区域的顶部绘制部分边框和标题。使用Bounds 调整了各种东西


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

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

我还可以使用TextRenderer 让标题显示得更好。

为了做你想做的事,你可能不得不接管default GroupBoxRenderer 所做的一切。如果您将边框颜色更改为 White 或 Fuschia 之类的颜色,您会看到正常渲染仍在进行,您的代码只是试图在其顶部进行绘制。

即使您 this 正常工作,FlatStyleColor、Theme 或 Style 的某些 other 组合也会失败,因为您的代码没有将其纳入考虑。

这是我使用的,但它只会导致出现不同问题的打地鼠游戏:

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 主题。此外,你是谁说所有用户都会同意它“丑陋”(我认为它使表单看起来很忙但并不完全“丑陋”)。

【讨论】:

  • 我认为它可以理解为“丑陋”的上下文,它对我来说很丑,在我看来。关于其余的我只能说谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-25
  • 2016-09-26
  • 1970-01-01
  • 2012-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多