0

我目前正在创建一个 WPF 窗口,它在调整大小时保留其纵横比。

我的第一个想法是处理 WM_SIZE 消息并在那里设置大小,但这会产生烦人的闪烁。所以我试图改变产生 AccessViolationExceptions 的 WM_Size 的 lParam。在 WM_SIZING 上操作 lParam 时也会发生同样的情况。

方面窗口.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
4

1 回答 1

-1

通过处理 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
于 2016-10-17T07:38:43.720 回答