Die Community zu .NET und Classic VB.
Menü

Verwenden von Dialogressourcen

 von 

Einleitung 

In Visual Basic kann man sehr einfach visuell Formulare, d.h. Fenster, die Steuerelemente enthalten, zusammenstellen. Auch wenn damit die Entwicklungszeit verkürzt wird und praktisch jeder Anfänger aussagekräftige Formulare gestalten kann, sollte man aber auch wissen, dass Windows es erlaubt, Formulare in Ressourcendateien abzulegen. Das Windows-API stellt nämlich die Möglichkeit bereit, in Ressourcendateien definierte Formulare während der Laufzeit zu laden und in der Anwendung zu benutzen. Dieser Artikel zeigt, wie man Formulare in Ressourcendateien definiert und sie aus Visual Basic-Anwendungen laden, manipulieren und anzeigen kann.

Hinweis:
Die Erstveröffentlichung dieses Tutorials finden Sie unter http://dotnet.mvps.org/vb/articles/dialogresources/

Grundlagen  

Was sind Dialoge?

Unter einem Dialog versteht man im Allgemeinen ein Fenster, über das der Benutzer mit der Anwendung „kommuniziert“. Heutzutage enthält fast jede Anwendung mehrere Dialoge. Einfachstes Beispiel ist ein Anmeldungsdialog oder der legendäre Versionsinformationsdialog, der bei fast keiner Anwendung fehlt. Allerdings sind nicht alle Fenster unter Windows Dialoge im Sinne von Dialogressourcen. Ein Grossteil der Anwendungen besteht aus einem Hauptfenster, bei dem es sich nicht um eine Dialogressource handelt. Dieses Hauptfenster enthält meist eine Werkzeugleiste, eine Menüleiste und eine Statusleiste. Dabei ist zu bemerken, dass auch Menüs und Werkzeugleisten aus Ressourcendateien geladen werden können.

Motivation für die Verwendung von Dialogressourcen

Wie bereits angesprochen, werden Hauptfenster nicht aus Ressourcendateien geladen. Natürlich kann man bei allen Dialogen einer Anwendung so verfahren, jedoch muss man dann dem Code die entsprechenden Anweisungen zum Erstellen des Fensters, der darauf enthaltenen Steuerelemente und der notwendigen Ereignisverarbeitungsroutinen hinzufügen, was den Codeumfang vergrössern würde. Dies war einer der Gründe, warum der Ressourcentyp DIALOG eingeführt wurde; weitere Ursachen sind die Folgenden:

  • Erleichtertes GUI-Design durch Drag and Drop-Editoren, die das Erstellen und Testen von Dialogen vereinfachen. Dies ist besonders wichtig, da das reine Erstellen durch Code mittels RegisterClass, CreateWindowEx usw. viel zu umständlich ist. Man bedenke dabei nur, dass nach jeder Änderung des Codes das neuerliche Kompilieren der gesamten Anwendung notwendig ist, was besonders bei grösseren Projekten ziemlich zeitaufwendig ist. Ausserdem ist dadurch der Code schwer verständlich und erfordert eine sehr detaillierte Kommentierung. Denn wie soll man schnell erkennen, welcher Code dann exekutiert welches Fenster erstellt?
  • Abstrahierung der Projektstruktur auf Seite der Entwicklung. Gliederung des Codes in Module, Klassen und Ressourcen.
  • Durch den vorigen Punkt implizierte Förderung der Programmierung durch Teams mit Aufgabentrennung. So kann beispielsweise ein eigener Bildschirmdesigner mit dem Erstellen der Dialoge beauftragt werden, ohne dabei den anderen Programmierern im Weg zu sein.
  • Leichter durchschaubare Speicherverwaltung. Es ist leichter nachvollziehbar, wie lange welches Fenster geladen ist, da nicht mehr so viel Code zum Erstellen und Entladen von Fenstern und Steuerelementen notwendig ist.

Dialogressourcen versus Visual Basic-Dialogfelder  

Das Formularsystem von Visual Basic

Natürlich werden Sie sich die Frage stellen, warum man eigentlich Dialoge aus Ressourcen verwenden soll, bietet doch Visual Basic bereits eine so einfache Möglichkeit, Dialoge zu erstellen und zu verwalten. Sieht man sich aber Formulare und Steuerelemente unter Visual Basic genauer an, so erkennt man schnell die etwas starre Struktur und die mangelnde Flexibilität. Bestes Beispiel hierfür sind die schreibgeschützten und fehlenden Eigenschaften. Wer unter Visual Basic eine wirklich professionelle Anwendungsoberfläche erstellen will, findet sowieso keinen Umweg um die Verwendung von Plattformfunktionen; warum sollte man nicht gleich auch die Dialoge per API-Aufrufen erstellen und organisieren?

Bei näherer Betrachtung des Visual Basic-eigenen Steuerelementsystems wird man feststellen, dass die Steuerelemente keine „echten“ Windows-Steuerelemente sind. Unter Windows gibt es mehrere Standardsteuerelemente, die jeweils Instanzen einer bestimmten Fensterklasse sind. Der Klassennname einer Schaltfläche beispielsweise ist BUTTON, der eines TextBox-Steuerelements wäre EDIT. Wenn man nun die Klassennamen der Steuerelemente in einer mit Visual Basic erstellten Anwendung ansieht (beispielsweise mit dem Werkzeug Spy++), wird man feststellen, dass es sich hier um andere Klassennamen handelt, die nur die Windows-Steuerelementtypen kapseln.

Probleme und Einschränkungen des Visual Basic-Formularsystems

Normalerweise hat man damit auch kein Problem, spätestens seit Windows 2000 gibt einem diese Architektur aber zu denken: Windows 2000 bietet nämlich einen eigenen Darstellungsmodus an, in dem die Zugriffstasten nicht unterstrichen werden und auch keine Fokusrechtecke angezeigt werden. Dies verleiht Anwendungen ein moderneres und aufgelockerteres Design, wenngleich auch die Benutzerfreundlichkeit etwas darunter leidet. Während bei normalen Anwendungen Fenster in diesem Modus gestartet werden, werden bei Visual Basic-Anwendungen immer die Zugriffstasten unterstrichen und Fokusrechtecke angezeigt; dies kann allerdings über Windows-API-Funktionen ausgeschaltet werden.

Ein weiterer Vorteil ist, dass Steuerelemente wie ListView oder StatusBar ohne Verwendung der Microsoft Windows Common Controls-Komponente eingebunden werden können. Leider können diese Steuerelemente anschliessend nur über Plattformfunktionen modifiziert werden, was aber auch als Vorteil gesehen werden kann. Wenn man schon auf API-Funktionen zurückgreifen muss, um verschiedene Merkmale und Verhaltensweisen zu erzielen, dann kann man die Steuerelemente auch gleich per API-Aufrufen erstellen und in weiterer Folge auch verwalten. Das selbe gilt auch für Formulare.

Weiters sind Dialogressourcen vorteilhaft, da bestimmte Aussehenseigenschaften wie z.?B. seitenverkehrte Fenster komfortabel eingestellt werden können. Das Formularsystem von Visual Basic stellt zwar die wichtigsten Eigenschaften bei der Formularklasse zur Verfügung, jedoch ist nur eine Teilmenge der Möglichkeiten direkt verfügbar.

Nachteil der Verwendung von Dialogressourcen unter Visual Basic ist, dass die Tabulatorreihenfolge der Steuerelemente in den daraus geladenen Dialogen nicht korrekt funktioniert. Ausserdem ist zu beachten, dass die Verwendung von Dialogressourcen unter Visual Basic nicht vorgesehen ist und daher auch von der Entwicklungsumgebung nicht vollständig unterstützt wird. Damit eine Dialogressource geladen werden kann, muss die Anwendung als kompilierte Anwendung vorliegen, beim Ausführen aus der Entwicklungsumgebung heraus funktioniert das Laden der Ressourcen nicht, da App.hInstance auf die Instanz der Entwicklungsumgebung, nicht aber auf die Anwendung selbst verweist.

Der Ressourcentyp DIALOG  

Hier wird auf eine genauere Beschreibung des Aufbaus von DIALOG-Ressourcen verzichtet, da diese nur selten als Code bearbeitet werden müssen. Für nähere Informationen sei auf die Dokumentation verweisen. Zum Erstellen kann beispielsweise Microsoft Visual C++ verwendet werden. Dieses Programm bietet die Möglichkeit, in einem WYSIWYG-Editor mittels Drag and Drop Dialogfelder zu erstellen. Dabei können Steuerelemente einfach mit der Maus, wie dies von Visual Basic her bekannt ist, positioniert und formatiert werden. Anschliessend muss dann das Ressourcenscript noch in eine Ressourcendatei kompiliert werden, die man dem Visual Basic-Projekt hinzufügt.

Relevante Windows-API-Funktionen  

Hier soll aus Gründen der leichteren Nachvollziehbarkeit nur ein sehr einfaches Beispiel zur Verwendung von Dialogressourcen präsentiert werden. Selbstverständlich stehen mit Dialogressourcen noch weitere, fortgeschrittenere Möglichkeiten bereit.

Die Funktion CreateDialogParam

Die Funktion CreateDialogParam wird wie folgt deklariert:

Private Declare Function CreateDialogParam _
    Lib "user32.dll" _
    Alias "CreateDialogParamA" _
( _
    ByVal hInstance As Long, _
    ByVal lpName As String, _
    ByVal hWndParent As Long, _
    ByVal lpDialogFunc As Long, _
    ByVal lParamInit As Long _
) As Long

Listing 1: Deklaration der Funktion CreateDialogParam.

Im Parameter hInstance wird die Zugriffsnummer der Instanz der Anwendung angegeben, also App.hInstance, lpName ist der Name der Dialogressource in der Ressourcendatei in Form einer Zeichenfolge, hWndParent gibt das Fenster an, das als Elternfenster verwendet werden soll, lpDialogFunc ist ein Zeiger auf die Rückrufprozedur für die Dialogmeldungen und der Parameter lParamInit ist nicht relevant, wir übergeben im Beispiel den Wert 0&. Als Rückgabewert wird die Zugriffsnummer des Dialogs geliefert, tritt ein Fehler auf, dann ist der Rückgabewert 0.

Die Funktion EndDialog

EndDialog wird verwendet, um den Dialog wieder zu entladen.

Die Deklaration sieht folgendermassen aus:

Private Declare Function EndDialog Lib "user32.dll" ( _
    ByVal hDlg As Long, _
    ByVal nResult As Long _
) As Long

Listing 2: Deklaration der Funktion EndDialog.

Der Parameter nResult ist hier nicht relevant, hDlg ist die Zugriffsnummer des Dialogs (der Rückgabewert von CreateDialogParam).

Die Rückrufprozedur DialogProc

Wie bereits unter CreateDialogParam angeführt muss unter lpDialogFunc ein Zeiger auf eine benutzerdefinierte Rückruffunktion übergeben werden. Dies ist seit VB5 über den Operator AddressOf möglich. Laut Windows Platform SDK-Dokumentation hat diese Rückruffunktion folgendes Aussehen:

bool CALLBACK DialogProc(
  HWND hwndDlg,  // handle to dialog box
  UINT uMsg,     // message
  WPARAM wParam, // first message parameter
  LPARAM lParam  // second message parameter
);

Listing 3: Prototyp der Rückrufprozedur DialogProc.

Übersetzt man dies in Visual Basic-Syntax, sieht der Kopf der Funktion wie folgt aus:

Public Function DialogProc( _
    ByVal hWnd As Long, _
    ByVal uMsg As Long, _
    ByVal wParam As Long, _
    ByVal lParam As Long _
) As Long

Listing 4: Der Kopf der Funktion DialogProc in Visual Basic.

Es ist zu beachten, dass alle Parameter als Wertparameter deklariert werden. Wenn das Schlüsselwort ByVal weggelassen wird, stürzt die Anwendung ab. Visual Basic-Programmierer, die bereits Fenster gesubclasst haben, werden bemerken, dass die Funktionsdeklaration gleich wie die für eine Fensterprozedur aussieht.

Ein einfaches Beispiel  

Dieses Kapitel zeigt in Ansätzen, wie man auf bestimmte Ereignisse innerhalb des Dialogs reagieren kann. Dazu wird hier demonstriert, wie beim Klick auf die Schaltfläche eines Meldungsfeldes mit dem Inhalt eines TextBox-Steuerelements auf dem Dialog angezeigt werden kann. Der Code stellt eine sehr stark vereinfachte Version des Codes aus dem Beispielprojekt dar.

Im Folgenden gehen wir davon aus, dass unsere Dialogressource folgende Steuerelemente enthält:

Steuerelemente und ihre Typen

ID Steuerelementtyp
IDC_SHOWMSG CommandButton
IDC_MSGTEXT TextBox

Abbildung 1: Übersicht über die im Beispiel verwendeten Steuerelement-IDs.

Weiters enthalte die Variable g_hWndDialog die Zugriffsnummer des Dialogs.

Die gesamte Ereignisbehandlung spielt sich innerhalb der Funktionsprozedur DialogProc ab. Natürlich wäre es auch möglich, Pseudoereignisse für verschiedene Ereignisse und Steuerelemente am Dialog zu definieren. Der folgende Code bewirkt das Anzeigen eines Meldungsfensters beim Klick auf IDC_SHOWMSG, wobei als Meldungstext der Inhalt des TextBox-Steuerelements IDC_MSGTEXT verwendet wird:

Public Function DialogProc( _
    ByVal hWnd As Long, _
    ByVal uMsg As Long, _
    ByVal wParam As Long, _
    ByVal lParam As Long _
) As Long
    
    ' Es wurde ein Klick auf 'IDC_SHOWMSG' ausgeführt.
    If wParam = IDC_SHOWMSG And uMsg = WM_COMMAND Then
        Dim s As String, n As Long
        
        ' Ermitteln des TextBox-Inhalts mit den entsprechenden Dialog-APIs.
        s = Space$(256)
        n = GetDlgItemText(g_hWndDialog, IDC_MSGTEXT, s, Len(s))
        s = Left$(s, n)
        
        ' Ausgabe des Textes.
        Call MsgBox(s)
    End If
End Function

Listing 5: Implementierung des laufenden Beispiels.

Schlusswort  

Auch wenn die vorgestellte Methode ihre Einschränkungen hat und in der Praxis wohl kaum einsetzbar ist, sollte man als Programmierer darüber bescheid wissen. Es ist nämlich auch möglich, Dialogressourcen aus anderen Anwendungsdateien und Bibliotheken zu laden und mit eigenen Ereignisbehandlungsroutinen auszustatten. Ein Beispiel dazu ist im nachfolgenden Abschnitt „Downloads“ zu finden.

Ihre Meinung  

Falls Sie Fragen zu diesem Tutorial haben oder Ihre Erfahrung mit anderen Nutzern austauschen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.