Jens
Jens

Reputation: 6385

Drawing parts of MDI Client windows: Drawn on other child's controls as well (moving/resizing)

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):

enter image description here

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

Answers (1)

DonBoitnott
DonBoitnott

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

Related Questions