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
SftTree/DLL 8.0 is fully Per-Monitor v2 DPI-aware. A tree 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 SftTree/DLL 8.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, SftTree does not observe DPI changes, SFTTREEN_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 tree 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 tree 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 control draws: caller-supplied SFT_PICTURE cell / label / item / row-header / column-header / column-footer pictures; plus / minus bitmaps (SetPlusMinus); user-supplied tree button bitmaps (SetButtons); control-owned glyphs and the check-mark PNGs used in column-filter drop-downs. | 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: column widths (SFTTREE_COLUMN_EX.width / .minWidth), indentation (SetIndentation), row header width (SetRowHeaderWidth), horizontal extent and offset, item min / max heights, splitter offset (SetSplitterOffset, SetSplitterOffsetMin). | Values are used verbatim in physical screen pixels. A column width of 100 is 100 pixels on any monitor. | Values are interpreted as 96-DPI reference pixels. A column width of 100 is 100 pixels at 100%, 150 pixels at 150%, 200 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 SFT_PICTURE images sized for the target DPI. |
| "I want crisp images on high-DPI monitors without code changes" | Call SetImageScaling with SFTTREE_IMAGESCALING_STRETCH once at control creation. |
| "Column widths should stay physically the same size as the user moves between monitors" | Call SetPixelScaling with SFTTREE_PIXELSCALING_STRETCH once at control creation. |
| "My serialized / saved column widths must stay portable across DPI" | SetPixelScaling STRETCH. Storage stays in 96-DPI reference pixels regardless of monitor. |
| "I have owner-draw code that caches pixel metrics" | Stop caching. Read the dpi field on every SFTTREE_OWNERDRAW callback and re-compute pixel sizes each paint. |
When the control's monitor DPI changes, SftTree raises SFTTREEN_DPI_CHANGED to the parent window. The application should:
The SFTTREE_OWNERDRAW 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 - SftTree still renders correctly but does not fire SFTTREEN_DPI_CHANGED.
