Zurück zur Homepage

Polygone Forms

Vor einigen Jahren wollte ich verhindern, dass meine User-Forms geschlossen werden. Aber ich wollte nicht nur einfach mittels dem Ereignis QueryClose das Schließen verhindern, es sollte auch das Kreuz rechts oben nicht zu sehen sein. Letztendlich habe ich einfach via API das Systemmenü entfernt.
Bis dahin habe ich unter anderen auch mit Regionen experimentiert. Runde, vieleckige und auch durchlöcherte User-Formen sind also quasi als Abfallprodukt entstanden. Persönlich habe ich diese aber nie benutzt.
Das Prinzip ist einfach:
Mittels einer API-Funktion wird eine (polygone, runde, elyptische) Region erzeugt. Das Fensterhandle der Form wird ermittelt und mittels der Funktion SetWindowRgn wird die normal rechteckige Region des Fensters überschrieben. Da auch die Titelleiste wegfallen kann und ein Verschieben mit der Maus dann nicht mehr möglich ist, wurde dafür im UserForm_MouseDown-Ereignis Abhilfe geschaffen.

Beispieldatei (polygoneform.zip)

Private Type POINTAPI
  X As Long
  Y As Long
End Type

Private Declare Function FindWindowA Lib "user32" _
  (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Declare Function CreatePolygonRgn Lib "gdi32" _
  (lpPoint As POINTAPI, ByVal nCount As Long, _
  ByVal nPolyFillMode As Long) As Long

Private Declare Function SetWindowRgn Lib "user32" _
  (ByVal hwnd As Long, ByVal hRgn As Long, _
  ByVal bRedraw As Boolean) As Long

Private Declare Function DeleteObject Lib "gdi32" _
  (ByVal hObject As Long) As Long

Private Declare Function SendMessage Lib "user32" _
  Alias "SendMessageA" (ByVal hwnd As Long, _
  ByVal wMsg As Long, ByVal wParam As Long, _
  lParam As Any) As Long

Private Declare Function ReleaseCapture Lib "user32" () As Long

Private Declare Function EnableWindow Lib "user32" _
  (ByVal hwnd As Long, ByVal bEnable As Long) As Long

Private Umriss() As POINTAPI
Private PolygonRegion As Long
Private GesamtRegion As Long
Private MeHwnd As Long
Private iNonModal As Boolean
Private iPixel As Boolean
Private iWerteÜbergeben As Boolean

Private Const WM_NCLBUTTONDOWN = &HA1
Private Const HTCAPTION = 2
Private Const WINDING = 2

 Private Sub UserForm_Activate()
  On Error Resume Next
  'Wenn keine Werte für das Polygon übergeben
  'wurden, werden feste Einstellungen benutzt
  If Not iWerteÜbergeben Then FesteEinstellungen
  'Polygone Region erzeugen
  CreatePolygoneRegion
  'Fensterhandle der Form ermitteln
  MeHwnd = FindWindowA("ThunderXFrame", Me.Caption)
  'Regionen ändern
  GesamtRegion = SetWindowRgn(MeHwnd, PolygonRegion, True)
  If iNonModal Then
    'Jetzt kann auch gleichzeitig im Blatt gearbeitet werden
    EnableWindow FindWindowA("XLMAIN", Application.Caption), 1
  End If
End Sub

 Private Function CreatePolygoneRegion()
  'Polygone Region erzeugen
  PolygonRegion = CreatePolygonRgn(Umriss(1), _
    UBound(Umriss), WINDING)
End Function

Private Sub UserForm_MouseDown(ByVal Button As Integer, _
  ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
  'Ohne diese Prozedur ist kein Verschieben möglich,
  'wenn die Titelleiste nicht sichtbar ist
  If Button = 1 Then
    If MeHwnd <> 0 Then
      ReleaseCapture
      SendMessage MeHwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0
    End If
  Else
    'Mausklick mit der rechten Maustaste schließt die Form
    Unload Me
  End If
End Sub

Private Sub UserForm_Terminate()
  DeleteObject PolygonRegion
  DeleteObject GesamtRegion
End Sub

Private Sub FesteEinstellungen()
  'Wenn nichts als Property übergeben wird,
  'diese Prozedur benutzen
  Dim X_Array, Y_Array
  Dim i As Long
  Dim FaktorX As Double
  Dim FaktorY As Double
  Dim dummy As Double
  'Wenn das Array in Pixeln vorliegt, Faktoren auf 1 setzen
  FaktorX = Me.Width / 50
  FaktorY = Me.Height / 35
  'Kopiert aus Tabellenblatt Formeditor
  X_Array = Array(27, 4, 4, 10, 10, 4, 4, 27, 27, 38, 27)
  Y_Array = Array(9, 9, 11, 11, 20, 20, 22, 22, 26, 16, 5)
  ReDim Preserve Umriss(1 To UBound(X_Array) + 1)
  For i = 0 To UBound(X_Array)
    dummy = X_Array(i)
    Umriss(i + 1).X = dummy * FaktorX
    dummy = Y_Array(i)
    Umriss(i + 1).Y = dummy * FaktorY
  Next
End Sub

Public Property Let Pixelwerte(Wert As Boolean)
  'Durch setzen auf True kann man auch Pixelwerte benutzen
  iPixel = Wert
End Property

Public Property Let ArrayX(X_Array)
  Dim i As Long
  Dim FaktorX As Double
  Dim dummy As Double
  On Error GoTo Fehlerbehandlung
  FaktorX = Me.Width / 50
  'Wenn das Array in Pixeln vorliegt, Faktoren auf 1 setzen
  If iPixel Then FaktorX = 1
  ReDim Preserve Umriss(1 To UBound(X_Array))
  For i = 1 To UBound(X_Array)
    dummy = X_Array(i)
    Umriss(i).X = dummy * FaktorX
  Next
   iWerteÜbergeben = True
  Fehlerbehandlung:
End Property

Public Property Let ArrayY(Y_Array)
  Dim i As Long
  Dim FaktorY As Double
  Dim dummy As Double
  On Error GoTo Fehlerbehandlung
  FaktorY = Me.Height / 35
  'Wenn das Array in Pixeln vorliegt, Faktoren auf 1 setzen
  If iPixel Then FaktorY = 1
  ReDim Preserve Umriss(1 To UBound(Y_Array))
  For i = 1 To UBound(Y_Array)
    dummy = Y_Array(i)
    Umriss(i).Y = dummy * FaktorY
  Next
  iWerteÜbergeben = True
  Fehlerbehandlung:
  End Property

Public Property Let NichtModal(Wert As Boolean)
  'Durch setzen auf True kann gleichzeitig
  'im Tabellenblatt gearbeitet werden
  iNonModal = Wert
End Property