问题
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