@Kukulkan
Ich kenne das Problem auch, weil ich auf mein Notebook die DPI hochstellen muß, um etwas zu erkennen. Mir ging es aber nur um das proportionale Vergrößern entsprechend der eingestellten DPI. Mittlerweile habe ich eine Lösung gefunden, bei der man relativ wenig am Code ändern muß.
Der ursprüngliche Code stammt aus dem englischen Forum.
DPI Aware Application
Ich habe noch zwei Funktionen hinzugefügt, die das eigentliche Resizen übernehmen. Die machen beide im Prinzip das selbe, nur mit unterschiedlicher Methode.
Die modifizierte ScaleDPI.pbi
Code: Alles auswählen
#PB_Compiler_Exe = #True ;This does not exist (yet?)
Global _ScaleDPI_X_.f = 1.0
Global _ScaleDPI_Y_.f = 1.0
#DefaultDPIX = 96.0 ;Different platforms might have different default DPI, Windows is 96 DPI.
#DefaultDPIY = 96.0
Macro ScaleDPIx(x)
(x)*_ScaleDPI_X_
EndMacro
Macro ScaleDPIy(y)
(y)*_ScaleDPI_Y_
EndMacro
Procedure InitScaleDPI() ;Windows 5.0 or higher needed for minimum functionality of this procedure.
Protected dpiaware.l = #False
Protected hDC.i
Protected lpx.i
Protected lpy.i
Protected dll.i
Protected *SetProcessDPIAware
Protected *IsProcessDPIAware
Protected ncm.NONCLIENTMETRICS
Protected font.i
Protected name$, points.i, styles.i, charset.i
;This part is Windows 6.x+ only (Vista etc.) and must be done before we use devcaps.
;http://msdn.microsoft.com/en-us/library/dd464660%28VS.85%29.aspx#declaring_dpi_awareness
;You really should use the DPI aware manifest instead of SetProcessDPIAware() when possible.
;On Windows 2000 and XP the manifest has no effect and set dpi aware is not available,
;however Devicecaps still returns usefull info that can be used.
;Note! If the dpi aware manifest is missing on Vista and Win7 then the OS will lie on devicecaps and will autoscale the entire app window.
CompilerIf #PB_Compiler_Exe ;Only use this in exes, as dlls inherit DPI from the calling process.
;If the exe or the calling exe in case of this being a dll is allready dpi aware (like through a manifest),
;then we skip using the the set dpi aware function, a dll should never use the set function, but it should check if the process id dpi aware
;and apply the proper modifiers where appropriate obviously.
dll = OpenLibrary(#PB_Any,"user32.dll")
If dll
*IsProcessDPIAware = GetFunction(dll,"IsProcessDPIAware")
If *IsProcessDPIAware
dpiaware = CallFunctionFast(*IsProcessDPIAware)
EndIf
If Not dpiaware
*SetProcessDPIAware = GetFunction(dll,"SetProcessDPIAware")
If *SetProcessDPIAware
CallFunctionFast(*SetProcessDPIAware)
EndIf
EndIf
EndIf
CompilerEndIf
hDC = GetDC_(#Null)
If hDC
lpx = GetDeviceCaps_(hDC,#LOGPIXELSX)
lpy = GetDeviceCaps_(hDC,#LOGPIXELSY)
If lpx>0
_ScaleDPI_X_ = lpx / #DefaultDPIX
EndIf
If lpy>0
_ScaleDPI_Y_ = lpy / #DefaultDPIY
EndIf
;Get the system font for message boxes etc.
;We default to a size of 9, which is also the Vista and Win7 default size.
;The OS will automatically (Vista and Win7 at least) scale the font per the current user's DPI setting.
ncm\cbSize = SizeOf(NONCLIENTMETRICS)
If SystemParametersInfo_(#SPI_GETNONCLIENTMETRICS,SizeOf(NONCLIENTMETRICS),ncm,#Null)
name$ = PeekS(@ncm\lfMessageFont\lfFaceName)
charset = ncm\lfMessageFont\lfCharSet
points = -MulDiv_(ncm\lfMessageFont\lfHeight, 72, GetDeviceCaps_(hDC, #LOGPIXELSY))
If ncm\lfMessageFont\lfWeight = 700 : styles = #PB_Font_Bold : EndIf
If ncm\lfMessageFont\lfItalic > 0 : styles = styles + #PB_Font_Italic : EndIf
font = LoadFont(#PB_Any,name$,points,#PB_Font_HighQuality|styles)
If font
SetGadgetFont(#PB_Default,FontID(font))
EndIf
EndIf
ReleaseDC_(#Null,hDC)
EndIf
EndProcedure
Procedure __EnumChild__(hwnd, Parent)
Protected Buffer.s = Space(256)
Protected RC.RECT
Protected p1.POINT
Protected p2.POINT
GetClassName_(hwnd, @Buffer,256)
Debug Buffer
If Buffer
; Screen-Koordinaten des Gadgets
GetWindowRect_(hWnd, RC)
; In Client-Koordinaten umwandeln
p1\x = rc\left
p1\y = rc\top
ScreenToClient_(GetParent_(hwnd), p1)
p2\x = rc\right
p2\y = rc\bottom
ScreenToClient_(GetParent_(hwnd), p2)
;Debug Str(p1\x) + " , " + Str(p1\y) + " - " + Str(p2\x - p1\x) + " , " + Str(p2\y - p1\y)
; Skalieren
SetWindowPos_(hwnd, 0, ScaleDPIx(p1\x), ScaleDPIy(p1\y), ScaleDPIx(p2\x - p1\x), ScaleDPIy(p2\y - p1\y), #SWP_NOZORDER)
ProcedureReturn 1
EndIf
EndProcedure
Procedure WindowScaleDpi(Window)
; Skaliert alle Gadgets eines Windows
EnumChildWindows_(WindowID(window), @__EnumChild__(), WindowID(window))
EndProcedure
Procedure GadgetScaleDpi(Gadget, LastGadget = 0)
; Skaliert ein oder mehrere Gadgets
Protected.i i, x, y, w, h
If LastGadget = 0
LastGadget = Gadget
EndIf
For i = Gadget To LastGadget
If IsGadget(i)
x = GadgetX(i) * _ScaleDPI_X_
y = GadgetY(i) * _ScaleDPI_Y_
w = GadgetWidth(i) * _ScaleDPI_X_
h = GadgetHeight(i) * _ScaleDPI_Y_
ResizeGadget(i, x, y, w, h)
EndIf
Next i
EndProcedure
Man muß nur an sehr wenigen Stellen im eigenen Code Änderungen vornehmen. Ich habe hier das gleiche PureBasic-Gadget-Demo modifiziert, wie der ursprüngliche Autor. Ich habe nur wesentlich weniger schreiben müssen. Die Stellen, wo ich etwas einfügen mußte, habe ich mit ####### markiert.
* Am Anfang muß die Include eingebunden werden und die InitScaleDPI() aufgerufen werden.
* Die Fenstergröße muß manuell angepaßt werden mit ScaleDPIx() und ScaleDPIy(), und das Fenster sollte unsichtbar erstellt werden, damit man das rumrutschen der Gadgets nicht sieht.
* Nachdem das Fenster erstellt wurde, muß eine der beiden Resize-Funktionen aufgerufen werden und das Fenster wieder sichtbar gemacht werden.
WindowsScaleDPI() und GadgetScaleDPI() machen im Prinzip das selbe. Der Unterschied ist: Bei GadgetScaleDPI() müssen die Gadgets angegeben werden. Bei WindowsScaleDPI muß das Fenster angegeben werden.
Wenn Fenster noch in der Größe änderbar sein sollen, kann man awgdgres aus dem Packet verwenden:
AWPB-Tools Includes
Das funktioniert nach dem Skalieren immer noch richtig.
Das Purebasic Gadget-Demo:
Code: Alles auswählen
;
; ------------------------------------------------------------
;
; PureBasic - Gadget example file
;
; (c) 2002 - Fantaisie Software
;
; ------------------------------------------------------------
;
IncludeFile "ScaleDPI.pbi" ; ###################
InitScaleDPI() ; ###################
#WindowWidth = 390
#WindowHeight = 350
; ########## Das Window muß manuell skaliert werden
; ########## Fenster sollte versteckt werden, damit man das Resizen nicht sieht
If OpenWindow(0, 100, 200, ScaleDPIx(#WindowWidth), ScaleDPIy(#WindowHeight), "PureBasic - Gadget Demonstration", #PB_Window_MinimizeGadget|#PB_Window_Invisible)
Top = 10
GadgetHeight = 24
Frame3DGadget(#PB_Any, 10, Top, 370, 290, "Player...") : Top+20
StringGadget(0, 20, Top, 200, GadgetHeight, "")
ButtonGadget(1, 223, Top, 72, GadgetHeight, "Play")
ButtonGadget(2, 295, Top, 72, GadgetHeight, "Stop") : Top+35
DisableGadget(2,1)
GadgetToolTip(1,"Play the current song")
PanelGadget(3, 20, Top, #WindowWidth-50, #WindowHeight-Top-60)
AddGadgetItem(3, 0, "MP3 PlayList")
ListViewGadget(4, 6, 10, 230, 148)
For k=0 To 30
AddGadgetItem(4, -1, "Music Song n° "+Str(k))
Next
ButtonGadget(5, 250, 10, 80, GadgetHeight, "Add")
ButtonGadget(6, 250, 38, 80, GadgetHeight, "Remove")
ButtonGadget(7, 250, 66, 80, GadgetHeight, "Select")
GadgetToolTip(7, "Select the current song")
TrackBarGadget(17, 10, 168, 310, 25, 0, 100)
AddGadgetItem(3, 1, "Options")
Top = 10
CheckBoxGadget(10, 10, Top, 250, GadgetHeight, "Enable low-pass filter") : Top+30
CheckBoxGadget(11, 10, Top, 250, GadgetHeight, "Enable visual plug-in") : Top+30
ComboBoxGadget(12, 10, Top, 250, 21) : Top+30
AddGadgetItem(12, -1, "FireWorks")
AddGadgetItem(12, -1, "OpenGL spectrum")
AddGadgetItem(12, -1, "Bump bass")
SetGadgetState(12,0)
DisableGadget(12,1)
OptionGadget(13, 10, Top, 80, GadgetHeight, "640*480") : Top+20
OptionGadget(14, 10, Top, 80, GadgetHeight, "800*600") : Top+20
OptionGadget(15, 10, Top, 80, GadgetHeight, "1024*768")
SetGadgetState(13, 1)
ButtonGadget(16, 150, Top, 80, GadgetHeight, "Info")
CloseGadgetList()
TextGadget (9, 10, #WindowHeight-30, 250, 24, "PureBasic - Gadget demonstration")
ButtonGadget(8, #WindowWidth-100, #WindowHeight-36, 80, 24, "Quit")
SetGadgetState(3, 0)
WindowScaleDpi(0) ; ################ Gadgets neu positionieren
HideWindow(0, 1) ; ################ Fenster zeigen
Repeat
Event = WaitWindowEvent()
If Event = #PB_Event_Gadget
Select EventGadget()
Case 0
If EventType() = #PB_EventType_ReturnKey
MessageRequester("Info", "Return key pressed", 0)
SetActiveGadget(0)
EndIf
Case 1 ; Play
DisableGadget(2,0) ; Enable the 'Stop' gadget
DisableGadget(1,1) ; Disable the 'Play' Gadget
Case 2 ; Stop
DisableGadget(1,0) ; Enable the 'Play' gadget
DisableGadget(2,1) ; Disable the 'Stop' Gadget
Case 4
If EventType() = 2
SetGadgetText(0, GetGadgetText(4)) ; Get the current item from the ListView..
EndIf
Case 5 ; Add
AddGadgetItem(4, -1, "New Item Added...")
Case 6 ; Remove
RemoveGadgetItem(4, GetGadgetState(4)) ; Remove the current element of the ListView
Case 7 ; Select
SetGadgetText(0, GetGadgetText(4)) ; Get the current item from the ListView..
Case 8 ; Quit...
Event = #PB_Event_CloseWindow
Case 11 ; Enable PlugIn..
DisableGadget(12, 1-GetGadgetState(11))
Case 16 ;
If GetGadgetState(13) : Result$ = GetGadgetText(13) : EndIf
If GetGadgetState(14) : Result$ = GetGadgetText(14) : EndIf
If GetGadgetState(15) : Result$ = GetGadgetText(15) : EndIf
MessageRequester("Info", "Selected screen mode: "+Result$, 0)
Case 17
SetGadgetText(0, Str(GetGadgetState(17)))
EndSelect
EndIf
Until Event = #PB_Event_CloseWindow
EndIf
End