Dynamische Größenänderung eines StatusBar-Feldes (WIN)

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
Daniel P.
Beiträge: 333
Registriert: 06.12.2005 21:49
Kontaktdaten:

Dynamische Größenänderung eines StatusBar-Feldes (WIN)

Beitrag von Daniel P. »

Da ich weder hier, im englischen Board oder im Code-Archiv etwas passendes gefunden habe und PureBasic leider keine enstprechende Funktion bietet (warum auch immer), habe etwas gebastelt, das die Größe eines beliebigen StatusBar-Feldes "on-the-fly" in typischer PureBasic-Einfachheit ändert. Sehr praktisch zum Beispiel, wenn man ein in der Größe flexibles Fenster hat und die StatusBar-Felder bei einem Resize ihre Größe bzw. Breite auch ändern sollen. Das Ganze funktioniert ohne Callback-Funktion und kann beispielsweise direkt in #PB_Event_SizeWindow eingebaut werden. Hier der Code, sollte selbsterklärend sein <)

[edit]
Speicher muss am Ende der Funktion noch freigegeben werden. Ansonsten wird bei jedem Resize-Window mehr Speicher reserviert :oops:

Code: Alles auswählen

#SB_GETPARTS = $406
#SB_SETPARTS = $404

;
; Setzt die Breite eines StatusBar-Feldes neu
;
; (Long) hWndControl: Nummer der StatusBar (#StatusBar)
; (Long) dwField:     Nummer des Feldes
; (Long) dwWidth:     Neue Breite des Feldes
;
; Rückgabewert: (Byte) #True bei Erfolg oder #False im Fehlerfall
;
Procedure.b API_SetStatusBarFieldWidth(hWndControl.l, dwField.l, dwWidth.l)
  ;
  ; Geschützte Funktionsvariablen
  ;
  ; (Long) dwResult: Container für Funktionsergebnisse
  ; (Long) nParts:   Container für Anzahl der StatusBar-Felder
  ; *      dwFields: Pointer zu Positionsangaben der StatusBar-Felder (Long-Array)
  ; (Long) dwLeft:   Container für Position eines vorangegangenen StatusBar-Feldes
  ;
  Protected dwResult.l
  Protected nParts.l
  Protected *dwFields
  Protected dwLeft.l
  ;
  ; Funktionsparameter prüfen
  ;
  If IsStatusBar(hWndControl) And dwField > 0 And dwWidth > 0
    ;
    ; DLL laden
    ;
    OpenLibrary(0, "user32.dll")
    ;
    ; Aktuelle Anzahl Felder ermitteln
    ;
    nParts = CallFunction(0, "SendMessageW", StatusBarID(hWndControl), #SB_GETPARTS, #Null, #Null)
    If nParts <> 0 And dwField <= nParts
      ;
      ; Speicher für Positionsangaben der Felder reservieren
      ;
      *dwFields = AllocateMemory(SizeOf(Long) * nParts)
      ;
      ; Positionsangaben ermitteln
      ;
      nParts = CallFunction(0, "SendMessageW", StatusBarID(hWndControl), #SB_GETPARTS, nParts, *dwFields)
      If nParts <> 0
        ;
        ; Alte Feld-Position ermitteln (vorangegangenes Feld)
        ;
        If dwField = 1
          dwLeft = 0
        Else
          dwLeft = PeekL(*dwFields + (SizeOf(Long) * (dwField - 2)))
        EndIf
        ;
        ; Neue Feld-Position setzen
        ;
        PokeL(*dwFields + (SizeOf(Long) * (dwField - 1)), dwLeft + dwWidth)
        ;
        ; Neue Feld-Position an StatusBar übergeben
        ;
        dwResult = CallFunction(0, "SendMessageW", StatusBarID(hWndControl), #SB_SETPARTS, nParts, *dwFields)
      EndIf
      ;
      ; Speicher freigeben
      ;
      FreeMemory(*dwFields)
    EndIf
    ;
    ; DLL schließen
    ;
    CloseLibrary(0)
  EndIf
  ;
  ; Funktionsergebnis zurückgeben
  ;
  If dwResult <> 0
    ProcedureReturn #True
  Else
    ProcedureReturn #False
  EndIf
EndProcedure
Ist bestimmt nicht der schönste Code, aber er verrichtet seine Arbeit schnell und zuverlässig :mrgreen:

HTH
Zuletzt geändert von Daniel P. am 13.12.2007 22:25, insgesamt 3-mal geändert.
Gruß, Daniel :? | In der Realität ist die Wirklichkeit ganz anders...

PB 4.10 (Windows XP SP 2)
Benutzeravatar
bobobo
jaAdmin
Beiträge: 3873
Registriert: 13.09.2004 17:48
Kontaktdaten:

Beitrag von bobobo »

Das geht einfacher ! und sogar ohne weitere Parameterübergabe und sogar
ohne Aufruf externer Funktionen (die in PB eh schon eingebaut sind)

Code: Alles auswählen

Procedure nix()
  ProcedureReturn #True
EndProcedure
Dein Code ändert zumindest an meinen Statusbars nix
Ein Beispiel mit Funktion wäre angebracht
‮pb aktuel 6.2 windoof aktuell und sowas von 10
Ich hab Tinnitus im Auge. Ich seh nur Pfeifen.
Benutzeravatar
AND51
Beiträge: 5220
Registriert: 01.10.2005 13:15

Beitrag von AND51 »

Also ich versuche meine Anwendung so zu konzipieren, dass alle Felder bis auf das ganz rechte statisch bleiben können.
Das letzte (rechte) Feld ist dann mein "dynamisches".

Der Trick: Ich mache es einfach 5000 Pixel breit, überbiete damit jede Auflösung - und schon muss ich mir darum keine Gedanken mehr machen.
PB 4.30

Code: Alles auswählen

Macro Happy
 ;-)
EndMacro

Happy End
Benutzeravatar
Daniel P.
Beiträge: 333
Registriert: 06.12.2005 21:49
Kontaktdaten:

Beitrag von Daniel P. »

Hier ein Beispiel-Code (bei mir getestet - geht). Fügt ihn einfach unterhalb der o. g. Funktion ein und startet den Compiler:

Code: Alles auswählen

If OpenWindow(0, #PB_Ignore, #PB_Ignore, 200, 400, "test", #PB_Window_SizeGadget | #PB_Window_SystemMenu)
  If CreateStatusBar(1, WindowID(0))
    AddStatusBarField(1)
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_SizeWindow
          API_SetStatusBarFieldWidth(1, 1, WindowWidth(0) - 20)
        Case #PB_Event_CloseWindow
          Break
      EndSelect
    ForEver
  EndIf
EndIf

End
@AND51:
Das ist ja Cheaterei und funktioniert auch nur dann, wenn man nicht mit mehreren Monitoren arbeitet. Da kann man nämlich ziemlich schnell über die von dir genannten 5000 Pixel kommen. Ist zwar eher selten, aber möglich. Außerdem läuft das letzte Feld dann über das ResizeGadget des Fensters und das sieht doof aus. Das letzte Feld sollte eine Begrenzung zum ResizeGadget haben und die sollte mitwandern. Sieht optisch schöner aus, finde ich. Sieht man zum Beispiel im Explorer oder im Firefox, IE, etc. ...
Gruß, Daniel :? | In der Realität ist die Wirklichkeit ganz anders...

PB 4.10 (Windows XP SP 2)
Benutzeravatar
Bisonte
Beiträge: 2470
Registriert: 01.04.2007 20:18

Beitrag von Bisonte »

@AND51:
Das ist ja Cheaterei und funktioniert auch nur dann, wenn man nicht mit mehreren Monitoren arbeitet. Da kann man nämlich ziemlich schnell über die von dir genannten 5000 Pixel kommen. Ist zwar eher selten, aber möglich.
Oder warte noch 2 Jahre und zähle wie der Typ in der Werbung im Kaufhaus die Pixel ;)

*sry spam
PureBasic 6.21 (Windows x86/x64) | Windows11 Pro x64 | AsRock B850 Steel Legend Wifi | R7 9800x3D | 64GB RAM | GeForce RTX 5080 | ThermaltakeView 270 TG ARGB | build by vannicom​​
Benutzeravatar
AND51
Beiträge: 5220
Registriert: 01.10.2005 13:15

Beitrag von AND51 »

> Da kann man nämlich ziemlich schnell über die von dir genannten 5000 Pixel kommen
Dann nimmste 9999 Pixel, wo ist das Problem? Zusätzlich kannst du Felder statt staischen Größen mit WindowWidth(0)*0.5 dynamischen Größen arbeiten. So bist du auch auf der sicheren Seite.

> Außerdem läuft das letzte Feld dann über das ResizeGadget des Fensters und das sieht doof aus
Geschmackssache. Sieht besser aus, als wenn direkt neben dem WindowResize Gadget noch eine extra Grenze klebt.

> Das letzte Feld sollte eine Begrenzung zum ResizeGadget haben und die sollte mitwandern. Sieht optisch schöner aus, finde ich. Sieht man zum Beispiel im Explorer oder im Firefox, IE, etc.
Kein IE User, das merkt man. Bei dem IE ist nur das erste Feld dynmaisch, nicht das letzte.

Ich sag ja nichts gegen deine Lösung, liefere ja nur einen simplen Alternativvorschlag. Wenns funktioniert (deine Methode), dann ist dein Code durchaus eine nützliche Prozedur.
PB 4.30

Code: Alles auswählen

Macro Happy
 ;-)
EndMacro

Happy End
Benutzeravatar
Daniel P.
Beiträge: 333
Registriert: 06.12.2005 21:49
Kontaktdaten:

Beitrag von Daniel P. »

AND51 hat geschrieben:> Da kann man nämlich ziemlich schnell über die von dir genannten 5000 Pixel kommen
Dann nimmste 9999 Pixel, wo ist das Problem?
... das ist "quick'n dirty" und IMHO kein guter Stil ;)
AND51 hat geschrieben:Zusätzlich kannst du Felder statt staischen Größen mit WindowWidth(0)*0.5 dynamischen Größen arbeiten.
Abgesehen davon, dass zumindest meine Fenster nicht mit halben Pixeln arbeiten, funktioniert genau das IMHO nur mit meiner Funktion. Beim Anlegen eines neuen Feldes wird das einmal ausgerechnet und schon hast du wieder eine statische Breite. Bei einem Window-Resize ändern sich die Felder nicht...
AND51 hat geschrieben:> Das letzte Feld sollte eine Begrenzung zum ResizeGadget haben und die sollte mitwandern. Sieht optisch schöner aus, finde ich. Sieht man zum Beispiel im Explorer oder im Firefox, IE, etc.
Kein IE User, das merkt man. Bei dem IE ist nur das erste Feld dynmaisch, nicht das letzte.
Es ist genau umgekehrt. Denn die StatusBar speichert nicht die Breite, wie von den PureBasic-Funktionen (oder auch meiner) suggeriert wird, sondern den linken Abstand zum Fensterrand ("left"). Der linke Abstand des ersten Feldes ist immer 0. Die Abstände der anderen Felder wird beim Window-Resize errechnet; allerdings bleiben sie untereinander gleich. Ist aber nur Korinthenkackerei <)
AND51 hat geschrieben:Ich sag ja nichts gegen deine Lösung, liefere ja nur einen simplen Alternativvorschlag.
Hab das auch nicht als Angriff gewertet - aber wie ich schon oben schrieb, stehe ich nicht auf solche Work-Arounds. BTW würde ich wenn überhaupt nur einen negativen Wert für das letzte Feld übergeben ("-1"). Laut MSDN wird das Feld dann automatisch angepasst. Da aber der PureBasic-Befehl zum Hinzufügen neuer Felder keine negativen Werte akzeptiert, muss man hier auch wieder mit SendMessage arbeiten und so ist dann auch meine Funktion entstanden...
AND51 hat geschrieben:Wenns funktioniert (deine Methode), dann ist dein Code durchaus eine nützliche Prozedur.
Gutes Stichwort. Läuft denn mein Beispielcode? Konnte es nur bei mir unter Windows XP testen. Bei bo³ schien es ja nicht zu funktionieren :|
Gruß, Daniel :? | In der Realität ist die Wirklichkeit ganz anders...

PB 4.10 (Windows XP SP 2)
Benutzeravatar
AND51
Beiträge: 5220
Registriert: 01.10.2005 13:15

Beitrag von AND51 »

> das ist "quick'n dirty" und IMHO kein guter Stil
Finde ich nicht direkt. Ich kenne die Definition von "quick'n'dirty" zwar nicht, aber solange es eine sichere Methode ist, die nicht fehlschlägt und prima funktioniert, finde ich solche Lösungen ok.

> Abgesehen davon, dass zumindest meine Fenster nicht mit halben Pixeln arbeiten
Keine Fenster arbeiten mit halben Pixeln. Ist aber auch nicht weiter tragisch, denn der erwartete Parameter ist ein Long, d. h. von Floats wird automatisch der Integer-Teil (Long) genommen.

> BTW würde ich wenn überhaupt nur einen negativen Wert für das letzte Feld übergeben ("-1"). Laut MSDN wird das Feld dann automatisch angepasst. Da aber der PureBasic-Befehl zum Hinzufügen neuer Felder keine negativen Werte akzeptiert, muss man hier auch wieder mit SendMessage arbeiten und so ist dann auch meine Funktion entstanden...
Danke für die Info!

> Gutes Stichwort. Läuft denn mein Beispielcode?
Bei mir funktionierts (XP SP2, PB Beta 3)
PB 4.30

Code: Alles auswählen

Macro Happy
 ;-)
EndMacro

Happy End
Benutzeravatar
Kurzer
Beiträge: 1618
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Beitrag von Kurzer »

Hallo Jungs,

hatte gerade das gleiche Problem... bin aber im Gegensatz zu Daniel P. im Englischen Forum fündig geworden.

Sparkie hat genauso kurzen, wie genialen Code gepostet (Win-API):

Code: Alles auswählen

If OpenWindow(0, 0, 0, 440, 50, "", #PB_Window_SystemMenu|#PB_Window_ScreenCentered | #PB_Window_SizeGadget)
  myStatusBar = CreateStatusBar(0, WindowID(0))
  AddStatusBarField(WindowWidth(0)/2)
  AddStatusBarField(WindowWidth(0)/2)
  StatusBarText(0, 0, "Field 0")
  StatusBarText(0, 1, "Field 1")
  ; --> Array for holding right side coordinates od StatusBar fields
  Dim sbFields(1)
  Repeat
    event = WaitWindowEvent()
    Select event
      Case #PB_Event_SizeWindow
        ; --> Field 0 will extend from left edge of window border to 1/2 of window width
        sbFields(0) = WindowWidth(0)/2
        ; --> Field 1 will extend from right edge of field 0 to right edge of window border
        sbFields(1) = WindowWidth(0) - 20
        SendMessage_(myStatusBar, #SB_SETPARTS, 2, sbFields())
    EndSelect
  Until event = #PB_Event_CloseWindow
EndIf
End
Kann ich super mit leben... kurz und gut. Und man kann das letzte Feld sogar vor dem ResizeGad aufhören lassen.

PS: Jetzt müsste man nur noch eine API Funktion finden, die einem die Pixelanzahl rauswirft für einen Text, der in einem bestimmten Font gezeichnet wird. Dann könnte man die Fields sogar dynamisch halten. :-)

Edit: Ich habe jetzt was für die dynamische Anpassung gefunden (WinAPI). Leider passt die Größe nicht immer ganz korrekt - da sind hin und wieder mal bis zu 15 Pixel zuviel Platz. Die Funktion GetTextExtentPoint32_ sollte doch eigentlich die korrekte Pixelanzahl zu einem Text zurückgeben?
Ich weiß nicht warum das nicht immer paßt, aber evtl. wird es unter Not(Win98) auch korrekt dargestellt - kann ja mal jemand testen.

Code: Alles auswählen

Procedure ResizeStatusBarFields(StatusBar.l)
	Protected TextSize.SIZE, StatusBarDC.l, NumFields.l, Field.l, LastWidth.l, OldText$
	
	NumFields = SendMessage_(StatusBarID(StatusBar), #SB_GETPARTS, #Null, #Null) 
	If NumFields > 0
		Dim Fields(NumFields - 1) ; Anzahl der StatusBarFelder - 1
		
	 	StatusBarDC = GetDC_(StatusBarID(StatusBar))
	
		For Field = 0 To NumFields - 1
			OldText$ = Space(256)
	  	SendMessage_(StatusBarID(StatusBar), #SB_GETTEXT, Field, @OldText$)						; Den vorhandenen Text der StatusBar auslesen
			If OldText$ = Space(256) : OldText$ = "" : EndIf
	
			GetTextExtentPoint32_(StatusBarDC, @OldText$, Len(OldText$), @TextSize.SIZE)	; Ermittelt die Breite eines Textes (in der Statusbar) in Pixeln
			LastWidth + TextSize\cx
			Fields(Field) = LastWidth
	  Next Field

		ReleaseDC_(StatusBarID(StatusBar), StatusBarDC) 
	
		; Setzt den Abstand der rechten Seite der Felder gemessen von Pos. 0
		SendMessage_(StatusBarID(StatusBar), #SB_SETPARTS, NumFields, Fields())
	EndIf

EndProcedure


If OpenWindow(0, 0, 0, 650, 150, "", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
  CreateStatusBar(0, WindowID(0))
	  AddStatusBarField(100)
	  AddStatusBarField(200)
	  AddStatusBarField(300)
	CreateGadgetList(WindowID(0))
		ButtonGadget(0, 10, 15,  72, 25, "Mach mal") 
	
	Repeat 
	  Select WaitWindowEvent() 
	    Case #PB_Event_CloseWindow 
	      Break 
	    Case #PB_Event_Gadget 
			  StatusBarText(0, 0, Left("Feld 1 trallalaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 6 + Random(25)))
			  StatusBarText(0, 1, Left("Feld 2 hopsassaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 6 + Random(25)))
			  StatusBarText(0, 2, Left("Feld 3 tirliliiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii", 6 + Random(25)))
				ResizeStatusBarFields(0)
  	EndSelect 
	ForEver

EndIf
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.12 x64, OS: Win 11 24H2 x64, Desktopscaling: 150%, CPU: I7 12700 H, RAM: 32 GB, GPU: Intel(R) Iris(R) Xe Graphics | NVIDIA GeForce RTX 3070
Useralter in 2025: 57 Jahre.
Benutzeravatar
Leonhard
Beiträge: 602
Registriert: 01.03.2006 21:25

Beitrag von Leonhard »

Praktische sache.

Für die, die nur die Größe vom letzten Feld immer angezeigt haben wollen, würde ich das empfehlen:

Code: Alles auswählen

If OpenWindow(0, #PB_Ignore, #PB_Ignore, 200, 400, "test", #PB_Window_SizeGadget | #PB_Window_SystemMenu)
  If CreateStatusBar(1, WindowID(0))
    ExamineDesktops()
      AddStatusBarField(100)
      AddStatusBarField(DesktopWidth(0)-100)
    
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
          Break
      EndSelect
    ForEver
  EndIf
EndIf

End
Wenn man aber ein anderen Statusbar-Feld als Dynamisch laufen lassen will, geht das dann nicht mehr.
Antworten