vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#
Sch?tzen Sie Ihre Software vor Software-Piraterie - mit sevLock 1.0 DLL!  
 vb@rchiv Quick-Search: Suche startenErweiterte Suche starten   RSS-Feeds  | Newsletter  | Impressum  | Datenschutz  | vb@rchiv CD Vol.6  | Shop Copyright ©2000-2017
 
zurück
Rubrik: Dialoge/Dateien   |   VB-Versionen: VB5, VB601.02.01
Ordnerauswahl-Dialog in VB

Mit Visual-Basic-Funktionen kaum zu lösen: die Auswahl eines Ordners mit dem von Windows dafür vorgesehenen Stadard-Dialog. Eine Win-API-Funktion löst das Problem.

Autor:  Ingo SteinhausBewertung:     [ Jetzt bewerten ]Views:  24.380 

Neue Version! sevEingabe 3.0 (für VB6 und VBA)
Das Eingabe-Control der Superlative! Noch besser und noch leistungsfähiger!
Jetzt zum Einführungspreis       - Aktionspreis nur für kurze Zeit gültig -

Mit Visual-Basic-Funktionen kaum zu lösen: die Auswahl eines Ordners mit dem von Windows dafür vorgesehenen Stadard-Dialog. Eine Win-API-Funktion löst das Problem.

Ordner wir die Profis

Mit dem Ordnerauswahldialog der Windows-Oberfläche (siehe Bild) haben Sie allen nur denkbaren Komfort: Sie können sowohl auf lokale Laufwerke als auch auf freigegebene Netzwerk-Ordner zugreifen. Leider gibt es in Visual Basic (VB) und dem in Programmen wie Microsoft Office eingebauten Visual Basic für Applikationen (VBA) leider immer noch keinen Befehl, um diesen eleganten Dialog aufzurufen.

Auch in Visual Basic möglich: Zwar bietet die Sprache keinen Befehl, diesen Dialog aufzurufen. Sie können aber eine Funktion der Win-API nutzen.

Sie können aber trotzdem diese Ordnerauswahl auf den Bildschirm zaubern. Nötig dafür ist lediglich eine einfache API-Funktion. Wie Sie API-Funktionen in eigene VB-Projekte einbauen, zeigt Ihnen der Workshop WinAPI-Funktionen in VB nutzen.

Das nötige Listing wird auf den nächsten Seiten Schritt für Schritt aufgebaut und erklärt. Den vollständigen Quellcode finden Sie auf der letzten Seite diese Beitrags. Dort können Sie ihn auch als Textdatei auf Ihren lokalen PC herunterladen.

Den benötigten Datentyp definieren

Um den abgebildeten Standarddialog zur Auswahl eines Verzeichnisses aufzurufen, müssen Sie zuerst einen Typ, Konstanten und API-Funktionen in Ihrem VB-Projekt deklarieren. Die zentrale API-Funktion ist "SHBrowseForFolder". Sie ruft die Dialogbox auf, die Sie auf der ersten Seite dieses Beitrags sehen. Ihr muss ein vom Anwender definierter Datentyp übergeben werden. Deklarieren Sie diesen Datentyp in einem Modul wie folgt:

Type BrowseInfo
    hWndOwner As Long
    pIDLRoot As Long
    pszDisplayName As Long
    lpszTitle As Long
    ulFlags As Long
    lpfnCallback As Long
    lParam As Long
    iImage As Long
End Type

Mit diesem Datentyp können Sie die API-Funktion konfigurieren. Für die korrekte Arbeit der Funktion sind lediglich die drei Variablen hWndOwner, lpszTitle und ulFlags notwendig. Trotzdem müssen Sie den vollständigen Datentyp definieren, da die API-Funktion sonst nicht arbeiten kann. Die Bedeutungen der drei Variablen sind:
HWndOwner: Das Handle des "Besitzer"-Fensters, also des Fensters, von dem aus der Verzeichnisdialog aufgerufen wird.
lpszTitle: Diese Variable enthält die Beschriftung des Dialogs, die in der Titelleiste des Dialogfensters angezeigt wird.
ulFlags: Diese Variable beeinflusst die Verhaltensweise des Dialogs. Dazu mehr auf der nächsten Seite.

Das Fenster-Handle hWndOwner wird mit einer weiteren API-Funktion ermittelt, die Sie ebenfalls im entsrechenden Modul deklarieren müssen: GetActiveWindow. Die Deklaration heißt:

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

Dann deklarieren Sie noch die API-Funktion SHBrowseForFolder:

Declare Function SHBrowseForFolder Lib "shell32" ( _
  lpbi As BrowseInfo) As Long

Diese API-Funktion erzeugt einen Zeiger auf eine Datenstruktur mit dem ausgewählten Pfad. Details hierzu folgen später.

Die Variable "ulFlags"

Zum Datentyp BrowseInfo gehört die Variable ulFlags. Ihr Wert beeinflusst das Verhalten des Verzeichnisdialogs. Je nach Wert zeigt der Dialog bestimmte objekte an und erlaubt nur die Auswahl bestimmter Objekte. Folgende Werte sind möglich:

&H1000:Als Auswahl sind nur Computer erlaubt. Wenn der Anwender andere Objekte, also Ordner oder Laufwerke markiert, kann der OK-Button nicht ausgewählt werden.
&H2000:Gestattet nur Drucker als Auswahl.
&H4000:Der Dialog zeigt neben Computern, Laufwerken und Ordnern auch Dateien an.
&H1:Gestattet nur Dateisystemordner als Auswahl.
&H2:Der Dialog zeigt keine Netzwerkordner unterhalb der aktuellen Domain.
&H4:Der Dialog enthält eine Statuszeile. Die Rückruffunktion kann die Statuszeile ausfüllen.
&H8:Gestattet nur Dateisystemobjekte als Auswahl.

Am besten deklarieren Sie diese Werte ebenfalls als Konstanten im entsprechenden Modul:

Private Const BIF_BROWSEFORCOMPUTER = &H1000
Private Const BIF_BROWSEFORPRINTER = &H2000
Private Const BIF_BROWSEINCLUDEFILES = &H4000
Private Const BIF_RETURNONLYFSDIRS = &H1
Private Const BIF_DONTGOBELOWDOMAIN = &H2
Private Const BIF_STATUSTEXT = &H4
Private Const BIF_RETURNFSANCESTORS = &H8

Die "Item Identifier List"

Die Ordnerstruktur der Windows-Oberfläche umfasst nicht nur Laufwerke und Verzeichnisse, sondern auch sogenannte Systemordner wie zum Beispiel die Systemsteuerung oder den Druckerordner. Außerdem gehören auch Netzwerk-Ressourcen wie Netzdrucker oder freigegebene Verzeichnisse auf anderen Rechnern dazu. Diese vielen Ordnertypen zeigt der Ordnerdialog als Baumansicht mit dem Desktop als Wurzel an.

Die gesamte Struktur aus Systemordnern, Datenträgerordnern und Netzwerk-Ressourcen wird als Namensraum bezeichnet. Ähnlich wie bei einer Ordnerstruktur in einem Dateisystem gibt es auch für den gesamten Namensraum von Windows einen Namensstandard, der Systemordner und Dateisysteme umfasst. Er ähnelt der Pfadangaben einer Datei auf der lokalen Festplatte, Diskette oder einem sonstigen Datenträger. Der Namensraum enthält zu einem bestimmten Element – meist ein beliebiger Ordner – alle übergeordneten Elemente bis zurück zur Wurzel: dem Desktop: Dieser Darstellungsstandard heißt "Item Identifier List" ("Elementidentifikationsliste") oder kurz IID-Liste. Das Beispiel des folgenden Pfads soll das verdeutlichen:

C:\Eigene Dateien\Konzepte\Buch.doc

Die dazugehörige IID-Liste besitzt eine andere Wurzel als den Laufwerksbuchstaben C, da auch alle weiteren Laufwerke von einer gemeinsamen Wurzel abzeigen – nämlich vom Arbeitsplatzsymbol. Die IID-Liste zu diesem Beispiel würde folgende Elemente enthalten:

[Arbeitsplatz][C:][Eigene Dateien][Konzepte][Buch.doc]

Diese Darstellung soll lediglich die Abfolge von Elementen dieser Datenstruktur verdeutlichen. Tatsächlich ist eine IID-Liste eine binäre Datenstruktur, die intern von Windows benutzt wird.

Die Funktion SHBrowserForFolder gibt einen Zeiger auf eine solche IID-Liste zurück. Sie können mit Hilfe der API-Funktion SHGetPathFromIDList zu jeder IID-Liste die Pfadangabe des Dateisystems ermitteln. Setzen Sie also auch folgende Deklaration in das Modul ein:

Declare Function SHGetPathFromIDList Lib "shell32" ( _
  ByVal pidList As Long, _
  ByVal lpBuffer As String) As Long

Gehört die IID-Liste nicht zu einem Dateisystem, erzeugt diese Funktion eine leere Zeichenkette. Das heißt: Sie können damit nur lokale und Netzwerkpfade behandeln. Auf Systemordner können Sie nicht zugreifen, da der Darstellungsstandard für Pfadangaben keine Elemente außerhalb eines Dateisystem kennt.

Mit der Funktion SHBrowseForFolder können Sie auch die Pfade zu Dateien und Ordnern im Netzwerk ermitteln. In diesem Fall erhalten Sie nach Umwandlung der IID-Liste mit SHGetPathFromIDList einen vollständigen Pfad nach der "Universal Naming Convention" (UNC). Auch dazu ein Beispiel: Sie haben im Dialog in der Netzwerkumgebung von dem Rechner "Computer5" das Verzeichnis "C:Eigene Dateien" markiert. Der entsprechende UNC-Pfad lautet dann:

\\Computer5\C\Eigene Dateien

Den Aufruf des Auswahldialogs in eine Funktion packen

Damit Sie den Aufruf des Ordnerdialogs in Ihrem gesamten VB-Projekt nutzen können und nicht jedes Mal neu schreiben müssen, verpacken Sie in am besten in eine eigene Funktion. Im vorliegenden Beispiel heißt sie "BrowseForFolders". Diese Mantelfunktion benötigt lediglich ein Argument: Die Beschriftung des Dialogfeldes, die Variable Prompt:

Function BrowseForFolder(Prompt As String) As StringEnd Function

Der Aufruf des Ordnerdialogs geschieht in drei Schritten:

  • Initialisieren des Datenstruktur FolderBrowse. Hier ist vor allem die Beschriftung wichtig.
  • Aufrufen der Funktion SHBrowseForFolder mit der initialisierten Datenstruktur.
  • Umwandeln der übergebenen IID-Liste in eine Pfadangabe des Dateisystems, um den vollständigen Pfad mit Dateinamen zu erhalten.
  • Rückgabe des ermittelten Pfads.
Doch zuvor müssen die benötigten Variablen definiert werden:

    Dim n As Integer
    Dim IDList As Long
    Dim Result As Long
    Dim ThePath As String
    Dim BI As BrowseInfo

Die Datenstruktur BrowseInfo wird als Variable BI deklariert. Sie wird dann sogleich mit den benötigten Werten gefüllt. Auf die einzelnen Bestandteile der Datenstruktur greifen Sie mit dem Punkt-Operator zu. Der Variablen hWndOwner weisen Sie beispielsweise den Wert des aktiven Fensters zu:

BI.hWndOwner = GetActiveWindow()

Entsprechend werden auch die anderen BI-Variablen mit Werten belegt. Wenn Sie nicht jedes Mal BI vor die Variable setzen wollen, nutzen Sie einfach die With-Konstruktion. Belegen Sie nun noch die Beschriftung des Dialogs und seine Flags. Im Beispiel wird als ulFlags der Wert BIF_RETURNONLYFSDIRS genutzt, so dass der Ordnerdialog nur die Auswahl von Dateisystemobjekten erlaubt:

    ' Erzeugen der Datenstruktur
    With BI
        ' Handle des aktiven Fensters ermitteln
        .hWndOwner = GetActiveWindow()
        ' Titel des Dialoges als nullterminierter String
        .lpszTitle = lstrcat(Prompt, "")
        ' Nur Dateisystemordner erlaubt
        .ulFlags = BIF_RETURNONLYFSDIRS
    End With

Sie bemerken sicher die Funktion lstrcat. Sie konvertiert einen Visual-Basic-String in einen sogenannten nullterminierten String – denn Windows selbst versteht nur Strings, deren Ende mit einem Null-Zeichen beendet werden. lstrcat erledigt das für Sie. Andernfalls gäbe es einen Laufzeitfehler im Programm. Auch diese Funktion ist eine API-Funktion, die Sie zuvor im Modul deklarieren müssen:

Private Declare Function lstrcat Lib "kernel32" _
  Alias "lstrcatA" ( _
  ByVal lpString1 As String, _
  ByVal lpString2 As String) As Long

Nachdem Sie BI mit Werten initialisiert haben, wird der Ordnerdialog mit SHBrowseForFolder aufgerufen:

    'Anzeigen des Dialogs und Übergabe an eine IID-Liste
    IDList = SHBrowseForFolder(BI)

Den Rückgabewert von SHBrowseForFolder auswerten

Der Rückgabewert der API-Funktion SHBrwoserForFolder ist vom Typ Long. Er enthält einen Zeiger auf eine IID-Liste. Dafür ist am Beginn Ihrer BrowseForFolder-Funktion die Variable IDList entsprechend deklariert. Tritt beim Aufruf von SHBrowseForFolder(BI) ein Fehler auf oder klickt der Anwender im Ordnerdialog auf "Abbrechen", so hat IDList den Wert 0. Diesen können Sie in einer einfachen If-Abfrage auswerten. Der Rückgabewert wird nur bearbeitet, wenn IDList nicht 0 ist:

    ' Wenn IDList > 0, dann Auswahl bearbeiten
    If IDList Then

Um die IID-Liste in eine Pfadangabe zu konvertieren, benötigen Sie als erstes eine Zeichenfolge, die Sie mit der VB-Funktion String$() Speicher zuweisen: ThePath. Anschließend genügt ein Aufruf der Funktion SHGetPathFromIDList, um den Pfad zu ermitteln:

        ' Speicher anfordern
        ThePath = String$(MAX_PATH, 0)
        ' IID-Liste in Pfadangabe konvertieren
        Result = SHGetPathFromIDList(IDList, ThePath)

Der Wert MAX_PATH ist wiederum eine zuvor im Modul definierte Konstante. Sie enthält die maximale Länge der Zeichenfolge, nämlich 260 Zeichen:

Private Const MAX_PATH = 260

Der nächste Schritt ist sehr wichtig. Windows hat für die IID-Liste Speicher reserviert, den Sie mit einem Aufruf von CoTaskMemFree wieder freigeben müssen. Wenn Sie dies nicht tun, kann Windows den angeforderten Speicher nicht wieder nutzen. Deshalb nun folgende Programmzeile:

        ' Speicher für IID-Liste verwerfen
        Call CoTaskMemFree(IDList)

Zum Schluss müssen Sie die Zeichenfolge noch nachbearbeiten. Windows behandelt sie als Puffer. Das heißt: Die Pfadangabe wird beginnend mit dem ersten Byte in den String kopiert. Der "Rest" bleibt erhalten und besteht aus mehr oder weniger vielen Null-Zeichen. Diese müssen von dem String entfernt werden, damit er innerhalb von Visual Basic korrekt weiterverwendet werden kann:

        ' Alle Bytes hinter Nullbyte verwerfen
        n = InStr(ThePath, vbNullChar)
        If n Then ThePath = Left$(ThePath, n - 1)
    End If

Der Wert vbNullChar ist dabei eine in VB eingebaute Konstante. Sie enthält einfach ein Zeichen mit dem Wert 0.

Zum Schluss gibt die Funktion den ermittelten Pfad als Ergebnis zurück:

    ' Rückgabewert der Funktion definieren
    BrowseForFolder = ThePath
End Function

Die BrowseForFolder-Funktion in eigene Projekte einbauen

Sie sollten den Quelltext des Listings in einem eigenen Modul in Ihr VB- oder VBA-Projekt einfügen. Er ist so geschrieben, dass alle internen Konstanten und Deklarationen als "Private" definiert sind. Dadurch werden Konflikte mit anderen Modulen vermieden. Die Funktion selbst ist als "Public" gekennzeichnet und wird beispielsweise wie folgt aufgerufen:

TheFolder$ = BrowseForFolder("Wählen Sie einen Ordner aus.")

Das vollständige Listing finden Sie im folgenden auf dieser Seite.

' ###########################################
'
'                     MODUL "FolderBrowse"
'
'                    (c) Ingo Steinhaus 2000
'                    ingo.steinhaus@gmx.de
'
'  Funktionen zur Anzeige des Windows-Standardialogs zur Auswahl
'  eines Ordners auf dem aktiven Rechner oder im Netzwerk.
'
'  Dieses Modul ist urheberrechtlich geschützte Freeware.
'  Die originale Copyright-Meldung darf nicht entfernt oder ver-
'  ändert werden. Der Quelltext darf nicht verändert werden.
'
'###########################################
 
Private Const MAX_PATH = 260
 
' *********************************************************
' Die Datenstruktur "BrowseInfo" dient der Konfiguration
' des Folder-Browse-Dialogs.
 
Private Type BrowseInfo
  hWndOwner As Long
  ' Handle des Besitzers (mit GetActiveWindow() abfragen)
  pIDLRoot As Long
  ' Adresse der IID-Liste.
  'Sie gibt die Position des Wurzelordners an, der als
  'Baumwurzel im Browse-Dialog erscheint. Nur dieser
  'Ordner und die davon abzweigenden Ordner erscheinen
  'im Browse-Dialog.
  'Sie können hier NULL eintragen; in diesem Fall wird
  'der Desktop als Baumwurzel benutzt. Dadurch haben Sie
  'Zugriff auf alle Laufwerke sowie die Netzwerkumgebung.
  pszDisplayName As Long
  ' Adresse eines Puffers, der den Namen des vom Anwender
  'ausgewählten Ordners (ohne Pfad) enthält. Der Puffer
  'kann maximal 260 Zeichen enthalten (MAX_PATH Konstante)
  lpszTitle As Long
  ' Adresse eines nullterminierten Strings, der über der
  'Baumansicht gezeigt werden. Sie können diesen String
  'für Informationen oder Anweisungen benutzen.
  ulFlags As Long
  ' Flags, die die Anzeigeoptionen des Dialogfeldes bestimmen
  lpfnCallback As Long
  ' Adresse einer Rückruffunktion, die in der Anwendung
  'definiert wird.
  'Sie können hier NULL eintragen.
  lParam As Long
  ' Ein anwendungsdefinierter Wert, den das Dialogfeld an
  'eine Rückruffunktion übergibt.
  iImage As Long
  ' Eine Variable für das Bild, mit dem der ausgewählte Ordner
  'in der Baumansicht gekennzeichnet ist. Die Variable enthält
  'einen Index auf die Systembilderliste von Windows 95/98.
End Type
 
' *********************************************************
' Die folgenden Konstanten sind die erlaubten Werte für
' BrowseInfo->ulFlags.
 
Private Const BIF_BROWSEFORCOMPUTER = &H1000
' Nur Computer als Auswahl erlaubt. Wenn der Anwender andere
'Ordner markiert, kann der OK-Schalter nicht ausgewählt
'werden.
 
Private Const BIF_BROWSEFORPRINTER = &H2000
' Nur Drucker als Auswahl erlaubt. Wenn der Anwender andere
'Ordner markiert, kann der OK-Schalter nicht ausgewählt
'werden.
 
Private Const BIF_BROWSEINCLUDEFILES = &H4000
' Der Dialog zeigt neben den Ordnern auch Dateien.
 
Private Const BIF_DONTGOBELOWDOMAIN = &H2
' Der Dialog zeigt keine Netzwerkordner unterhalb der
'aktuellen Domain.
 
Private Const BIF_RETURNFSANCESTORS = &H8
' Nur Dateisystemobjekte als Auswahl erlaubt. Wenn der
'Anwender andere Ordner markiert, kann der OK-Schalter
'nicht ausgewählt werden.
 
Private Const BIF_RETURNONLYFSDIRS = &H1
' Nur Dateisystemordner als Auswahl erlaubt. Wenn der 
'Anwender andere Ordner markiert, kann der OK-Schalter
'nicht ausgewählt werden.
 
Private Const BIF_STATUSTEXT = &H4
' Der Dialog enthält eine Statuszeile. Die Rückruffunktion
'kann die Statuszeile ausfüllen.
 
' *********************************************************
'*** CoTaskMemFree
' Eine Funktion zum Verwerfen von angefordertem globalen
' Speicher.
 
Private Declare Sub CoTaskMemFree Lib "ole32.dll" ( _
  ByVal hMem As Long)
 
' *********************************************************
'*** lstrcat
' Eine Funktion zum Verknüpfen von nullterminierten Strings.
 
Private Declare Function lstrcat Lib "kernel32" _
  Alias "lstrcatA" ( _
  ByVal lpString1 As String, _
  ByVal lpString2 As String) As Long
 
' *********************************************************
' *** GetActiveWindow
' Eine Funktion zum Ermitteln des Fenster-Handles.
 
Private Declare Function GetActiveWindow Lib "user32" () As Long
 
' *********************************************************
' *** SHGetPathFromIDList
' Diese Funktion konvertiert eine IID-Liste in einen Pfad des
' Dateisystems.
 
Private Declare Function SHGetPathFromIDList Lib "shell32" ( _
  ByVal pidList As Long, _
  ByVal lpBuffer As String) As Long
 
' ********************************************************
Rem *** SHBrowseForFolder
Rem Diese Funktion ruft den Folder-Browse-Dialog auf.
Rem Der Aufrufer muß den Speicher der IID-Liste verwerfen.
 
Private Declare Function SHBrowseForFolder Lib "shell32" ( _
  lpbi As BrowseInfo) As Long
 
' *********************************************************
' *** BrowseForFolder
' Eine VB/VBA-Funktion als einfach zu nutzender Mantel für den
' Aufruf des Folder-Browse-Dialogs.
 
Public Function BrowseForFolder(Prompt As String) As String
 
  Dim n As Integer
  Dim IDList As Long
  Dim Result As Long
  Dim ThePath As String
  Dim BI As BrowseInfo
 
  ' Erzeugen der Datenstruktur
  With BI
    ' Handle des aktiven Fensters ermitteln
    .hWndOwner = GetActiveWindow()
    ' Titel des Dialoges
    .lpszTitle = lstrcat(Prompt, "")
    ' Nur Dateisystemordner erlaubt
    .ulFlags = BIF_RETURNONLYFSDIRS
  End With
 
  ' Anzeigen des Dialogs und Übergabe an eine IID-Liste
  IDList = SHBrowseForFolder(BI)
 
  ' Wenn IDList > 0, dann Auswahl bearbeiten
  If IDList Then
    ' Speicher anfordern
    ThePath = String$(MAX_PATH, 0)
    ' IID-Liste in Pfadangabe konvertieren
    Result = SHGetPathFromIDList(IDList, ThePath)
    ' Speicher für IID-Liste verwerfen
    Call CoTaskMemFree(IDList)
    ' Alle Bytes hinter Nullbyte verwerfen
    n = InStr(ThePath, vbNullChar)
    If n Then ThePath = Left$(ThePath, n - 1)
  End If
 
  ' Rückgabewert der Funktion definieren
  BrowseForFolder = ThePath
End Function

Dieser Workshop wurde bereits 24.380 mal aufgerufen.

Über diesen Workshop im Forum diskutieren
Haben Sie Fragen oder Anregungen zu diesem Workshop, können Sie gerne mit anderen darüber in unserem Forum diskutieren.

Aktuelle Diskussion anzeigen (5 Beiträge)

nach obenzurück


Anzeige

Kauftipp Unser Dauerbrenner!Diesen und auch alle anderen Workshops finden Sie auch auf unserer aktuellen vb@rchiv  Vol.6
(einschl. Beispielprojekt!)

Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv!
- nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten
- Symbol-Galerie mit mehr als 3.200 Icons im modernen Look
Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m.
 
   

Druckansicht Druckansicht Copyright ©2000-2017 vb@rchiv Dieter Otter
Alle Rechte vorbehalten.
Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein.

Diese Seiten wurden optimiert für eine Bildschirmauflösung von mind. 1280x1024 Pixel