Reputation: 20464
SCENARIO
In WinForms, I've sub-classed a GroupBox
to change the border color of this control.
PROBLEM
At design mode (in the visual builder of VisualStudio), if I perform any kind of changes to the controls inside my Groupbox
lets say click on each control to change the textfont then my Groupbox
redraws the controls like this:
Note: After invalidating the control it redraws again properly.
QUESTION
This is a known issue when owner-drawing a container that stores a control collection like a GroupBox
?
What I'm missing to do in the OnPaint
method to fix this painting issue?
CODE
VB Version:
''' <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# version:
/// <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)
//=======================================================
Upvotes: 0
Views: 551
Reputation: 38875
There are actually several things wrong with your code and approach:
' 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
If you select a child control and move it just a bit, you will get the problem your image shows. The reason is that Windows does not ask the control to repaint its entire self for such a small change. Instead, it invalidates a small region around the child control(s) and passes it as the ClipRectangle
. In some cases, it isnt actually correct.
So, locating the GroupBox
border Rect based on e.ClipRectangle
will draw parts of the border and Caption at the top of the area around the control just moved. Use Bounds
adjusted for the various things
That should get rid of the problem mentioned, but it will uncover several others. For instance, if the theme or style calls for a 3D type border, 2 border lines are drawn and your calculations rely more on dead reckoning.
Next, I am not sure Control.DrawBorder
is entirely appropriate if you are trying to override default behavior. This is going to respect certain things determined by themes/styles etc which are what you are trying to override.
I can also get the Caption to display better using TextRenderer
.
To do what you want, you are probably going to have to take over everything which the default GroupBoxRenderer
does. If you change your bordercolor to something like White or Fuschia, you'll see that the normal rendering is still taking place, your code is just trying to draw over the top of it.
Even if you got this working, some other combination of FlatStyle
, Color
, Theme or Style will fail because your code is taking none of that into consideration.
Here is what I used, but it just leads to a game of Whack-A-Mole with different issues arising:
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)
This seems related to your other question about "ugly white borders". Trying to fix or override a Theme by subclassing a (bunch of) control(s) is not the best way to go about it.
I would look into providing an alternate Aero-like theme which fits your taste. Besides, who are you to say that all users will agree it is "ugly" (I think it makes the form look busy but not exactly "ugly").
Upvotes: 1