Dynamische Userform - schnelle Auswahlbox
#1
Liebe Leserin, lieber Leser,

für die schnelle Auswahl eines oder mehrerer Elemente aus einem Elementensatz könnte man bequem eine Auswahlbox mit Radio- oder Checkboxen nutzen.

Hierzu gibt es hier im Forum im Bereich "Beispiele und Workshops" ja auch schon Beiträge, z.B. der Umbau einer Inputbox usw.

Wer kein API-Freund ist und lieber bei den Excelmöglichkeiten bleiben möchte, der kann sich ja eine Userform dafür hernehmen.

Heute wollen wir uns für diese Aufgabe eine dynamische Userform bauen, die programmatisch erstellt, angezeigt und nach Benutzung wieder gelöscht wird.

Der nachfolgende Beispielcode erstellt eine Auswahlbox mit folgenden Eigenschaften:
  • Einfach- oder Mehrfachauswahl mittels Radiobuttons oder Checkboxen
  • Elementübergabe mittels String, Array, Range oder Objekt
  • Anzeigeposition zentriert, an vorgegebener Position oder an Mausposition z.B. bei Rechtsklick
  • Anzeige mit oder ohne Schaltflächen, für eine kleine Box mit schneller Auswahl
  • und weiteres

   

Zur besseren Demonstration verschiedenster Versionen lade Dir die anliegende Datei herunter....


.xlsb   Dynamische Userform.xlsb (Größe: 41,07 KB / Downloads: 5)

Code:

' #### "Zugriff auf das VBA-Projektobjektmoddel vertrauen" muss im Trustcenter aktiviert sein ####

' Auswahl zwischen mehreren Begriffen auf der Basis einer temporären UserForm
' entweder als Radio-Button (nur ein Eintrag auswählbar) oder als Checkbox (mehrere Einträge auswählbar)

' Beim Aufruf von "GetAuswahl" sind folgende Parameter mitzugeben:
'
' 1:    Ein Array oder ein kommagetrennter String oder ein Excel-Range mit den Begriffen

' alle anderen Parameter sind optional
' 2.    Titeltext (Caption der Userform)
' 3:    MultiSelect:
'       True     Anzeige als Checkbox-Buttons
'       False    Anzeige als Radio-Buttons
' 4:    Prompt:                 Default: "Bitte triff Deine Auswahl!"
'       optional: ein beliebiger Text
' 5, 6: Position
'       einer 0  Anzeige zentriert
'       beide >0 Anzeige der Userform an dieser Position
'       beide -1 Anzeige an der Mausposition

' Rückgabe: Kommagetrennter String, der durch den User durch Split bei Bedarf in ein Aray transferiert werden kann

Option Explicit
Option Compare Text
Option Base 1

' Private Const csPrompt As String = "Bitte triff Deine Auswahl!" ' Wenn leer, dann kein Prompt
Private Const csPrompt As String = ""                           ' Wenn leer, dann kein Prompt, kein Frame
Private Const ccUmrech As Currency = 5.5                        ' Zur Ermittlung der UF-Breite anhand des Textes
Private Const csSep    As String = ","

Private Declare PtrSafe Function GetCursorPos Lib "user32" ( _
        lpPoint As POINTAPI) As Long
Private Type POINTAPI
    x As Long
    y As Long
End Type

Public gvErg() As Variant           ' gvErg(1) ist die Rückgabevariable

Public Function GetAuswahl(ByVal Items As Variant, Optional ByVal Titel As String, _
                           Optional ByVal MultiSel As Boolean, _
                           Optional ByVal MsgTxt As String, _
                           Optional ByVal xPos As Long, Optional ByVal yPos As Long, _
                           Optional NoButtons As Boolean) As Variant

  Dim oUF          As Object
  Dim PT           As POINTAPI
  Dim oFrm         As Object
  Dim bFrame       As Boolean
  Dim sCtrlTyp     As String
  Dim sCode        As String
  Dim sUFName      As String
  Dim iCtrPos      As Long, iAnz  As Long, iNr As Long
  Dim iUFBreite    As Long
  Dim yPosInUF     As Long
  Const ciBtnLinks As Long = 5
  Const ciBtnBreit As Long = 65
  Const ciBtnAbst  As Long = 15

  If IsObject(Items) Then
     iAnz = Items.Count
  ElseIf IsArray(Items) Then
     iAnz = UBound(Items)
  Else
     If Items = vbNullString Then Exit Function
     Items = Split(csSep & Items, csSep)
     iAnz = UBound(Items)
  End If
  ReDim gvErg(iAnz)
  
  bFrame = True                                         ' Mit oder ohne Gruppenrahmen
  yPosInUF = 5

' Userformbreite ermitteln
  If MultiSel Then
     iUFBreite = (ciBtnLinks + ciBtnBreit) * 2 + ciBtnAbst + 12
  Else
     iUFBreite = ciBtnLinks * 2 + ciBtnBreit + 40
  End If
  If Len(Titel) * ccUmrech * 1.2 > iUFBreite Then iUFBreite = Len(Titel) * ccUmrech * 1.2
  If MsgTxt = "" Then MsgTxt = csPrompt
  If Len(MsgTxt) * ccUmrech > iUFBreite Then iUFBreite = Len(MsgTxt) * ccUmrech
  For iNr = 1 To iAnz
      If Len(Items(iNr)) * ccUmrech > iUFBreite Then iUFBreite = Len(Items(iNr)) * ccUmrech
  Next iNr
  
' Userform erstellen
  If Titel = "" Then Titel = Split(ActiveWorkbook.Name, ".")(0)
  Set oUF = ThisWorkbook.VBProject.VBComponents.Add(3)
  With oUF
     .Properties("Width") = iUFBreite                   ' Breite der Userform setzen
     .Properties("Caption") = Titel
  End With

' Label anlegen
  If MsgTxt <> "" Then
     With oUF.Designer.Controls.Add("Forms.Label.1")
          .Name = "Label1"
          .Caption = MsgTxt
          .Left = ciBtnLinks
          .Top = yPosInUF
          .Width = iUFBreite - 20
          .Height = 10
          .TextAlign = 2 'fmTextAlignCenter
          .Font.Bold = True
          yPosInUF = yPosInUF + .Height + 5
     End With
  Else
     bFrame = False
  End If
  
' Optional einen Frame für die Controls anlegen
  If bFrame = True Then
     Set oFrm = oUF.Designer.Controls.Add("Forms.Frame.1")
     With oFrm
          .Name = "Frame1"
          .Left = ciBtnLinks
          .Top = yPosInUF
          .Width = iUFBreite - 20
     End With
     iCtrPos = 5                                        ' Beginnposition Controls
  Else
     Set oFrm = oUF.Designer                            ' Keinen Frame anlegen
     iCtrPos = yPosInUF                                 ' Beginnposition Controls
  End If
  
' Mehrere gleichartige Controls anlegen
  sCtrlTyp = IIf(MultiSel, "CheckBox", "OptionButton")
  For iNr = 1 To iAnz
      With oFrm.Controls.Add("Forms." & sCtrlTyp & ".1", sCtrlTyp & iNr, True)
           .Name = sCtrlTyp & iNr
           .Caption = Items(iNr)
           .Left = ciBtnLinks
           .Top = iCtrPos
           .Width = iUFBreite - 20
           .Height = 15
           iCtrPos = iCtrPos + .Height + 5
' Code dazu
           sCode = sCode _
                 & "Private Sub " & .Name & "_Click()¶" _
                 & "  ControlClick " & .Name & ", " & iNr & "" _
                 & "End Sub¶¶"
      End With
  Next iNr

' Sammel-Sub Code für die Controls schreiben
  sCode = sCode & "Private Sub ControlClick(oCtrl as Object, iNr as Integer)¶"
  If MultiSel Then
     sCode = sCode & "  gvErg(iNr) = IIf(oCtrl.Value = True, oCtrl.Caption, '')¶""
  Else
     sCode = sCode _
           & "  gvErg(1) = oCtrl.Caption¶" _
           & "  Unload Me¶"
  End If
  sCode = sCode & "End Sub¶¶"

  If bFrame Then oFrm.Height = iCtrPos                      ' Framehöhe anpassen an Controlanzahl
  yPosInUF = yPosInUF + (iAnz * 20) + IIf(bFrame, 10, 5)    ' Position für die Buttons festlegen

  If NoButtons = True Then GoTo SetzeCode                   ' Es werden keine Buttons gewünscht, damit UF klein bleibt

' Abbrechen-Button erstellen
  With oUF.Designer.Controls.Add("Forms.CommandButton.1")
       .Name = "Abbrechen"
       .Caption = "Abbrechen"
       .Left = IIf(MultiSel, ciBtnLinks + ciBtnBreit + ciBtnAbst, iUFBreite \ 2 - ciBtnBreit \ 2 - 4)
       .Top = yPosInUF
       .Width = ciBtnBreit
       .Height = 25
       .BackColor = RGB(255, 200, 200)
' Code dazu
       sCode = sCode _
             & "Private Sub " & .Name & "_Click()¶" _
             & "  gvErg(1)=vbNullString¶" _
             & "  Unload Me¶" _
             & "End Sub¶¶"
  End With
  
  If MultiSel Then
   sCode = "Dim bAll as Boolean¶¶" & sCode
' Übernahme-Button erstellen
     With oUF.Designer.Controls.Add("Forms.CommandButton.1")
          .Name = "Uebernehmen"
          .Caption = "Übernehmen"
          .Left = ciBtnLinks
          .Top = yPosInUF
          .Width = ciBtnBreit
          .Height = 25
          .BackColor = RGB(100, 255, 100)
' Code dazu
          sCode = sCode _
                & "Private Sub " & .Name & "_Click()¶" _
                & "  Dim i As Long, Werte As String¶¶" _
                & "  For i = 1 To UBound(gvErg)¶" _
                & "      If gvErg(i) <> vbNullString Then Werte = Werte & gvErg(i) & ','¶" _"
                & "  Next i¶" _
                & "  If Werte = vbNullString Then¶" _
                & "     With Label1¶" _
                & "          .Caption = 'Keine Auswahl getroffen'¶" _"
                & "          .TextAlign = fmTextAlignCenter¶" _
                & "          .BackColor = RGB(255,255,100)¶" _
                & "          .Font.Bold = True¶" _
                & "     End With¶" _
                & "  Else¶" _
                & "     gvErg(1) = Left(Werte, Len(Werte) - 1)¶¶" _
                & "     Unload Me¶" _
                & "  End If¶" _
                & "End Sub¶¶"
     End With

' SetzeAlle-Button erstellen
     With oUF.Designer.Controls.Add("Forms.CommandButton.1")
          .Name = "ButtonAlle"
          .Caption = "Alle auswählen"
          .Left = ciBtnLinks
          .Top = yPosInUF + 35
          .Width = ciBtnBreit
          .Height = 25
          .BackColor = RGB(255, 255, 100)
' Code dazu
          sCode = sCode _
                & "Private Sub " & .Name & "_Click()¶" _
                & "  Dim i As Long, iChk As Long¶¶" _
                & "  bAll = Not bAll¶" _
                & "  iChk = bAll And 1¶" _
                & "  For i = 1 To UBound(gvErg)¶" _
                & "      gvErg(i) = Controls('" & sCtrlTyp & "' & i).Caption¶" _
                & "      Controls('" & sCtrlTyp & "' & i).Value = iChk¶" _
                & "  Next i¶" _
                & "End Sub¶¶"
     End With
     yPosInUF = yPosInUF + 40
  End If
  yPosInUF = yPosInUF + 40
  
SetzeCode:
' Erstellten Code ins Modul einfügen
  oUF.CodeModule.InsertLines 1, Replace(Replace(sCode, "'", Chr(34)), "", vbLf)
  oUF.Properties("Height") = yPosInUF + 25
  
' Userform positionieren
  If xPos < 0 And yPos < 0 Then
     GetCursorPos PT                                    ' Mausposition holen
     xPos = PT.x / 1.67: yPos = PT.y / 1.67
  End If
  If xPos > 0 And yPos > 0 Then
     oUF.Properties("Left") = xPos
     oUF.Properties("Top") = yPos
  End If

' Userform anzeigen
  Set oUF = CallByName(UserForms, "Add", VbMethod, oUF.Name)
  sUFName = oUF.Name
  oUF.Show
  
  GetAuswahl = Replace(gvErg(1), ",", csSep)            ' Auswahl zurückgeben
' Exit Function                                         ' Aktivieren, wenn Userform erhalten bleiben soll

' Userform entfernen
  With ActiveWorkbook.VBProject
       For Each oUF In .VBComponents
           If oUF.Type = 3 And oUF.Name Like sUFName Then
              .VBComponents.Remove oUF
           End If
       Next oUF
  End With
End Function

_________
viele Grüße
Karl-Heinz
Antworten Top


Gehe zu:


Benutzer, die gerade dieses Thema anschauen: volti, 1 Gast/Gäste