ABOUT Visual Basic Programmieren Programmierung Download Downloads Tips & Tricks Tipps & Tricks Know-How Praxis VB VBA Visual Basic for Applications VBS VBScript Scripting Windows ActiveX COM OLE API ComputerPC Microsoft Office Microsoft Office 97 Office 2000 Access Word Winword Excel Outlook Addins ASP Active Server Pages COMAddIns ActiveX-Controls OCX UserControl UserDocument Komponenten DLL EXE
Diese Seite wurde zuletzt aktualisiert am 16.09.2002

Diese Seite wurde zuletzt aktualisiert am 16.09.2002
Aktuell im ABOUT Visual Basic-MagazinGrundlagenwissen und TechnologienKnow How, Tipps und Tricks rund um Visual BasicActiveX-Komponenten, Controls, Klassen und mehr...AddIns für die Visual Basic-IDE und die VBA-IDEVBA-Programmierung in MS-Office und anderen AnwendungenScripting-Praxis für den Windows Scripting Host und das Scripting-ControlTools, Komponenten und Dienstleistungen des MarktesRessourcen für Programmierer (Bücher, Job-Börse)Dies&Das...

Themen und Stichwörter im ABOUT Visual Basic-Magazin
Code, Beispiele, Komponenten, Tools im Überblick, Shareware, Freeware
Ihre Service-Seite, Termine, Job-Börse
Melden Sie sich an, um in den vollen Genuss des ABOUT Visual Basic-Magazins zu kommen!
Informationen zum ABOUT Visual Basic-Magazin, Kontakt und Impressum

Zurück...

ZOrder im Griff

Zurück...


Anzeige

(-hg) mailto:hg_controlszorder@aboutvb.de

Die ZOrder-Methode der Steuerelemente hat einen entscheidenden Nachteil: Sie können damit ein Steuerelement nur an die Spitze (Control.ZOrder [0]) oder ans Ende (Control.ZOrder 1) der ZOrder-Reihenfolge eines Containers verfrachten. Ein Steuerelement an eine beliebige Position innerhalb der Reihung zu verschieben, ist mit Visual Basic-Bordmitteln eigentlich nicht möglich. Sie können damit höchstens eine Holzhammer-Methode anwenden, indem Sie die Steuerelemente eines Containers der Reihe nach an die Spitze setzen. Dasjenige Steuerelement, das letztlich als unterstes erscheinen soll, wird zuerst an die Spitze gesetzt, danach das vorletzte Steuerelement, dann das vorvorletzte - und so weiter, bis schließlich das Steuerelement, das am Ende an der Spitze stehen soll, tatsächlich an die Spitze gesetzt wird. Der folgenden Prozedur übergeben Sie eine Collection, in die Sie zuvor die neu zu reihenden Steuerelemente eingefügt haben:

Public Sub ZOrderControlsFromTop(Controls As Collection)
  Dim i As Integer
  
  For i = Controls.Count To 1 Step -1
    Controls(i).ZOrder 0
  Next 'i
End Sub

Sie brauchen natürlich nicht alle Steuerelemente eines Containers in die zu übergebende Collection einfügen. Dann werden eben nur die darin enthaltenen Steuerelemente entsprechend sortiert ans obere Ende der Reihung gesetzt.

Umgekehrt können Sie auch eine Gruppe von Steuerelemente ans untere Ende der Reihung verschieben:

Public Sub ZOrderControlsFromBottom(Controls As Collection)
  Dim nControl As Control
  
  For Each nControl In Controls
    nControl.ZOrder 1
  Next 'i
End Sub

Um jedoch ein einzelnes Steuerelement oder eine Auswahl von Steuerelementen gezielt an beliebiger Stelle innerhalb der Reihung eines Containers anzuordnen, müssen Sie nach wie vor sämtliche Steuerelemente des Containers in der Collection in der gewünschten Reihenfolge übergeben.

Einen Ausweg aus dieser Umständlichkeit bietet die API-Funktion SetWindowPos. Dies kann dazu verwendet werden, ein bestimmtes Steuerelement (Fenster) hinter einem anderen zu positionieren, das im zweiten Parameter dieser Funktion angegeben wird (Dies ist allerdings nur bei Steuerelementen möglich, die ihr Fenster-Handle über eine hWnd-Eigenschaft zur Verfügung stellen). Die übrigen Parameter, ausgenommen den letzten, können sie ignorieren (den Wert 0 übergeben). Lediglich im letzten Parameter übergeben Sie eine Kombination von Flags, die der für eine ganze Reihe von Zwecken einsetzbaren Funktion mitteilen, dass sie sich hier nur um die ZOrder-Reihung der Fenster kümmern soll.

Private Declare Function SetWindowPos Lib "user32" _
 (ByVal hWnd As Long, ByVal Order As Long, ByVal X As Long, _
 ByVal Y As Long, ByVal cX As Long, ByVal cY As Long, _
 ByVal Flags As Long) As Long 

Private Const SWP_NOMOVE = &H2
Private Const SWP_NOSIZE = &H1
Private Const SWP_NOACTIVATE = &H10
Private Const kFlags = SWP_NOMOVE Or SWP_NOSIZE Or SWP_NOACTIVATE
 
Public Sub ZOrderControls(Controls As Collection)
  Dim i As Integer
  Dim nWnd As Long
  Dim nLastWnd As Long
  
  On Error Resume Next
  nLastWnd = Controls(1).hWnd
  For i = 2 To Controls.Count
    nWnd = Controls(i).hWnd
    If Err.Number Then
      Err.Clear
    Else
      SetWindowPos nWnd, nLastWnd, 0, 0, 0, 0, kFlags
      nLastWnd = nWnd
    End If
  Next 'i
End Sub

Dieser Prozedur ZOrderControls können Sie beliebige Steuerelemente eines Containers übergeben. Die neue Reihung beginnt tatsächlich erst mit dem ersten in der Collection übergebenen Steuerelement.

Möchten Sie nur ein einzelnes Steuerelement um eine Position nach oben oder unten in der Reihung verschieben, können Sie es sich etwas einfacher machen. Anhand des Fenster-Handles dieses Steuerelements können Sie mittels der API-Funktion GetWindow und den beiden Flags GW_HWNDNEXT bzw. GW_HWNDPREV das aktuell nachfolgende bzw. vorhergehende Fenster ermitteln. Mit der bereits beschriebenen Funktion SetWindowPos vertauschen Sie nun einfach nur die Reihenfolge der beiden Fenster:

Private Declare Function GetWindow Lib "user32" _
 (ByVal hWnd As Long, ByVal wCmd As Long) As Long

Private Const GW_HWNDNEXT = 2
Private Const GW_HWNDPREV = 3 

Public Function ZOrderDown(Control As Control) As Boolean
  Dim nWnd As Long
  Dim nControlWnd As Long
  
  nControlWnd = Control.hWnd
  nWnd = GetWindow(nControlWnd, GW_HWNDNEXT)
  If nWnd Then
    SetWindowPos nControlWnd, nWnd, 0, 0, 0, 0, kFlags
    ZOrderDown = True
  End If
End Function

Public Function ZOrderUp(Control As Control) As Boolean
  Dim nWnd As Long
  Dim nControlWnd As Long
  
  nControlWnd = Control.hWnd
  nWnd = GetWindow(nControlWnd, GW_HWNDPREV)
  If nWnd Then
    SetWindowPos nWnd, nControlWnd, 0, 0, 0, 0, kFlags
    ZOrderUp = True
  End If
End Function

Auf ähnliche Weise können Sie ein gegebenes Steuerelement auch vor oder hinter ein anderes, in der Reihung an beliebiger Stelle befindliches Steuerelement verschieben. Neben dem zu verschiebenden Steuerelement übergeben Sie wahlweise das Steuerelement, hinter dem es einsortiert werden soll (AfterControl) oder das Steuerelement vor dem es einsortiert werden soll (BeforeControl):

Public Sub SetZOrder(Control As Control, _
 Optional AfterControl As Control, _
 Optional BeforeControl As Control)

  If AfterControl Is Nothing Then
    If BeforeControl Is Nothing Then
      Err.Raise 5
    Else
      SetWindowPos BeforeControl.hWnd, Control.hWnd, _
       0, 0, 0, 0, kFlags
    End If
  Else
    SetWindowPos Control.hWnd, AfterControl.hWnd, _
     0, 0, 0, 0, kFlags
  End If
End Sub

Nach der Anwendung der API-Funktion GetWindow ahnen Sie vielleicht schon, dass sich damit ein anderes Manko der ZOrder-Geschichte in VB ausbügeln lässt: das Ermitteln der aktuellen ZOrder-Position eines Steuerelements oder das Ermitteln der gesamten Reihenfolge innerhalb eines Containers. Die folgende Funktion ControlsZOrder liefert die Steuerelemente eines Containers in der ZOrder-Reihung sortiert in einem Array ab:

Private Const GW_CHILD = 5

Public Function ControlsZOrder(Container As Object) As Variant
  Dim nControlWnds As Collection
  Dim nWnd As Long
  Dim nControl As Control
  Dim nControls As Object
  Dim nOrderedControls() As Control
  Dim nCount As Integer
  
  With Container
    nWnd = GetWindow(.hWnd, GW_CHILD)
    If nWnd Then
      Set nControlWnds = New Collection
      With nControlWnds
        nCount = 1
        .Add nCount, CStr(nWnd)
        Do
          nWnd = GetWindow(nWnd, GW_HWNDNEXT)
          If nWnd = 0 Then
            Exit Do
          Else
            nCount = nCount + 1
            .Add nCount, CStr(nWnd)
          End If
        Loop
      End With
    End If
    Select Case True
      Case TypeOf Container Is Form
        Set nControls = .Controls
      Case TypeOf Container Is Control
        Set nControls = .Parent.Controls
    End Select
  End With
  ReDim nOrderedControls(1 To nControlWnds.Count)
  On Error Resume Next
  For Each nControl In nControls
    nWnd = nControl.hWnd
    If Err.Number Then
      Err.Clear
    Else
      Set nOrderedControls(nControlWnds(CStr(nWnd))) = nControl
    End If
  Next
  ControlsZOrder = nOrderedControls
End Function

Hier wird zunächst mit GetWindow und dem Flag GW_CHILD das erste Kind-Fenster des gegebenen Containers ermittelt. Davon ausgehend wird für jedes weitere Fenster das jeweils nachfolgende Fenster innerhalb des Containers ermittelt (GW_HWNDNEXT), bis keines mehr gefunden wird (GetWindow hat den Wert 0 zurückgegeben). Für jedes gefundene Fenster-Handle wird die Position (nCount) in eine Collection eingetragen, wobei das Handle in einen String konvertiert als Schlüssel verwendet wird. Nun wird die Controls-Collection des Forms (falls dieses als Container übergeben worden ist) bzw. des Forms, in dem sich der Container befindet, durchlaufen. Anhand der Fenster-Handles (wieder in einen String konvertiert) der Steuerelemente in der Controls-Collection wird der Positionswert aus der zuvor zusammengestellten Sammlung gezogen und das betreffende Steuerelement an dieser Position in ein Array eingetragen. Steuerelemente, die nicht über eine hWnd-Eigenschaft verfügen oder die sich in einem anderen (untergeordneten) Container befinden, werden übersprungen.

Falls Ihnen statt des Arrays eine Collection mit den Steuerelementen in der aktuellen ZOrder-Reihenfolge lieber ist, setzt die folgende Funktion das Array in einen Collection um:

Public Function ControlsZOrderColl(Container As Object) _
 As Collection

  Dim nControlsArr() As Control
  Dim nControls As Collection
  Dim i As Integer
  
  nControlsArr = ControlsZOrder(Container)
  Set nControls = New Collection
  With nControls
    For i = 1 To UBound(nControlsArr)
      .Add nControlsArr(i)
    Next 'i
  End With
  Set ControlsZOrderColl = nControls
End Function

Da Sie ein UserControl-Objekt nicht so ohne weiteres als Container an diese beiden Funktion übergeben können, um auf dessen Controls-Collection zugreifen zu können, muss es mittels eines kleinen Tricks zunächst in ein übergebbares Objekt konvertiert werden (Funktion CUcl, siehe auch: "UserControl unter Kontrolle"khwcucl.htm). Die beiden Funktionen müssen dazu auch noch ein wenig abgewandelt werden: Der Container-Parameter wird gleich als UserControl deklariert, und die Ermittlung der Controls-Collection erübrigt sich.

Public Function UCControlsZOrder(Container As UserControl) _
 As Variant

  Dim nControlWnds As Collection
  Dim nWnd As Long
  Dim nControl As Control
  Dim nOrderedControls() As Control
  Dim nCount As Integer
  
  With Container
    nWnd = GetWindow(.hWnd, GW_CHILD)
    If nWnd Then
      Set nControlWnds = New Collection
      With nControlWnds
        nCount = 1
        .Add nCount, CStr(nWnd)
        Do
          nWnd = GetWindow(nWnd, GW_HWNDNEXT)
          If nWnd = 0 Then
            Exit Do
          Else
            nCount = nCount + 1
            .Add nCount, CStr(nWnd)
          End If
        Loop
      End With
    End If
    ReDim nOrderedControls(1 To nControlWnds.Count)
    For Each nControl In .Controls
      Set nOrderedControls(nControlWnds(CStr(nControl.hWnd))) _
       = nControl
    Next
  End With
  UCControlsZOrder = nOrderedControls
End Function

Public Function UCControlsZOrderColl(Container As UserControl) _
 As Collection

  Dim nControlsArr() As Control
  Dim nControls As Collection
  Dim i As Integer
  
  nControlsArr = UCControlsZOrder(Container)
  Set nControls = New Collection
  With nControls
    For i = 1 To UBound(nControlsArr)
      .Add nControlsArr(i)
    Next 'i
  End With
  Set UCControlsZOrderColl = nControls
End Function

Public Function CUcl(ByVal iUCL As Variant) As UserControl
  Dim nUCL As UserControl
  
  CopyMemory nUCL, ObjPtr(iUCL), 4
  Set CUcl = nUCL
  CopyMemory nUCL, 0&, 4
End Function

Das gleiche Grundprinzip, den jeweiligen Nachfolger zu einem Fenster zu ermitteln, können sie auch verwenden, um die Position eines einzelnen Steuerelements zu ermitteln. Anders als zuvor wird vom Fenster-Handle dieses Steuerelements nicht das erste Kind-Fenster, sondern das erste Fenster-Handle auf der gleichen Ebene (im gleichen Container) ermittelt (GW_HWNDFIRST). Da uns hier nun die übrigen Steuerelemente nicht interessieren, zählen wir lediglich mit - und zwar nur solange, bis das gegebene Steuerelement ausfindig gemacht worden ist. Der Zählerstand ist demnach die Position des Steuerelements in der ZOrder-Reihung:

Private Const GW_HWNDFIRST = 0

Public Function GetZOrder(Control As Control) As Integer
  Dim nWnd As Long
  Dim nCtlWnd As Long
  Dim nCount As Integer
  
  nCtlWnd = Control.hWnd
  nWnd = GetWindow(nCtlWnd, GW_HWNDFIRST)
  If nWnd Then
    nCount = 1
    Do
      nWnd = GetWindow(nWnd, GW_HWNDNEXT)
      If nWnd = 0 Then
        Exit Do
      Else
        nCount = nCount + 1
      End If
      If nWnd = nCtlWnd Then
        GetZOrder = nCount
        Exit Function
      End If
    Loop
  End If
End Function

Nun folgen noch einige Funktionen, die Ihnen mittels GetWindow das erste oder letzte Steuerelement eines Containers oder den Nachfolger oder Vorgänger zu einem Steuerelement liefern, ohne die ZOrder-Reihung zu beeinflussen. Zur Ermittlung des zu einem Fenster-Handle gehörenden Steuerelements dient die Funktion zControlFromHandle, die einfach die Controls-Collection durchsucht, bis das Steuerelement zum gegebenen Handle gefunden wird.

Private Function zControlFromHandle(Controls As Object, _
 ByVal hWnd As Long) As Control

  Dim nControl As Control
  Dim nWnd As Long
  
  On Error Resume Next
  For Each nControl In Controls
    nWnd = nControl.hWnd
    If Err.Number Then
      Err.Clear
    Else
      If nWnd = hWnd Then
        Set zControlFromHandle = nControl
        Exit Function
      End If
    End If
  Next
End Function

Public Function GetFirstChild(Container As Object) As Control
  Dim nWnd As Long
  Dim nControls As Object
  
  With Container
    nWnd = GetWindow(.hWnd, GW_CHILD)
    If nWnd Then
      On Error Resume Next
      Set nControls = .Controls
      If Err.Number Then
        Set nControls = .Parent.Controls
      End If
      Set GetFirstChild = zControlFromHandle(nControls, nWnd)
    End If
  End With
End Function

Private Const GW_HWNDLAST = 1

Public Function GetLastChild(Container As Object) As Control
  Dim nWnd As Long
  Dim nControls As Object
  
  With Container
    nWnd = GetWindow(.hWnd, GW_CHILD)
    If nWnd Then
      nWnd = GetWindow(nWnd, GW_HWNDLAST)
      If nWnd Then
        On Error Resume Next
        Set nControls = .Controls
        If Err.Number Then
          Set nControls = .Parent.Controls
        End If
        Set GetLastChild = zControlFromHandle(nControls, nWnd)
      End If
    End If
  End With
End Function

Public Function GetFirstControl(Control As Control) As Control
  Dim nWnd As Long

  With Control
    nWnd = GetWindow(.hWnd, GW_HWNDFIRST)
    If nWnd Then
      Set GetFirstControl = _
       zControlFromHandle(.Parent.Controls, nWnd)
    End If
  End With
End Function

Public Function GetLastControl(Control As Control) As Control
  Dim nWnd As Long

  With Control
    nWnd = GetWindow(.hWnd, GW_HWNDLAST)
    If nWnd Then
      Set GetLastControl = _
       zControlFromHandle(.Parent.Controls, nWnd)
    End If
  End With
End Function

Public Function GetNextControl(Control As Control) As Control
  Dim nWnd As Long

  With Control
    nWnd = GetWindow(.hWnd, GW_HWNDNEXT)
    If nWnd Then
      Set GetNextControl = _
       zControlFromHandle(.Parent.Controls, nWnd)
    End If
  End With
End Function

Public Function GetPrevControl(Control As Control) As Control
  Dim nWnd As Long

  With Control
    nWnd = GetWindow(.hWnd, GW_HWNDPREV)
    If nWnd Then
      Set GetPrevControl = _
       zControlFromHandle(.Parent.Controls, nWnd)
    End If
  End With
End Function

Modul modControlsZOrder (controlszorder.zip - ca. 2 KB)


Artikel
Zum Download-Bereich dieses Artikel
Mail an den Autor dieses Artikels

KnowHow
Zur KnowHow-Übersicht

KnowHow-Themen
Themen - Allgemeines
Themen - Entwicklungsumgebung (VB-IDE)
Themen - Forms
Themen - Steuerelemente (Controls)
Themen - Grafik
Themen - Dateien
Themen - UserControls
Themen - Einsteiger-Tipps
Themen - Wussten Sie...?

Übersicht nach Titeln in alphabetischer Reihenfolge
Übersicht nach Erscheinungsdatum

Schnellsuche




Zum Seitenanfang

Copyright © 1999 - 2017 Harald M. Genauck, ip-pro gmbh  /  Impressum

Zum Seitenanfang

Zurück...

Zurück...

Download Internet Explorer