I have never been sure using system metrics how to align objects on a form correctly. I always seem to find myself adding a fudge here and there to get things just perfect.
With Object
.Move (ScaleWidth - .Width) / 2!, (ScaleHeight - .Height) / 2!
End With
On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
Sorry I didn't do a very good job of describing what I was trying to ask. For centering I do the same as the post from Bonnie, but when there are several objects on a form and your trying to get consistent border widths around everything, form edges to objects and objects to objects. You need to know the widths and heights of all the pieces and parts of a form. After scrounging through documentation using system metrics I still find myself adding pixels here and there to tweak things.
If you were to use all of what one might consider pertinent to a forms horizontal 'widths/dimensions' the numbers don't add up. Some of them must need to be excluded but I don't understand the correct combination to use.
SM_CXBORDER
SM_CXEDGE
SM_CXFIXEDFRAME
SM_CXFRAME
Same goes for the vertical elements, if you use all of them the value for the forms height end up to large, so only a subset needs to be used.
Can you post a screenshot or illustration of what you're trying to do?
On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
Can you post a screenshot or illustration of what you're trying to do?
Curious, myself.
Non-client metrics are needed possibly if trying to resize the non-client area to ensure a specific client area rect is visible. Also useful if subclassing WM_NCCALCSIZE and/or tweaking/positioning the client rect within the non-client rect.
One does not need to be concerned with non-client metrics when positioning client objects. They are different 'containers'.
Insomnia is just a byproduct of, "It can't be done"
Thought I'd pass on that if you multiply rather than divide IMO it will execute faster.
Yes, I know. I intentionally chose the division method because I think it helps make the formula more readable. I lied a bit when I said that's "how I'd usually do it". Actually, in most of my code, I also prefer the multiplication method and I even invent a constant name just to make the code more readable:
Code:
Const HALF = 0.5!
With Object
.Move (ScaleWidth - .Width) * HALF, (ScaleHeight - .Height) * HALF
End With
When I'm writing scratch code, however, I just use the easiest method (division) in order to save time.
On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
Well in starting a project all of the alignment of controls is done through the IDE. Then during a form re-size event, some controls grow and stretch while others are static. To keep the alignment exact so that all of the spacing stays consistent across the entire form, I use the forms new width and height in the Form_Resize event to calculate them. To do this I make use of the forms edge, frame and caption widths and heights to get the proper sizing.
I use the forms new width and height in the Form_Resize event to calculate them. To do this I make use of the forms edge, frame and caption widths and heights to get the proper sizing.
And that's your mistake in my opinion. The form's ScaleWidth & ScaleHeight are what you want when positioning/sizing controls. Those dimensions are the viewable area of the client area, after the non-client metrics have been applied. Use ScaleWidth,ScaleHeight as your real estate
Insomnia is just a byproduct of, "It can't be done"
There's been a lot of discussion regarding "Resizing Forms" and controls thereon.
Martin Liss (sp?) came up with a proportional method.
There are others methods about that try and segment the form into areas.
Others still that try and identify whether the control is static or to be resized.
I have yet to find one that works in all cases.
In addition to the control, resizing text is the biggest problem.
Some controls such as comboboxes are a particular problem to resize.
I came up with an API method a while back that allows a combobox and its text to be resized as well as other control text but ran out of time to apply to other controls.
I'll see if I can find it and post. Give me a day or two.
===============
Take a look at this and see if it helps.
Credit for part of code belongs to others downloaded over the years and which I've cut and pasted.
My apologizes to anyone whos code I used that is Not given credit.
Zip file was created using 7 Zip.
Forum with NOT accept a 7z extention, so extention changed to zip for upload.
If you improve on it, please post in codebank for others.
Last edited by dw85745; Dec 17th, 2015 at 09:15 AM.
I'll agree that a combination of the IDE's format tools (Menu/Format/...) and the ScaleWidth and ScaleHeight during runtime (with ScaleMode possibly set to pixels) is the way I'd tend to do it.
However, I have messed with the form's client and non-client area in the past, and it would be interesting to completely work out how to do it that way. It's been a while since I've messed with those API calls, but I'd think that you could get precisely corresponding information to the ScaleWidth and ScaleHeight properties.
Also, to my consternation, many of my clients don't run in 100% on their DPI mode and/or don't run in their monitors' native screen resolution. I'm also wondering how these things affect both the API calls and the ScaleWidth and ScaleHeight properties. Since I virtually always run my development machines at DPI 100% and all monitors in native resolution, I've never even tested to see how changing these settings affect things.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Here's another vote to stick to ScaleWidth and ScaleHeight. Non-client measurements are a can of worms.
GetSystemMetrics with the constants you've listed works okay for Win XP-era "classic theming". Custom user themes can and will mess up these measurements, however.
To get accurate measurements of the "chunky" window borders used since Vista, you need a separate Vista+ API called DwmGetWindowAttribute, which has its own list of special constants. You're looking for the DWMWA_EXTENDED_FRAME_BOUNDS measurement.
On Windows 10 (and probably 8.1?) border styles changed YET AGAIN, to a new thin style that lies somewhere between the chunky Aero sizes and the original "classic" sizes. Neither API returns pixel-exact measurements on Windows 10, and I'll admit that I haven't searched all the documentation to find a fix.
In other words, stick to client measurements only. If you want pixel-perfect measurements regardless of DPI, you'll need to include a DPI-aware manifest and possibly rely on GetClientRect instead of VB's internal ScaleWidth/Height measurements. Search the forums for "DPI" and you'll find many discussions on this topic.
Yes stick to ScaleWidth and ScaleHeight, and use Scalemode vbTwips on Forms where possible because it is more DPI friendly.
If you need Pixels or want to convert to/ from them you have Screen.TwipsPerPixel and the Form.ScaleX/Y Methods.
Okay, if we insist on using API calls to get it done, this would seem to take care of everything with respect to centering controls on a form:
Code:
Option Explicit
'
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function GetClientRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
'
Private Sub Form_Activate()
Dim ClientRect As RECT
Dim PelX As Long
Dim PelY As Long
Me.ScaleMode = vbPixels
GetClientRect Me.hwnd, ClientRect
PelX = ClientRect.Right - ClientRect.Left
PelY = ClientRect.Bottom - ClientRect.Top
MsgBox "ScaleWidth: " & Me.ScaleWidth & vbCrLf & _
"ScaleHeight: " & Me.ScaleHeight & vbCrLf & _
"Client Width: " & PelX & vbCrLf & _
"Client Height: " & PelY & vbCrLf
End Sub
I tested this on a Win7 and a Win10 machine, and it worked fine on both.
I'm sure I've run across this before, but one thing that did strike me as curious is that I didn't have to go...
PelX = ClientRect.Right - ClientRect.Left + 1
...when figuring the client width.
Here's the screen-shot of my message box (both Win7 and Win10):
And I checked with a screen capture and precise measurement tool, and the client area is 108 pixels wide. This suggests that (for the API return) ClientRect.Left is zero-based and ClientRect.Right is one-based, which seems quite strange to me.
Now, if we're talking about precisely centering a form on the screen (with respect to the form's full size and the screen's usable area (excluding taskbar)), that's an entirely different issue.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Also, for certain tablets (and other monitors) that you can turn 90 degrees, VB6 has some problems. In fact, to avoid these problems, I've had to write my own versions of:
VB6 just doesn't seem to pick up when the screen is rotated.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
I'm sure I've run across this before, but one thing that did strike me as curious is that I didn't have to go...
PelX = ClientRect.Right - ClientRect.Left + 1
...when figuring the client width.
. . .
This suggests that (for the API return) ClientRect.Left is zero-based and ClientRect.Right is one-based, which seems quite strange to me.
By convention, the right and bottom edges of the rectangle are normally considered exclusive. In other words, the pixel whose coordinates are ( right, bottom ) lies immediately outside of the rectangle. For example, when RECT is passed to the FillRect function, the rectangle is filled up to, but not including, the right column and bottom row of pixels.
On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
On Win XP Pro
Missing declarations and toooo slow in the IDE and when compiled the cursor disconnects from the form so only on resize happens.
Also things like textboxes fold over and text font is not considered.