2

I'm trying to do some non-client area painting to get a MS Office like windowsform. I have one or two other posts of the sort, but here is the one that is done with Graphics.FromHwnd passing IntPtr.Zero as arg. I consulted a lot of information, that I tried and just simply cannot get it to work. Dwm functions, GetWindowDC, and or combination of these. Nothing works. Except this example that I post.

Public Class Form6
    Protected Overrides Sub WndProc(ByRef m As Message)
        MyBase.WndProc(m)

        Select Case m.Msg
            Case WinAPI.Win32Messages.WM_ACTIVATEAPP
                Me.Invalidate()
        End Select
    End Sub

    Private Sub Form6_LocationChanged(sender As Object, e As EventArgs) Handles Me.LocationChanged
        Me.Invalidate()
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)

        Dim usedColor As Color = Color.Beige
        Me.BackColor = usedColor
        Dim usedBrush As Brush = New SolidBrush(usedColor)

        'Dim hDC As IntPtr = WinAPI.GetWindowDC(Me.Handle.ToInt64)

        Using g As Graphics = Graphics.FromHwnd(IntPtr.Zero)
            'Using g As Graphics = Graphics.FromHdc(hDC)

            'Caption
            Dim rect As Rectangle = New Rectangle(Me.Left, Me.Top, Me.Width, SystemInformation.CaptionHeight + 2 * SystemInformation.FrameBorderSize.Height)
            g.FillRectangle(usedBrush, rect)

            'left border
            rect = New Rectangle(Me.Left, Me.Top + SystemInformation.CaptionHeight + 2 * SystemInformation.FrameBorderSize.Height, (Me.Width - Me.ClientSize.Width) / 2, Me.ClientSize.Height)
            g.FillRectangle(usedBrush, rect)

            'right border
            rect = New Rectangle(Me.Right - 2 * SystemInformation.FrameBorderSize.Width, Me.Top + SystemInformation.CaptionHeight + 2 * SystemInformation.FrameBorderSize.Height, (Me.Width - Me.ClientSize.Width) / 2, Me.ClientSize.Height)
            g.FillRectangle(usedBrush, rect)

            'bottom border
            'If on maximize this border isn't drawn, by default the windowsize "drawing" is correct
            If Me.WindowState <> FormWindowState.Maximized Then
                rect = New Rectangle(Me.Left, Me.Bottom - 2 * SystemInformation.FrameBorderSize.Width, Me.Width, 2 * SystemInformation.FrameBorderSize.Height)
                g.FillRectangle(usedBrush, rect)
            End If

        End Using

        'WinAPI.ReleaseDC(Me.Handle.ToInt64, hDC)
    End Sub

    Private Sub Form6_Resize(sender As Object, e As EventArgs) Handles Me.Resize
        Me.Invalidate()
    End Sub

    Private Sub Form6_SizeChanged(sender As Object, e As EventArgs) Handles Me.SizeChanged
        Me.Invalidate()
    End Sub
End Class

To generate graphics, I pass IntPtr.Zero for the hole screen. I tried the GetWindowDC API (commented in code), and nothing happens. The handle was passed as Me.Handle, Me.Handle.ToInt32 and .ToInt64, and no result.
The invalidate called is to try to draw in every situation possible.

Problems that bring me here:

  1. Form does not start up painted (can't figure it out);
  2. Resizing flickers a lot (probably because the handle is to the entire screen, even form being double-buffered);
  3. On the resizing, it's visible the painting over the cursor (again probably because of the handle for the graphics isn't the form's handle);
  4. On mouse over control buttons (min, max and close), all drawing disappears;

Although I can detect problems, I can't get other ways to work, like the famous GetWindowDC, regardless of how many examples I tried that don't work, or even the DWM functions.

Being the purpose of getting my own "Office" like form, I ask some help in getting improvements to this code or some other ideas, that are welcome.

[EDIT]

Another flavor of the above code. This code was tried in form_load event, but nothing happened.

Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)

    MyBase.OnPaint(e)


    If Not DwmAPI.DwmIsCompositionEnabled(True) Then
        Dim myHandle As IntPtr = WinAPI.FindWindow(vbNullString, Me.Text)

        Dim hDC As IntPtr = WinAPI.GetWindowDC(myHandle)

        Dim rect As WinAPI.RECT

        With rect
            .Left = 0
            .Right = Me.Width
            .Top = 0
            .Bottom = 30
        End With

        Using g As Graphics = Graphics.FromHdc(hDC)
            g.DrawString("TESTER", New Font(Me.Font.Name, 50), Brushes.Red, New Point(0, 0))
        End Using

        WinAPI.ReleaseDC(myHandle, hDC)
    End If
End Sub

The result is this: http://postimg.org/image/yyg07zf87/

As it would be clear, I want to have whatever if graphics drawn over titlebar and not under, although it's visible that the coords for the drawing are from full form area and not client area. If I doublebuffer the form, nothing is drawn. Any ideas?

Thanks for your patience. Best regards.

JDF
  • 129
  • 1
  • 12
  • When overriding low-level message stuff, you don't absentmindedly call the base's overridden version as the first thing in your method. You study documentation on the particular low-level stuff to learn if, when and how you should call the base version. – GSerg Sep 26 '15 at 18:52
  • In any case it would not appear your approach is [correct in principle](http://stackoverflow.com/q/5571072/11683). – GSerg Sep 26 '15 at 19:02
  • Thanks for you comment. In the edited version testing was done, and nothing of that code gets any drawing done in form events. Only on overriding procedures. So, principles wrong or not, this code doesn't run on form events. The edit version has form handle and not screen handle for drawing. – JDF Sep 27 '15 at 09:16
  • Splattering pixels directly to the screen like this solves one problem but creates five new ones. The boilerplate way to create custom frames is to start with a borderless window. And just paint the frame the way you want it, no hacks required. – Hans Passant Sep 27 '15 at 09:36

0 Answers0