SftTree/DLL 7.5 - Tree Control
SftBox/OCX 5.0 - Combo Box Control
SftButton/OCX 3.0 - Button Control
SftMask/OCX 7.0 - Masked Edit Control
SftTabs/OCX 6.5 - Tab Control (VB6 only)
SftTree/OCX 7.5 - Tree Control
SftPrintPreview/DLL 2.0 - Print Preview Control (discontinued)
SftTree/DLL 7.5 - Tree Control
SftBox/OCX 5.0 - Combo Box Control
SftButton/OCX 3.0 - Button Control
SftDirectory 3.5 - File/Folder Control (discontinued)
SftMask/OCX 7.0 - Masked Edit Control
SftOptions 1.0 - Registry/INI Control (discontinued)
SftPrintPreview/OCX 1.0 - Print Preview Control (discontinued)
SftTabs/OCX 6.5 - Tab Control (VB6 only)
SftTree/OCX 7.5 - Tree Control
SftTabs/NET 6.0 - Tab Control (discontinued)
SftTree/NET 2.0 - Tree Control
SftTabs/DLL 7.0 is fully Per-Monitor v2 DPI-aware. A tab control hosted on a Per-Monitor v2 aware top-level window will re-render automatically when its window moves to a monitor of a different DPI or when the system DPI changes. The control owns the metrics it controls; the caller owns the images and fonts it provides. Two opt-in flags let the caller hand those over to the control as well.
The host application must declare Per-Monitor v2 DPI awareness. This is the single most common reason SftTabs/DLL 7.0 applications do not re-render correctly when moved between monitors of different DPI. Without a PMv2 declaration, Windows silently keeps the process in System-aware mode: the DPI is fixed for the process lifetime, SftTabs does not observe DPI changes, SFTTABSN_DPI_CHANGED is never raised, and high-DPI monitors render at System-DPI sizes stretched by Windows. Visual Studio's default app.manifest does not declare PMv2 awareness - the developer must opt in explicitly.
Two approaches - pick one:
Add a <dpiAwareness> / <dpiAware> element to the application manifest. Both elements are usually included for back-compatibility with older Windows 10 builds:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>In a Visual Studio C++ project, set Project Properties -> Manifest Tool -> Input and Output -> Additional Manifest Files to the .manifest file above, or edit the auto-generated manifest directly. In a C# / .NET project, check Application -> DPI awareness and select Per Monitor V2.
Alternatively, call SetProcessDpiAwarenessContext at process startup, before any window is created:
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
Caveat: This call must run before the first window (including hidden startup dialogs or splash screens) is created. If a window has already been created, Windows rejects the call and the process remains in whichever mode the manifest specified - which, with the Visual Studio default, is System-aware.
Quick check at runtime: GetDpiForWindow on the tab control returns the current monitor's DPI - 96 at 100%, 120 at 125%, 144 at 150%, 192 at 200%. If the returned value never changes as you drag the window between monitors with different scale factors, the host is not in PMv2 mode.
When the host is Per-Monitor v2 aware, the tab control scales these metrics itself on every DPI change without any caller involvement:
Two independent opt-in flags let the caller choose whether caller-supplied pixel metrics and caller-supplied images also scale with DPI. Both default to back-compatible behavior (no automatic scaling) so existing applications keep working unchanged.
| Flag | Covers | ASIS (default) | STRETCH |
|---|---|---|---|
| SetImageScaling | Every image the tab control draws: caller-supplied tab pictures (SFTTABS_TAB, TabPicture and TabPicture2, including hot and disabled variants); scroll button and close/minimize/restore button bitmaps (SFTTABS_CONTROL, hButtonBitmap, hButtonBitmap2, and their disabled counterparts). | Images are drawn at their native pixel size. Bitmaps supplied at 96 DPI look physically smaller on a high-DPI monitor. | Images are scaled by currentDPI / 96. Bitmaps use HALFTONE stretch; GDI+ images use InterpolationModeHighQualityBicubic. |
| SetPixelScaling | Caller-supplied pixel dimensions on SFTTABS_CONTROL: leftMargin and rightMargin (margins on the tab row), rowIndent (row indentation), forcedSize (forced tab row height / width). | Values are used verbatim in physical screen pixels. A margin of 10 is 10 pixels on any monitor. | Values are interpreted as 96-DPI reference pixels. A margin of 10 is 10 pixels at 100%, 15 pixels at 150%, 20 pixels at 200%. Storage and getters always return caller-reference units so serialized configurations stay portable. |
| Goal | Setting |
|---|---|
| "My existing application already ships multiple image sizes or only targets 96 DPI" | Leave SetImageScaling ASIS (default). Ship tab pictures sized for the target DPI. |
| "I want crisp images on high-DPI monitors without code changes" | Call SetImageScaling with SFTTABS_IMAGESCALING_STRETCH once at control creation. |
| "My margins and forced tab sizes should stay physically the same across monitors" | Call SetPixelScaling with SFTTABS_PIXELSCALING_STRETCH once at control creation. |
| "My serialized / saved layout metrics must stay portable across DPI" | SetPixelScaling STRETCH. Storage stays in 96-DPI reference pixels regardless of monitor. |
| "I have a drawing callback (SFTTABS_DRAWTABPROC) that caches pixel metrics" | Stop caching. Read the dpi field on every SFTTABS_DRAWINFO callback and re-compute pixel sizes each paint. |
When the control's monitor DPI changes, SftTabs raises SFTTABSN_DPI_CHANGED to the parent window. The application should:
The SFTTABS_DRAWINFO structure carries the control's current effective DPI in its dpi field. Owner-draw code should read this value on every paint and must not cache pixel metrics across callbacks. GetDPI returns the same value outside a paint callback.
Platform note: Per-Monitor v2 DPI awareness requires Windows 10 version 1703 or later. On older platforms the host process runs in System-aware or Unaware mode and DPI is effectively fixed for the process lifetime - SftTabs still renders correctly but does not fire SFTTABSN_DPI_CHANGED.
