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 30.11.1999

Diese Seite wurde zuletzt aktualisiert am 30.11.1999
Aktuell im ABOUT Visual Basic-MagazinGrundlagenwissen und TechnologienKnow How, Tipps und Tricks rund um Visual BasicAddIns 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 zur AVB-Web-Site, Kontakt und Impressum

Zurück...

Zurück...

(-hg) mailto:hg_backpic@aboutvb.de

Ein Hintergrundbild in einem Form oder einem MDI-Form ist ein netter Anblick, wenn es das Form füllt. Ein Logo, das kleiner als der Arbeitsbereich des Form ist, sieht dagegen nicht so umwerfend aus, wenn es in der linken oberen Ecke kleben muss, da die Zuweisung eines Bildes an die Picture-Eigenschaft eines Forms oder MDI-Forms keinerlei andere Positionierung zulässt. Mit dem hier beschriebenen ActiveX-Steuerelement BackPic können Sie dagegen ein Bild nicht nur automatisch in einem Form oder in einem MDI-Form zentriert anzeigen. Sondern darüber hinaus kann BackPic auch ein Bild automatisch proportional oder füllend in ein Form einpassen, oder auch die gesamte Fläche des Forms mit dem Bild gekachelt füllen.


Das Steuerelement BackPic ermöglicht verschiedene Hintergrunddarstellungen auch bei MDI-Forms

Sie können über die Eigenschaft Style die Anzeigeart wählen und die Anzeige über die Eigenschaft Enabled ein- oder ausschalten. Der Eigenschaft Picture weisen Sie das anzuzeigende Bild zu, das allerdings zur Laufzeit ein eventuell der Picture-Eigenschaft des Forms bzw. MDI-Forms ersetzt. Damit das BackPic-Steuerelement problemlos auf einem MDI-Form platziert werden kann, ist die Eigenschaft MSDN-Library - VB InvisibleAtRuntimeInvisibleAtRuntime des UserControls auf True gesetzt. Wir können uns seine Oberfläche aber dennoch zunutze machen, wie Sie im folgenden sehen werden.

Die eigentliche Arbeit der Bildanzeige wird in der privaten Prozedur zRefresh erledigt, die von mehreren Stellen im Code des UserControls aus aufgerufen wird. Schauen wir uns jedoch zunächst die Implementierung der drei Eigenschaften des BackPic-Steuerelements an.

Private pEnabled As Boolean

Public Property Get Enabled() As Boolean
  Enabled = pEnabled
End Property

Public Property Let Enabled(ByVal New_Enabled As Boolean)
  If pEnabled <> New_Enabled Then
    pEnabled = New_Enabled
    If Ambient.UserMode Then
      zRefresh
    End If
  End If
  PropertyChanged "Enabled"
End Property

Der Wert der Eigenschaft Enabled wird in der privaten Variablen pEnabled festgehalten. Sie wird nur dann neu gesetzt, wenn sich ihr Wert ändern soll. Im Laufzeit-Modus des UserControls (MSDN-Library - VB Ambient.UserModeAmbient.UserMode = True) wird die Anzeige aktualisiert. Ist pEnabled gleich False, wird das angezeigte Bild gelöscht, andernfalls wird es angezeigt.

Private pPicture As StdPicture

Public Property Get Picture() As StdPicture
  Set Picture = pPicture
End Property

Public Property Let Picture(New_Picture As StdPicture)
  zSetPicture New_Picture
End Property

Public Property Set Picture(New_Picture As StdPicture)
  zSetPicture New_Picture
End Property

Private Sub zSetPicture(New_Picture As StdPicture)
  Set pPicture = New_Picture
  If Ambient.UserMode Then
    zRefresh
  Else
    Set UserControl.Picture = pPicture
    UserControl_Resize
  End If
  PropertyChanged "Picture"
End Sub

Eine Änderung des in der privaten Variablen pPicture abgelegten Bild-Objekts ist dagegen nur auf sehr aufwändige Weise schlüssig zu ermitteln. Daher aktualisieren wir die Anzeige im Laufzeit-Modus jedes Mal, wenn die Eigenschaft gesetzt wird. Im Entwicklungsmodus zeigen wir das Bild im UserControl selbst an. Der Anwender des Steuerelements hat damit genau so eine optische Kontrolle darüber, welches Bild geladen ist, als wenn es in die Picture-Eigenschaft des Forms geladen worden wäre. Da in Visual Basic die Zuweisung eines Bildes zur Form-Eigenschaft Picture auch ohne das Set-Schlüsselwort erfolgen kann, ist die Picture-Eigenschaft hier sowohl mit einer Set- als auch mit einer Let-Prozedur implementiert. Diese beiden Prozeduren reichen die Übergabe einfach an die private Prozedur zSetPicture weiter, in der die eigentliche Zuweisung erfolgt.

Im Ereignis Resize des UserControls wird dafür gesorgt, dass zur Entwicklungszeit das UserControl nie größer als das Bild werden kann. Die Größe des Bildes ermitteln wir wie in Hoch mal breit?"Hoch mal breit?" beschrieben. Dass die Prozedur bei den in ihr selbst verursachten Größenänderungen mehrmals durchlaufen wird, verhindern wir mit der statischen Variablen sInProc (Aller guten Dinge ist eins"Aller guten Dinge ist eins"). Damit das BackPic-Steuerelement zur Entwicklungszeit vor dem Form-Hintergrund sichtbar bleibt, wird über ein Shape-Steuerelement, dessen DrawMode-Eigenschaft auf 6 (- Invers) eingestellt ist, eine gestrichelte Umrisslinie dargestellt.

Private Sub UserControl_Resize()
  Dim nPicWidth As Single
  Dim nPicHeight As Single
  
  Static sInProc As Boolean

  If sInProc Then
    Exit Sub
  Else
    sInProc = True
  End If
  If Not Ambient.UserMode Then
    With UserControl
      If Not zIsNothing(pPicture) Then
        nPicWidth = .ScaleX(pPicture.Width, vbHimetric, vbTwips)
        nPicHeight = .ScaleY(pPicture.Height, vbHimetric, vbTwips)
        If .Width > nPicWidth Then
          .Width = nPicWidth
        End If
        If .Height > nPicHeight Then
          .Height = nPicHeight
        End If
      End If
      shp.Move 0, 0, .ScaleWidth, .ScaleHeight
    End With
  End If
  sInProc = False
End Sub

Die Style-Eigenschaft schließlich bestimmt die Anzeigeart des Bildes. Ihr können die vier enumerierten Werte der bpStyleConstants zugewiesen werden.

Public Enum bpStyleConstants
  bpCenter
  bpAdjust
  bpTile
  bpStretch
End Enum

Private pStyle As bpStyleConstants

Public Property Get Style() As bpStyleConstants
  Style = pStyle
End Property

Public Property Let Style(ByVal New_Style As bpStyleConstants)
  Select Case New_Style
    Case pStyle
    Case bpCenter To bpStretch
      pStyle = New_Style
      If Ambient.UserMode Then
        zRefresh
      End If
    Case Else
      Err.Raise 380
  End Select
  PropertyChanged "Style"
End Property

Hier achten wir wieder darauf, ob der zugewiesene Wert eine Änderung der Darstellung erfordert. Wird der bereits eingestellte Wert zugewiesen, passiert gar nichts. Wird ein ungültiger Wert zugewiesen, also ein Wert, der nicht in der Enumeration enthalten ist, wird der Fehler 380 (Ungültiger Wert für die Eigenschaft) ausgelöst. Ansonsten erfolgt die Zuweisung und zur Laufzeit wird die Anzeige über den Aufruf der privaten Prozedur zRefresh aktualisiert.

Beim erstmaligen Platzieren eines BackPic-Steuerelements auf einem Form wird zunächst als Voreinstellung die Variable pEnabled auf True gesetzt. Dann erfolgt eine Prüfung, ob sich das Steuerelement auf einem Form oder MDI-Form befindet, da es nur auf solchen verwendet werden kann. Ist dies nicht der Fall, wird ein Fehler mit einer entsprechenden Beschreibung ausgelöst. Anschließend wird mit einem Aufruf von zSetPicture ein eventuell bereits vorhandenes Bild in der Picture-Eigenschaft des Forms übernommen und es erfolgt eine Rückfrage, ob nach der Übernahme das Bild aus der Picture-Eigenschaft des Forms gelöscht werden soll. Die Prüfung, ob das Form bereits ein Bild enthält, erfolgt hier über die private Funktion zIsNothing. Diese Funktion stellt im Gegensatz zu einer einfachen Prüfung mit "Is Nothing" sicher, dass auch ein leeres StdPicture-Objekt erkannt wird (Wenn Nichts nicht Nichts ist"Wenn Nichts nicht Nichts ist").

Private Sub UserControl_InitProperties()
  Dim nControl As Control
  
  pEnabled = True
  With UserControl
    .BackColor = Ambient.BackColor
    If TypeOf .Parent Is MDIForm Then
    ElseIf TypeOf UserControl.Parent Is Form Then
    Else
      Err.Raise vbObjectError + 10000, Ambient.DisplayName, _
       "BackPic kann nur auf Forms und MDIForms in Visual Basic " & _
       "verwendet werden!"
      Exit Sub
    End If
    With .Parent
      For Each nControl In .Controls
        If nControl.Name <> Ambient.DisplayName Then
          If TypeName(nControl) = TypeName(Me) Then
            Err.Raise vbObjectError + 10001, Ambient.DisplayName, _
             "Es kann nur 1 BackPic-Steuerelement auf einem Form " & _
             "platziert werden!"
            Exit Sub
          End If
        End If
      Next
      If Not zIsNothing(.Picture) Then
        zSetPicture .Picture
        If MsgBox("Picture-Objekt des Forms löschen?", _
         vbYesNo Or vbDefaultButton2 Or vbQuestion, Ambient.DisplayName) _
         = vbYes Then
          Set .Picture = Nothing
        End If
      End If
    End With
  End With
End Sub

Private Function zIsNothing(Obj As Object, _
 Optional ByVal CheckHandleIfPicture As Boolean = True) As Boolean
  Dim nIsNothing As Boolean
  
  nIsNothing = CBool(Obj Is Nothing)
  If Not nIsNothing Then
  If CheckHandleIfPicture Then
    If TypeOf Obj Is StdPicture Then
    nIsNothing = Not CBool(Obj.Handle)
    End If
  End If
  End If
  zIsNothing = nIsNothing
End Function

Das Zusammenspiel des BackPic-Steuerelements mit dem Form, auf dem es platziert ist, beruht auf der in Lausch-Eingriffe"Lausch-Eingriffe" beschriebenen Technik des Abfangens der Form-Ereignisse. Wie Sie weiter unten noch sehen werden, brauchen wir die Information, wann sich die Größe des Forms ändert.

Daher ermitteln wir hier im ReadProperties-Ereignis des UserControls den Typ des Forms und weisen ihn der entsprechenden mit WithEvents deklarierten Form-Variablen zu. Damit wir später nicht ständig zwischen beiden Möglichkeiten (Form oder MDIForm) unterscheiden müssen, weisen wir das Form auch noch der privaten Variablen mForm zu. Sie ist vom Datentyp Form - dies ist zulässig, da ein MDI-Form immer auch als vom Datentyp Form erkannt und akzeptiert wird. Speziell zur sauberen Aktualisierung eines MDI-Forms brauchen wir das Fenster-Handle des Forms. Bei MDI-Forms hat der Arbeitsbereich ein eigenes Fenster-Handle, wohingegen die hWnd-Eigenschaft eines MDI-Forms das Handle des Hauptfensters zurück gibt. Da das Handle des Arbeitsbereichs jedoch das erste Kind-Fenster des MDI-Fensters darstellt, kann es problemlos mit einem Aufruf der API-Funktion MSDN-Library - API GetWindowGetWindow und dem Flag GW_CHILD ermittelt werden. Bei einem normalen Form brauchen wir das Fenster-Handle nicht zu ermitteln.

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

Private mClientWnd As Long
Private mForm As Form

Private WithEvents eMDIForm As MDIForm
Private WithEvents eForm As Form

Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
  Const GW_CHILD = 5
  
  If Ambient.UserMode Then
    shp.Visible = False
    If TypeOf UserControl.Parent Is MDIForm Then
      Set eMDIForm = UserControl.Parent
      Set mForm = eMDIForm
      mClientWnd = GetWindow(mForm.hwnd, GW_CHILD)
    ElseIf TypeOf UserControl.Parent Is Form Then
      Set eForm = UserControl.Parent
      Set mForm = eForm
    Else
      Err.Raise vbObjectError + 10000, Ambient.DisplayName, _
       "BackPic kann nur auf Forms und MDIForms in Visual Basic " & _
       "verwendet werden!"
    End If
  Else
    Set UserControl.Picture = pPicture
  End If
  Me.Enabled = PropBag.ReadProperty("Enabled", True)
  pStyle = PropBag.ReadProperty("Style", bpCenter)
  zSetPicture PropBag.ReadProperty("Picture", Nothing)
End Sub

Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
  PropBag.WriteProperty "Enabled", pEnabled, True
  PropBag.WriteProperty "Picture", pPicture, Nothing
  PropBag.WriteProperty "Style", pStyle, bpCenter
End Sub

Wie bereits erwähnt, erfolgt die eigentliche Bearbeitung der Anzeige des Bildes in der privaten Prozedur zRefresh. Sie wird aufgerufen (wie Sie in den obenstehenden Code-Ausschnitten sicher bereits gesehen haben), wenn sich eine der Eigenschaften des BackPic-Steuerelements geändert hat. Sie muss ebenfalls aufgerufen werden, wenn sich die Größe des Forms geändert hat. Da wir sowohl für ein einfaches Form als auch ein MDI-Form eine Ereignisempfänger-Variable deklariert haben, können wir auch für beide Form-Typen das Resize-Ereignis mit den entsprechenden Ereignis-Prozeduren abfangen. Ist das Form nicht minimiert, wird dort die Prozedur zRefresh aufgerufen.

Weiterhin wird sie von der öffentlichen Methode Refresh aufgerufen, über die gegebenenfalls von außen per Code eine Aktualisierung der Anzeige angestoßen werden kann.

Private Sub eForm_Resize()
  If eForm.WindowState <> vbMinimized Then
    zRefresh
  End If
End Sub

Private Sub eMDIForm_Resize()
  If eMDIForm.WindowState <> vbMinimized Then
    zRefresh
  End If
End Sub

Public Sub Refresh()
  zRefresh
End Sub

Kommen wir nun zur privaten Prozedur zRefresh. Die benötigten Deklarationen und Dimensionierungen der verwendeten lokalen Variablen lauten:

Private Type RECT
  Left As Long
  Top As Long
  Right As Long
  Bottom As Long
End Type

Private Declare Function GetClientRect Lib "user32" _
 (ByVal hwnd As Long, lpRect As RECT) As Long
Private Declare Function InvalidateRect Lib "user32" _
 (ByVal hwnd As Long, lpRect As RECT, ByVal bErase As Long) _
 As Long


Private Sub zRefresh()
  Dim nRect As RECT
  Dim nPicWidth As Single
  Dim nPicHeight As Single
  Dim nDestScale As Single
  Dim nImageScale As Single
  Dim nLeft As Single
  Dim nTop As Single
  Dim nRow As Long
  Dim nCol As Long
  Dim nScaleHeight As Single
  Dim nScaleWidth As Single

Wir prüfen zuerst, ob die Eigenschaft Enabled des Steuerelements auf True gesetzt ist, weil es anderenfalls keinen Sinn hat, den Anzeigemechanismus abzuarbeiten. Ebenso ist die Bearbeitung sinnlos, wenn das Steuerelement sich nicht auf einem Form bzw. MDI-Form befinden sollte - ergibt die Prüfung der Form-Variablen mForm Nothing, wird die Prozedur gleich wieder verlassen. Die Prozedur wird auch verlassen, falls die Variable pPicture kein Bildobjekt enthalten sollte.

  If pEnabled Then
    If mForm Is Nothing Then
      Exit Sub
    End If
    If zIsNothing(pPicture) Then
      Exit Sub
    End If

Das ganze weitere Prinzip beruht darauf, dass wir auf dem UserControl, dessen Größe nun dem Arbeitsbereich des Forms entspricht, das Bild in der gewünschten Weise abbilden und anschließend das Gesamtbild der Picture-Eigenschaft des Forms zuweisen wollen. Bei normalen Forms könnten wir zwar den Umweg über das UserControl sparen. Doch da der Performance-Unterschied entgegen aller Vermutung nicht spürbar ist, verzichten wir auf die separate Auslegung für MDI-Forms und normale Forms.

Zunächst wird das zur Laufzeit unsichtbar bleibende UserControl auf die Größe des Arbeitsbereichs des Forms gesetzt. Bei MDI-Forms ist dies der Bereich, in dem die MDI-Child-Forms geöffnet werden. Dieser Bereich wird gegebenenfalls von an den Rändern angedockten anderen Steuerelementen verkleinert. Bei gewöhnlichen Forms schränken an den Rändern angedockte Steuerelemente den Arbeitsbereich nicht ein.

    With mForm
      UserControl.Size .ScaleWidth, .ScaleHeight
    End With

Dann ermitteln wir die Größe des Bildes und halten zur Verbesserung der Performance die Größe des Arbeitsbereichs in den lokalen Variablen nScaleWidth und nScaleHeight fest.

    With UserControl
      nPicWidth = .ScaleX(pPicture.Width, vbHimetric, vbPixels)
      nPicHeight = .ScaleY(pPicture.Height, vbHimetric, vbPixels)
      nScaleWidth = .ScaleWidth
      nScaleHeight = .ScaleHeight

Nun bereiten wir das UserControl für die folgenden Anzeigeoperationen vor. Wir weisen ihm die Hintergrundfarbe des Forms zu und setzen die Eigenschaft AutoRedraw auf True. Dies ist notwendig, weil das UserControl ja nicht sichtbar ist und wir das gezeichnete Ergebnis bewahren müssen. Anschließend löschen wir mit einem Cls die alte Darstellung (die zuvor erfolgte Zuweisung der Hintergrundfarbe hätte die Darstellung nur dann gelöscht, wenn sich die Hintergrundfarbe geändert hätte).

      .BackColor = mForm.BackColor
      .AutoRedraw = True
      .Cls

Jetzt sind wir soweit, dass wir die Einstellung der Eigenschaft Style auswerten und dementsprechend das anzuzeigende Bild aufbauen können.

      Select Case pStyle
        Case bpCenter

Dies ist der einfachste aller Fälle - das Bild wird einfach zentriert in die Fläche des UserControls gemalt:

          .PaintPicture pPicture, (nScaleWidth - nPicWidth) \ 2, _
           (nScaleHeight - nPicHeight) \ 2
        Case bpAdjust

Diese Einstellung bedeutet, dass das Bild größtmöglichst in der Fläche dargestellt werden soll, ohne dass die Seitenverhältnisse geändert werden. Die Berechnung der Größe und der Position des Bildes erfolgt dem Prinzip nach dem Verfahren aus Zwangspassnahmen"Zwangspassnahmen":

          If CBool(nScaleWidth > 0) And CBool(nScaleHeight > 0) Then
            nDestScale = nScaleWidth / nScaleHeight
            nImageScale = nPicWidth / nPicHeight
            If nDestScale >= nImageScale Then
              nPicWidth = nPicWidth / (nPicHeight / nScaleHeight)
              nPicHeight = nScaleHeight
              nLeft = (nScaleWidth - nPicWidth) \ 2
            Else
              nPicHeight = nPicHeight / (nPicWidth / nScaleWidth)
              nPicWidth = nScaleWidth
              nTop = (nScaleHeight - nPicHeight) \ 2
            End If
            .PaintPicture pPicture, nLeft, nTop, nPicWidth, nPicHeight
          End If
        Case bpTile

Die aufwändigste Operation ist die Kachelung, die um so länger dauert, je größer die Fläche und je kleiner das darzustellende Bild ist. Hierbei muss das Bild Zeile für Zeile und Spalte für Spalte je einmal gemalt werden.

          Do While nTop < nScaleHeight
            Do While nLeft < nScaleWidth
              .PaintPicture pPicture, nLeft, nTop
              nLeft = nLeft + nPicWidth
            Loop
            nLeft = 0
            nTop = nTop + nPicHeight
          Loop
        Case bpStretch

Ähnlich einfach wie das Zentrieren ist das füllende Einpassen des Bildes in die Fläche. Dabei wird das Bild auf die volle Breite bzw. volle Höhe der Fläche gedehnt oder gestaucht. Dabei wird allerdings das ursprüngliche Seitenverhältnis des Bildes ignoriert und es wird verzerrt dargestellt.

          .PaintPicture pPicture, 0, 0, .ScaleWidth, .ScaleHeight
      End Select

Jetzt weisen wir die Darstellung des Bildes im UserControl der Picture-Eigenschaft des Form zu und setzen ressourcenschonend AutoRedraw wieder auf False.

      Set mForm.Picture = .Image
      .AutoRedraw = False
    End With

Der folgende Else-Zweig wird ausgeführt, wenn die Enabled-Eigenschaft des BackPic-Steuerelements auf False gesetzt ist - die Anzeige des Bildes im Form wird gelöscht.

  Else
    Set mForm.Picture = Nothing
  End If

Leider hat ein MDI-Form ein Problem damit, ein zur Laufzeit per Code frisch zugewiesenes Bild bzw. dessen Löschung überhaupt darzustellen - die Zuweisung eines Bildes oder von Nothing zeigt einfach keine Wirkung. Da ein MDI-Form auch nicht über eine Refresh-Eigenschaft verfügt, bleibt uns nichts anderes übrig, als über API-Funktionen die Darstellung anzustoßen. Die API-Funktion MSDN-Library - API InvalidateRectInvalidateRect teilt Windows mit, dass ein bestimmter rechteckiger Bereich eines Fensters ungültig geworden ist und löst sofort eine Neudarstellung aus, wenn ihr letzter Parameter True ist. Ehe wir uns nun langwierig mit der Umrechnung der in Twips vorliegenden Maße des Arbeitsbereichs aufhalten, ermitteln wir das Rechteck mit einem Aufruf der API-Funktion MSDN-Library - API GetClientRectGetClientRect, bevor wir InvalidateRect aufrufen. Hier sehen Sie auch den Sinn der Variablen mClientWnd, in der wir im ReadProperties-Ereignis das Fenster-Handle des MDI-Arbeitsbereichs festgehalten haben.

  If TypeOf mForm Is MDIForm Then
    GetClientRect mClientWnd, nRect
    InvalidateRect mClientWnd, nRect, True
  End If
End Sub


Code des Controls BackPic Code des Controls BackPic

Das Projekt BackPicOCX (backpic.zip - ca. 13 KB)

ActiveX-Control als Setup (ohne VB 6-Runtime!) (backpics.zip - ca. 273 KB)



Komponenten-Übersicht

Zum Seitenanfang

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

Zum Seitenanfang

Zurück...

Zurück...