Reputation: 6385
I have an MDI Application in WinForms. One parent window and some child windows that are not maximized.
Since I was not satisfied how the application looked I chose to draw parts of the child windows myself by overriding OnPaint.
The childforms are derived from a BaseForm-Class I created (which inherits System.Windows.Forms.Form). Its FormBorderStyle
is set to None, DoubleBuffering is enabled. It also defines the general style, handles the drawing and moving/resizing of the form.
A stripped down version of the class, that still shows the odd behaviour, described below:
Public Class clsBaseForm
Inherits System.Windows.Forms.Form
Private MouseMoveStart As Point
Private IsMouseDown As Boolean
Private WithEvents dgv As DataGridView
Public Sub New()
MyBase.New()
'Used for moving
IsMouseDown = False
'Set form style options
Me.ResizeRedraw = True
Me.DoubleBuffered = True
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.None
'Add a datagridview
dgv = New DataGridView With {.Size = New Size(100, 100), .Location = New Point(35, 35)}
Me.Controls.Add(dgv)
End Sub
'Draw a border on the form
Protected Overrides Sub OnPaint(e As PaintEventArgs)
MyBase.OnPaint(e)
'Draw a black border
Using p As New Pen(Color.Black, 5)
p.Alignment = Drawing2D.PenAlignment.Inset
e.Graphics.DrawRectangle(p, Me.ClientRectangle)
End Using
End Sub
'Handle moving
Private Sub clsBaseForm_MouseDown(sender As Object, e As MouseEventArgs) Handles MyBase.MouseDown
MouseMoveStart = e.Location
IsMouseDown = True
End Sub
Private Sub clsBaseForm_MouseMove(sender As Object, e As MouseEventArgs) Handles Me.MouseMove
If IsMouseDown Then
Dim moved As Point = New Point(e.Location.X - MouseMoveStart.X, e.Location.Y - MouseMoveStart.Y)
Me.Location = New Point(Me.Location.X + moved.X, Me.Location.Y + moved.Y)
End If
End Sub
Private Sub clsBaseForm_MouseUp(sender As Object, e As MouseEventArgs) Handles Me.MouseUp
IsMouseDown = False
End Sub
End Class
I use this class by adding a new form to my project and changing the designer code to inherit from my baseform instead of directly from Windows.Forms.Form.
If you want to test out the problem, create a new project, add a parent window with IsMDIContainer
property set to true. Create some child forms as described above and in the Child Form's Load event add Me.MDIParent = ParentForm
.
This works quite well. However with several MDI-Children active in my parent window I experience the problem, that when I move one child over another the self drawn parts are smeared over controls of the underlying controls (but not on the form itself). I added a datagridview in the example above to show it.
It looks like this (The smear appears on a datagridview that's on the underlying child, but also appears on labels and so on):
I read, that you can turn on the WS_EX_COMPOSITED
WindowStyle to prevent this. However when I do this:
Protected Overrides ReadOnly Property CreateParams As CreateParams
Get
Dim params As CreateParams = MyBase.CreateParams
params.ExStyle = params.ExStyle Or &H2000000
Return params
End Get
End Property
the drawing problem is gone, but at the same time the forms get completely screwed up when I move them beyond the bounds of the parent, so this can't really be the way to go.
The information regarding MDI I found is unfortunately very limited, but I can't be the first to run into this.
Every advise in both VB.NET and C# is very welcome.
Edit 1:
A possible workaround I found was to just add Me.MDIParent.Refresh
to the MouseMove
method that performs the moving of the form. This works but makes moving the form very stuttering because the whole application is redrawn every time. So that doesn't satisfy me.
Edit 2:
According to DonBoitnott answer I tried implementing a redraw scheme:
In the BaseForm I added an Event that gets fired when the form is moved
Public Class clsBaseForm
Inherits System.Windows.Forms.Form
Public Event FormMoved()
[...]
In the MDI-Parent form I added a new method:
Public Sub CallNeedRedraw()
For Each c As Form In Me.MdiChildren
c.Invalidate()
Next
End Sub
And in each of the child forms I added an event handler:
Private Sub frmFiles_FormMoved() Handles MyBase.FormMoved
frmMain.CallNeedRedraw()
End Sub
So every time a child moves, every child is invalidated. This however does not produce a satisfactory result: It basically looks as before. If I change c.Invalidate to c.Refresh the child forms are drawn correctly, however the parent isn't (smearing) and the movement is stuttering again. It's basically all a little worse than what I suggested under "Edit 1".
Upvotes: 1
Views: 1184
Reputation: 11035
You just need to ensure that all child windows get a Paint
message when any child moves. You cannot simply redraw the one that is moving, you must redraw them all. Add an event to your parent form that the moving child can call when it moves. That method should then call Invalidate()
for all other children.
Upvotes: 2