|
Zur Anzeige von eigenen Objekten auf dem Desktop gibt es eigentlich eine eigene Technologie, den ActiveDesktop. Doch diese ist relativ kompliziert (zumindest aus der Sicht von Visual Basic) und setzt zudem beim Anwender den Internet Explorer voraus. Es ist jedoch gar nicht so schwierig, mittels ein paar weniger API-Funktionen dem Desktop beliebige Steuerelemente unterzujubeln. Diese bleiben auch erhalten, wenn alle Anwendungsfenster (etwa über jenes Symbol auf der Schnellstartleiste) minimiert werden.
Die Aufgabe besteht zunächst darin, dasjenige Fenster zu finden, das die eigentliche Desktop-Oberfläche darstellt, auf der auch die Desktop-Icons angeordnet sind. Die API-Funktion GetDesktopWindow liefert allerdings nicht das Handle zu diesem gesuchten Fenster, sondern das des blanken Bildschirmuntergrundes, so zu sagen der "Mutter aller Fenster". Die Desktop-Oberfläche selbst ist dagegen eine eigene Anwendung der Shell. Sie trägt den "uralten" Namen "Program Manager" (der einigen von Ihnen noch aus Windows 3.x-Zeiten bekannt sein wird). Deren Hauptfenster müssen Sie zunächst ermitteln, über die API-Funktion FindWindow:
Private Declare Function FindWindow Lib "user32" _
Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
ProgManWindow = FindWindow("Progman", "Program Manager")
Die gesuchte Desktop-Oberfläche ist ein Kindfenster der zweiten Ebene, an die wir nun problemlos über einen zweifach verschachtelten Aufruf der API-Funktion GetWindow mit der Konstanten GW_CHILD herankommen:
Private Declare Function GetWindow Lib "user32" _
(ByVal hwnd As Long, ByVal wCmd As Long) As Long
DesktopWindow = _
GetWindow(GetWindow(ProgManWindow, GW_CHILD), GW_CHILD)
Nun können Sie ein Steuerelement (beispielsweise eine PictureBox oder ein UserControl) von einem Form mit Hilfe der API-Funktion SetParent zu einem Kindfenster dieses Desktop-Fensters machen:
Private Declare Function SetParent Lib "user32" _
(ByVal hWndChild As Long, ByVal hWndNewParent As Long) _
As Long
SetParent Steuerelement.hwnd, DesktopWindow
Wenn Sie als das auf den Desktop zu verschiebende Steuerelement ein UserControl verwenden, haben Sie sogar freie Hand bei der Gestaltung des Aussehens und der Funktionalität. Sie können auch das Verschieben auf den Desktop in das UserControl verlagern und dieses damit völlig "autark" machen - außerhalb des UserControls ist dann kein weiterer Code mehr notwendig.
Wie solch ein UserControl aussehen könnte, zeigt Ihnen das folgende Code-Beispiel. Die Kontrolle über das Eltern-Form, auf dem das UserControl ursprünglich zu platzieren ist (ohne Form kann ein UserControl ja nicht existieren) erfolgt nach dem Prinzip, über das Sie im Artikel "Lausch-Eingriffe" mehr erfahren können. Das UserControl kann (wenn dessen Eigenschaft Movable auf True gesetzt wird) verschoben werden, wenn der linke Mausknopf über dem Label lblCaption niedergedrückt wird - dazu erfahren Sie mehr im Artikel "Schiebung!"khwmoveobject.
Das UserControl ist als ControlContainer angelegt (Eigenschaft ControlContainer auf True gesetzt), so dass darauf auch von "außen" beliebige Steuerelemente platziert werden können. Im Beispiel-Projekt zu diesem Artikel haben wir ein Kalender-Steuerelement aus den Microsoft Common Controls 2 darauf platziert.
Private Declare Function FindWindow Lib "user32" _
Alias "FindWindowA" (ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Private Declare Function GetWindow Lib "user32" _
(ByVal hwnd As Long, ByVal wCmd As Long) As Long
Private Declare Function SetParent Lib "user32" _
(ByVal hWndChild As Long, ByVal hWndNewParent As Long) As Long
Private Declare Function ReleaseCapture Lib "user32" () 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 mOldParent As Long
Private WithEvents eForm As Form
Private pMovable As Boolean
Public Property Get Movable() As Boolean
Movable = pMovable
End Property
Public Property Let Movable(New_Movable As Boolean)
pMovable = New_Movable
PropertyChanged "Movable"
End Property
Private Sub cmdClose_Click()
Unload eForm
End Sub
Private Sub imgIcon_MouseDown(Button As Integer, _
Shift As Integer, X As Single, Y As Single)
PopupMenu mnu, vbPopupMenuRightButton
End Sub
Private Sub lblCaption_MouseDown(Button As Integer, _
Shift As Integer, X As Single, Y As Single)
Const WM_NCLBUTTONDOWN = &HA1
Const HTCAPTION = 2
Select Case Button
Case vbLeftButton
If pMovable Then
ReleaseCapture
SendMessage UserControl.hwnd, WM_NCLBUTTONDOWN, _
HTCAPTION, 0
End If
End Select
End Sub
Private Sub mnuClose_Click()
Unload eForm
End Sub
Private Sub eForm_Load()
Const GW_CHILD = 5
With UserControl
mOldParent = SetParent(.hwnd, _
GetWindow(GetWindow(FindWindow("Progman", _
"Program Manager"), GW_CHILD), GW_CHILD))
Extender.Move (Screen.Width - .Width) \ 2, _
(Screen.Height - .Height) \ 2
End With
End Sub
Private Sub eForm_Unload(Cancel As Integer)
SetParent UserControl.hwnd, mOldParent
End Sub
Private Sub UserControl_InitProperties()
zInit
End Sub
Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
zInit
pMovable = PropBag.ReadProperty("Movable", False)
End Sub
Private Sub zInit()
If Ambient.UserMode Then
Set eForm = UserControl.Parent
With eForm
.BorderStyle = 0
.Caption = .Caption
.Visible = False
End With
End If
End Sub
Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
PropBag.WriteProperty "Movable", pMovable, False
End Sub
Private Sub UserControl_Terminate()
Set eForm = Nothing
End Sub
Private Sub UserControl_Resize()
On Error Resume Next
With UserControl
shp.Move 0, 0, .ScaleWidth, .ScaleHeight
cmdClose.Move .ScaleWidth - 1.75 * cmdClose.Width, _
0.75 * cmdClose.Height
End With
With cmdClose
imgIcon.Top = .Top + (.Height \ 2) - (imgIcon.Height \ 2)
lblCaption.Top = .Top + (.Height \ 2) - _
(lblCaption.Height \ 2)
End With
End Sub
|