0

I am currently creating a WPF Window which preserves its aspect ratio when resized.

My first idea was to handle the WM_SIZE Message and set the Size there, but this produced annoying flickering. So I tried to change the lParam of WM_Size which produced AccessViolationExceptions. Same happend with manipulating lParam on WM_SIZING.

AspectWindow.vb

Imports System.Runtime.InteropServices
Imports System.Windows.Interop


Public Class AspectWindow
    Inherits Window

    Private AspectRatio As Double
    Private ResizeDirection As Direction

    Enum Direction
        Horizontal
        Vertical
    End Enum

    Enum WM
        WM_SIZE = &H5
        WM_SIZING = &H214
        WM_EXITSIZEMOVE = &H232
        WM_NCCALCSIZE = &H83
    End Enum

    Enum WMSZ
        WMSZ_BOTTOM = &H6
        WMSZ_BOTTOMLEFT = &H7
        WMSZ_BOTTOMRIGHT = &H8
        WMSZ_LEFT = &H1
        WMSZ_RIGHT = &H2
        WMSZ_TOP = &H3
        WMSZ_TOPLEFT = &H4
        WMSZ_TOPRIGHT = &H5
    End Enum

    Enum WVR
        WVR_VALIDRECTS = &H400
    End Enum

    Enum IntPtrBool
        [True] = 1
        [False] = 0
    End Enum

    <StructLayout(LayoutKind.Sequential)>
    Friend Structure RECT
        Public left As Long
        Public top As Long
        Public right As Long
        Public bottom As Long
    End Structure

    Protected Overrides Sub OnSourceInitialized(e As EventArgs)
        AspectRatio = Me.ActualWidth / Me.ActualHeight
        MyBase.OnSourceInitialized(e)
        Dim source As HwndSource = TryCast(HwndSource.FromVisual(Me), HwndSource)
        If source IsNot Nothing Then
            source.AddHook(New HwndSourceHook(AddressOf WinProc))
        End If
    End Sub

    Private Function WinProc(hwnd As IntPtr, msg As Integer, wParam As IntPtr, lParam As IntPtr, ByRef handled As Boolean) As IntPtr
        Select Case msg
            Case WM.WM_SIZING
                Select Case wParam
                    Case WMSZ.WMSZ_BOTTOM, WMSZ.WMSZ_TOP
                        ResizeDirection = Direction.Vertical
                        Exit Select
                    Case WMSZ.WMSZ_LEFT, WMSZ.WMSZ_RIGHT
                        ResizeDirection = Direction.Horizontal
                        Exit Select

                End Select

                If Not lParam = Nothing Then

                    Dim Rect As RECT = Marshal.PtrToStructure(Of RECT)(lParam)

                    If ResizeDirection = Direction.Horizontal Then
                        Rect.bottom = Rect.top
                    Else
                        Rect.right = Rect.top
                    End If

                    'Manipulating Resize Rectangle
                    Rect.top = 1
                    Rect.bottom = 2
                    Rect.left = 3
                    Rect.right = 4 

                    Marshal.StructureToPtr(Of RECT)(Rect, lParam, False)

                End If

                Return IntPtrBool.True
        End Select

        Return IntPtr.Zero
    End Function


End Class
Lloyd
  • 29,197
  • 4
  • 84
  • 98
Error404
  • 719
  • 9
  • 30
  • 1
    It bombs in a very hard to diagnose way because you did not declare RECT correctly. It is a VB6 declaration as posted, in VB.NET the members are `Integer`, not Long. The memory corruption caused by writing too much data is drastically hard to debug. Use the pinvoke.net website to find decent declarations. You also *really* want to put Option Strict On at the top of the source file so the compiler can help you get the minor mistakes taken care of. – Hans Passant Oct 16 '16 at 23:29

1 Answers1

-1

Solved it with handling WM_WINDOWPOSCHANGING:

Imports System.Runtime.InteropServices
Imports System.Windows.Interop


Public Class AspectWindow
    Inherits Window

    Private AspectRatio As Double
    Private ResizeDirection As WMSZ

    Enum WM
        WM_SIZING = &H214
        WM_WINDOWPOSCHANGING = &H46
    End Enum

    Enum WMSZ
        WMSZ_BOTTOM = &H6
        WMSZ_BOTTOMLEFT = &H7
        WMSZ_BOTTOMRIGHT = &H8
        WMSZ_LEFT = &H1
        WMSZ_RIGHT = &H2
        WMSZ_TOP = &H3
        WMSZ_TOPLEFT = &H4
        WMSZ_TOPRIGHT = &H5
    End Enum

    Enum IntPtrBool
        [True] = 1
        [False] = 0
    End Enum

    <StructLayout(LayoutKind.Sequential)>
    Friend Structure WINDOWPOS
        Public hwnd As IntPtr
        Public hwndInsertAfter As IntPtr
        Public x As Integer
        Public y As Integer
        Public cx As Integer
        Public cy As Integer
        Public flags As Integer
    End Structure

    Protected Overrides Sub OnSourceInitialized(e As EventArgs)
        AspectRatio = Me.ActualWidth / Me.ActualHeight
        MyBase.OnSourceInitialized(e)
        Dim source As HwndSource = TryCast(HwndSource.FromVisual(Me), HwndSource)
        If source IsNot Nothing Then
            source.AddHook(New HwndSourceHook(AddressOf WinProc))
        End If
    End Sub

    Private Function WinProc(hwnd As IntPtr, msg As Integer, wParam As IntPtr, lParam As IntPtr, ByRef handled As Boolean) As IntPtr
        Select Case msg
            Case WM.WM_SIZING
                ResizeDirection = wParam

                Return IntPtrBool.True

            Case WM.WM_WINDOWPOSCHANGING
                Dim Pos = Marshal.PtrToStructure(Of WINDOWPOS)(lParam)

                Dim Last = Pos

                If Not ResizeDirection = WMSZ.WMSZ_TOP AndAlso Not ResizeDirection = WMSZ.WMSZ_BOTTOM Then
                    Pos.cy = Pos.cx / AspectRatio
                End If
                If Not ResizeDirection = WMSZ.WMSZ_RIGHT AndAlso Not ResizeDirection = WMSZ.WMSZ_LEFT Then
                    Pos.cx = Pos.cy * AspectRatio
                End If
                If ResizeDirection = WMSZ.WMSZ_TOPRIGHT OrElse ResizeDirection = WMSZ.WMSZ_TOPLEFT Then
                    Pos.y += Last.cy - Pos.cy
                End If


                Marshal.StructureToPtr(Of WINDOWPOS)(Pos, lParam, True)


        End Select
    End Function

End Class
Error404
  • 719
  • 9
  • 30