This is for Krool. Others may want to experiment.
Background: When a DPI-aware project is loaded into non-integral DPI (my own term), 175%, 200%, etc, where VB's twipsPerPixel (TPP) is not the same as the system's (1440 / realDPI) <> VB's TPP, then OCXs have problems. An ocx for simplicity: any control that is not a VB intrinsic control.
Problem: The ocx does not draw into the full width/height of the ocx, it draws smaller than expected. This is directly related to the TPP discrepancy.
Fix when resizing ocxs in this scenario: while sizing, also move the control +1 units Left & Top, then re-move the control -1 units left and top. Though this does address the problem, it still fails when an OCX has an Align property that is set to other than vbAlignNone (zero). You can try with common controls progressbar and set its Align property to non-zero. Again project must be DPI-aware and loaded into 175% DPI or similar non-integral DPI
Ultimate fix is to send the control the dimensions of itself as pixels via an Interface method. That addresses the problem whether the ocx is aligned or not. This requires a bit of low-level API calls, if no TLB for that interface. We would call that interface method each time the form is resized because resizing the form changes the ocx dimensions when Align is non-zero.
Here is some sample code that can be used from the parent/form of an ocx. It should be called from within the Form_Resize event for ocxs that have an Align property set to non-zero.
Code:
Private Declare Function DispCallFunc Lib "oleaut32.dll" (ByVal pvInstance As Long, ByVal offsetinVft As Long, ByVal CallConv As Long, ByVal retTYP As Integer, ByVal paCNT As Long, ByVal paTypes As Long, ByVal paValues As Long, ByRef retVAR As Variant) As Long
Private Declare Function IIDFromString Lib "ole32.dll" (ByVal lpsz As Long, ByVal lpiid As Long) As Long
Public Sub SyncOcxClientToParent(theControl As Control)
' This can be called in any DPI, but for for non-intrinsic controls, should always
' be called in non-integral DPI. In that case, external OCXs (not VB
' intrinsic controls) may not render to the full bounds of the control. This method
' attempts to fix that. It should only be called for non-intrinsic controls and
' after the control is sized.
Dim oIUnk As IUnknown, oObj As Object, frm As Form
Dim vParamPtr(0 To 1) As Long, vParamType(0 To 1) As Integer
Dim vRtn As Variant, vParams(0 To 1) As Variant
Dim aData(0 To 3) As Long
Dim hWnd As Long, sm As ScaleModeConstants
Const IID_IOleInPlaceObject = "{00000113-0000-0000-c000-000000000046}"
Const IOIP_SetRects As Long = 28&
On Error Resume Next
Set oObj = theControl.object
If Err Then
Err.Clear
Else
Set frm = theControl.Parent
If Err Then ' applies to forms only
Err.Clear: Set oObj = Nothing
Else
sm = theControl.Container.ScaleMode
If Err Then
Err.Clear: sm = vbTwips
End If
End If
End If
On Error GoTo 0
If oObj Is Nothing Then Exit Sub
IIDFromString StrPtr(IID_IOleInPlaceObject), VarPtr(aData(0))
' set up call to DispCallFunc for querying for IID_IOleInPlaceObject
vParams(0) = VarPtr(aData(0)): vParams(1) = VarPtr(oIUnk)
vParamType(0) = vbLong: vParamType(1) = vbLong
vParamPtr(0) = VarPtr(vParams(0)): vParamPtr(1) = VarPtr(vParams(1))
' does object implement IID_IOleInPlaceObject?
DispCallFunc ObjPtr(oObj), 0&, 4&, vbLong, 2, VarPtr(vParamType(0)), VarPtr(vParamPtr(0)), vRtn
If oIUnk Is Nothing Then
With theControl
.Move .Left + frm.ScaleX(1, vbPixels, sm), .Top + frm.ScaleX(1, vbPixels, sm)
.Move .Left - frm.ScaleX(1, vbPixels, sm), .Top - frm.ScaleX(1, vbPixels, sm)
End With
Else
aData(0) = frm.ScaleX(theControl.Left, sm, vbPixels)
aData(1) = frm.ScaleX(theControl.Top, sm, vbPixels)
aData(2) = aData(0) + frm.ScaleX(theControl.Width, sm, vbPixels)
aData(3) = aData(1) + frm.ScaleX(theControl.Height, sm, vbPixels)
vParams(1) = VarPtr(aData(0))
DispCallFunc ObjPtr(oIUnk), IOIP_SetRects, 4&, vbLong, 2, VarPtr(vParamType(0)), VarPtr(vParamPtr(0)), vRtn
End If
Set oObj = Nothing: Set oIUnk = Nothing: Set frm = Nothing
End Sub
Now, the above should work in most cases, but can still fail when multiple aligned controls are stacked on each other. Multiple sizing events may occur that undoes the call to SyncOcxClientToParent. In that case, you need a delay. For example, use a disabled timer with a short interval and when Form_Resize kicks off, enable the timer. When the timer kicks off, disable it, and call SyncOcxClientToParent for the affected controls.
For Krool: The above code is for an external call. I'd imagine you can fix your own CCR controls using the same logic. However, here's some things to consider:
1. That sample code needs the UC's position/dimensions from the container's point of view and translated to pixels relative to the form's client area. Consider using the UC's Extender. You can't use GetWindowRect on the UC.hWnd because it will return the wrong dimensions. Actually, you could but would need to scale it by the difference in VB TPP vs real TPP. But not all UCs have hWnds (windowless).
2. The interface method expects two rectangles. Both are identical, so use the same pointer: VarPtr(udtRect)
3. If you are using TLBs, may want to include IOleInPlaceObject. Doing so should negate the DispCallFunc, IIDFromString calls along with the variables used for those calls.
4. From trial and error. Calling the interface method should be done whenever the control resizes, after it resizes, i.e., trap WM_SIZE if possible. If no subclassing is in play, see if Usercontrol_Resize will be good enough & if not, maybe a timer delay method like described earlier.