How to determine correctly the Non-Client Area Size for Aero?

那年仲夏 提交于 2019-12-12 14:08:38

问题


How to determine correctly with VBNET or C# code the Non-Client Area Size when Aero is activated for a COMPILED application? (Yes, this problem only occurs when running a compiled application, not when launching the app from the IDE)

When I resize my form or I do any operation relationed with the height/width of the form I never get the expected result.

For example this is a part of a code of a simple Docking of two forms:

VB-NET:

Me.Location = New Point((form1.Location.X + form1.Width), form1.Location.Y)

C#:

this.Location = new Point((form1.Location.X + form1.Width), form1.Location.Y);

To give an example I'll show a program of mine.

The code above runs perfectlly when Aero is not activated:

...But if Aero is activated then this is the result:

Notice how the form of the right is under the Non-Client border of the left Form.

...Or here is other image where the left form is under the Non-Client border of the right form:

My question is Which is the method to solve this?

UPDATE:

Extending the frame solution is not working.

Form1:

Imports System.Runtime.InteropServices

Public Class Form1
    Public Moving_From_Secondary_Form As Boolean = False

    <DllImport("dwmapi.dll")> _
    Private Shared Function DwmExtendFrameIntoClientArea(ByVal hwnd As IntPtr, ByRef margins As MARGINS) As Integer
    End Function

    <StructLayout(LayoutKind.Sequential)> _
    Public Structure MARGINS
        Public leftWidth As Integer
        Public rightWidth As Integer
        Public topHeight As Integer
        Public bottomHeight As Integer
    End Structure

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim margins As New MARGINS()
        margins.leftWidth = -1
        margins.rightWidth = -1
        margins.topHeight = -1
        margins.bottomHeight = -1
        DwmExtendFrameIntoClientArea(Me.Handle, margins)
        Form2.Show()
    End Sub

    Private Sub Form1_Move(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Move
        If Not Moving_From_Secondary_Form Then Form2.Location = New Point(Me.Right, Me.Top)
    End Sub

End Class

Form2:

Public Class Form2

    Private Sub Form2_Move(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Move
        Form1.Moving_From_Secondary_Form = True
        Form1.Location = New Point(Me.Left - Form1.Width, Me.Top)
        Form1.Moving_From_Secondary_Form = False
    End Sub

End Class

Result:

Also I want to remember: this problem only occurs when running a compiled application, not when launching the app from the IDE

**

UPDATE:

**

Tested the GetWindowRect solution and always return a 0, not working for me, maybe I'm doing something wrong:

Imports System.Runtime.InteropServices

Public Class Form1

    Private Declare Function GetWindowRect Lib "user32" (ByVal Handle As IntPtr, Rect As RECT) As Long
    Private Declare Function CopyRect Lib "user32" (DestRect As RECT, SourceRect As RECT) As Long

    <StructLayout(LayoutKind.Sequential)> _
    Structure RECT
        Public Left As Int32
        Public Top As Int32
        Public Right As Int32
        Public Bottom As Int32
    End Structure

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Form2.Show()
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim rectWindow As RECT, rectCopy As RECT

        'Get the bounding rectangle of this window
        GetWindowRect(Me.Handle, rectWindow)
        'Copy the rectangle
        CopyRect(rectCopy, rectWindow)

        MsgBox("This form's width:" & (rectCopy.Right - rectCopy.Left).ToString & " pixels")
        Form2.Location = New Point(rectCopy.Right, rectCopy.Top)
    End Sub

End Class

**

UPDATE:

**

Another try with GetWindowRect, This time the code is correctly wrote but don't solve the problem:

Imports System.Runtime.InteropServices

Public Class Form1
    <StructLayout(LayoutKind.Sequential)> _
    Private Structure RECT
        Public Left As Int32
        Public Top As Int32
        Public Right As Int32
        Public Bottom As Int32
    End Structure

    Private Declare Function GetWindowRect Lib "user32" (ByVal HWND As Integer, ByRef lpRect As RECT) As Integer

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim rc As RECT
        GetWindowRect(MyBase.Handle, rc)
        Dim width As Integer = rc.Right - rc.Left
        Form2.Show()
        Form2.Location = New Point(rc.Right, rc.Top)
    End Sub

End Class

I want to remember: this problem only occurs when running a compiled application on win7/Vista, not when launching the app from the IDE


回答1:


Although I've not tested this personally you can extend the frame into the client area using DwmExtendFrameIntoClientArea and passing -1.

In theory that should mean that the size/positions you specify will include the frame.

C# Signature

[DllImport("dwmapi.dll", PreserveSig = true)]
static extern int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins);

[DllImport("dwmapi.dll", PreserveSig = false)]
static extern void DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins);

VB.NET Signature:

<DllImport("dwmapi.dll")> _
Private Shared Function DwmExtendFrameIntoClientArea(ByVal hwnd As IntPtr, ByRef margins As MARGINS) As Integer
End Sub

Update

Have you thought about setting the forms border style to none and then using a button to control close/minimize? There are other options which might get you the result your after too - FixedSingle and FixedToolWindow

UPDATE 2

I managed to recreate your problem and solve it with the following code. When debugging the window position is skewed but when running the compiled exe the window location is correct.

Dim BorderWidth = (Me.Width - Me.ClientSize.Width)
Me.Location = New Point((Form1.Location.X + (Form1.Width + BorderWidth)), Form1.Location.Y)



回答2:


I dont have much experience with vb.net specifically, but usually, I would use GetWindowRect() to get the complete outer border of the window. It appears to be available in vb.net

http://www.pinvoke.net/default.aspx/user32/getwindowrect.html

Now I havent used vb since version 6.0, but you may have to convert that rect from pixels to twips.

Edit:

This code works for me:

Imports System.Runtime.InteropServices

Public Class Form1
    <StructLayout(LayoutKind.Sequential)> _
    Private Structure RECT
        Public Left As Int32
        Public Top As Int32
        Public Right As Int32
        Public Bottom As Int32
    End Structure

    Private Declare Function GetWindowRect Lib "user32" (ByVal HWND As Integer, ByRef lpRect As RECT) As Integer

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim rc As RECT
        GetWindowRect(MyBase.Handle.ToInt32, rc)

        Dim width As Integer = rc.Right - rc.Left
        MessageBox.Show(width.ToString)

    End Sub
End Class



回答3:


Finally I did this Snippet for Docking a secondary form to the right of the main form wich is working even if Aero is enabled and if we are not running the APP fom debug, Now my apps can be docked with AERO enabled on virtual machines and all (AERO) Windows :D.

  ' Instructions :
  ' Change Manually the "StartPosition" property of "Form2" to "Manual", don't change it with code.

    Public Moving_From_Secondary_Form As Boolean = False

    ' Move Event Main Form
    Private Sub Form1_Move(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Move
        If Not Moving_From_Secondary_Form Then
            If Debugger.IsAttached Then
                Form2.Location = New Point(Me.Right, Me.Top)
            Else
                Form2.Location = New Point((Me.Location.X + (Me.Width + (Me.Width - Me.ClientSize.Width))), Me.Location.Y)
            End If
        End If
    End Sub

    ' Move Event Secondary Form
    Private Sub Form2_Move(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Move
        Form1.Moving_From_Secondary_Form = True
        If Debugger.IsAttached Then
            Form1.Location = New Point(Me.Left - Form1.Width, Me.Top)
        Else
            Form1.Location = New Point((Me.Location.X - (Form1.Width + (Form1.Width - Form1.ClientSize.Width))), Me.Location.Y)
        End If
        Form1.Moving_From_Secondary_Form = False
    End Sub

...And also this function:

#Region " Get Non-Client Area Width "

    ' [ Get Non-Client Area Width Function ]
    '
    ' Examples :
    ' MsgBox(Get_NonClientArea_Width(Form1))
    ' Me.Location = New Point((Form1.Location.X + (Form1.Width + Get_NonClientArea_Width(Form1))), Form1.Location.Y) 

    Public Function Get_NonClientArea_Width(ByVal Form Form) As Int32
        Return (Form.Width - Form.ClientSize.Width)
    End Function

#End Region


来源:https://stackoverflow.com/questions/16111595/how-to-determine-correctly-the-non-client-area-size-for-aero

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!