Fenster Einbetten

Wolfgang Kluge
wolfgang@vbwelt.de
www.vbwelt.de

Also ich hab das schon oft gehabt und bin auch schon oft gefragt worden.
Ich hab ein Fenster, und dieses soll in der Größe veränderbar sein. Mit 2 Einschränkungen : 1. die Form darf nicht kleiner sein als eine bestimmte Größe, und 2. die Form darf auch nicht größer werden als eine bestimmte Größe.

Das naheliegendste ist dann natürlich im Resize-Ereigniss die aktuellen Width- und Heighteigenschften auszulesen und gegebenenfalls zu korrigieren.
Das Dumme daran ist, daß das mächtig Scheiße aussieht. (um mal kein Blatt vor den Mund zu nehmen...*gg*) Es flackert und tut und macht - bloß nicht das was es soll.

Die nächste überlegung ist, vor dem Resize-Ereigniss das gleiche zu tun. Und daß geht mit dem AdressOf-Operator. Die API-Funktion SetWindowLong bietet an, einen Pointer auf eine Funktion zu setzten, die dann alle Windows-Funktionen aufnimmt.

Man lege sich also ein Modul in einem Projekt an, in dem folgendes steht. (Das sieht jetzt alles sehr kryptisch aus, aber im Grunde ist es ganz einfach...ehrlich*g*)

 Deklaration:
Private WinOldProcMinMax As Long

Private Const GWL_WNDPROC = (-4)
' Die Nachricht WM_GETMINMAXINFO wird an ein Fenster geschickt,
' bevor es vergrößert bzw. verkleinert wird
Private Const WM_GETMINMAXINFO = &H24
Private lX1 As Long, lX2 As Long, lY1 As Long, lY2 As Long

Private Type POINTAPI
  x As Long
  y As Long
End Type
Private Type MINMAXINFO
  ptReserved As POINTAPI
  ptMaxSize As POINTAPI
  ptMaxPosition As POINTAPI
  ptMinTrackSize As POINTAPI
  ptMaxTrackSize As POINTAPI
End Type

Private Declare Function DefWindowProc Lib "user32" Alias "DefWindowProcA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

Private Declare Sub CopyMemory1 Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Sub CopyMemory2 Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As Any, Source As Any, ByVal Length As Long)


Aktiviert wird das ganze mit dem AdressOf-Operator. Deswegen auch erst ab VB5.

Public Sub SetMinMax(Form As Form, x1 As Long, y1 As Long, x2 As Long, y2 As Long)
  ' AufrufProzedur
  WinOldProcMinMax = SetWindowLong(Form.hWnd, GWL_WNDPROC, AddressOf WindowProcMinMax)
  lX1 = x1: lX2 = x2
  lY1 = y1: lY2 = y2
End Sub

Public Sub UnloadMinMax(Form As Form)
  ' Normalzustand
  SetWindowLong Form.hWnd, GWL_WNDPROC, WinOldProcMinMax
End Sub

Private Function WindowProcMinMax(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  ' Hier kommen alle Nachrichten an, die an daß Fenster geschickt werden
  Dim ret As Long
  If uMsg = WM_GETMINMAXINFO Then
    Dim MM As MINMAXINFO
    CopyMemory1 MM, lParam, Len(MM)
    MM.ptMinTrackSize.x = lX1
    MM.ptMinTrackSize.y = lY1
    MM.ptMaxTrackSize.x = lX2
    MM.ptMaxTrackSize.y = lY2
    CopyMemory2 lParam, MM, Len(MM)
    ret = DefWindowProc(hWnd, uMsg, wParam, lParam)
  Else ' Wenn andere Nachricht, dann 'Durchlassen'
    ret = CallWindowProc(WinOldProcMinMax, hWnd, uMsg, wParam, lParam)
  End If
  WindowProcMinMax = ret
End Function


Ab hier braucht es nur noch den Aufruf aus (irgend-)einer Form, der am besten im Form_Load-Ereigniss steht. Natürlich kann der Maximalwert auch weit über dem Windows-Standard liegen...
Damit Windows keinen Grund zum Beschweren hat, sollte spätestens im Form_QueryUnload-Ereigniss der UnloadMinMax-Aufruf erfolgen.

Private Sub Form_Load()
  SetMinMax Me, 200, 200, 500, 500
End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
  UnloadMinMax Me
End Sub


So, daß ist das Grundprinzip der Funktion.
Im Modul gehts noch einen paar Schritte weiter.
Dort werden mehrere Formen auf einmal unterstützt(in einem Array gespeichert) und man kann auch nur eine Größe Angeben(die anderen bleiben dann auf dem Windows-Standard).
Auch ist es möglich, die Werte während der Laufzeit zu verändern.
Desweiteren kann man wahlweiße den Minimieren-, den Maximieren-, den Wiederherstellen- und/oder den Schließen-Button deaktivieren. Im Normalfall nicht wichtig, da dies auch in den Eigenschaften der Form geschehen kann, doch hier gehts auch in MDI-Childformen.

Das einzigste, das man jetzt noch beachten muß, ist daß man seine Programme beim Debuggen nicht in der IDE beendet(Stop), ohne vorher den Aufruf UnloadMinMax gesetzt zu haben. Ein Unload-Ereigniss tritt beim beenden aus der IDE nämlich nicht ein.

Viel Spaß beim Probieren.