Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » FAQ C / C++ (WinAPI, Konsole) » Menüs, Icons

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 <
000
10.01.2003, 16:18 Uhr
void*
Generic Pointer
(Operator)


Erstellt von Uwe

In diesem Teil der Einführung in die Programmierung mit dem API beschäftigen wir uns heute mit der Erzeugung von Menüs und deren Resource-Files. Wir werden zum ersten mal eine Headerdatei in unser Anwendungsgerüst einfügen, worin wir unsere Resourcen definieren und durch die Einbindung unserem Hauptprogramm mitteilen das wir die Resourcen in unserem Programm nutzen wollen.
Wir benötigen 3 Dateien:
* menues.cpp


Ist unsere Hauptdatei, welche das Fenster erzeugt und auf den Bildschirm bringt. In der weiteren Implementation wertet diese weiterhin die Befehle, die durch Anklicken eines Menüpunktes ausgelöst werden, aus.

* resource.h


Unsere Headerdatei, die sowohl in die menues.cpp

als auch in die resource.rc

eingebunden wird. Sie enthält #define-Anweisungen für unsere Menüs.

* resource.rc

Unsere eigentliche Resourcedatei, in welcher das Menü erzeugt wird.
--
Gruß
void*
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
10.01.2003, 16:18 Uhr
void*
Generic Pointer
(Operator)


Es folgen erst einmal die Listings für die einzelnen Dateien:

C++:
// menue.cpp
#include <windows.h>

// Den Header für die Resourcen einbinden

#include "resource.h"


LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain (HINSTANCE hInstance,
                        HINSTANCE hPrevInstance,
                        PSTR szCmdLine,
                        int iCmdShow)
{
   MSG msg;

   WNDCLASSEX wc={0};

   wc.cbSize= sizeof (WNDCLASSEX);
   static char szAppName[]= "Menü Erstellung";
   wc.style = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc = WndProc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 0;
   wc.hInstance = hInstance;
   wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
   wc.hCursor = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);

   // Handle für unser Menü setzten

   wc.lpszMenuName = "USERMENUE";
   wc.lpszClassName = szAppName;
   wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

   RegisterClassEx(&wc);

   HWND hWnd = CreateWindowEx(WS_EX_CLIENTEDGE,
                              szAppName,
                              "Menü Erstellung",
                              WS_OVERLAPPEDWINDOW,
                              CW_USEDEFAULT,
                              CW_USEDEFAULT,
                              CW_USEDEFAULT,
                              CW_USEDEFAULT,
                              NULL,
                              NULL,
                              hInstance,
                              NULL);

    ShowWindow(hWnd, iCmdShow);
    UpdateWindow(hWnd);

    while (GetMessage(&msg, NULL, 0, 0)== TRUE)
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }

    return msg.wParam;
}

LRESULT CALLBACK WndProc (HWND hwnd,
                          UINT message,
                          WPARAM wParam,
                          LPARAM lParam)

{
        HDC hdc;
        PAINTSTRUCT paintstruct;
        switch(message)
         {
          case WM_PAINT:
                  hdc = BeginPaint (hwnd, &paintstruct);

                  // Einen Text im Anwendungsfenster ausgeben

                  TextOut (hdc, 250, 200, "Eigene Menüs erzeugen.", 22);

                  EndPaint (hwnd, &paintstruct);
                  return 0;

          // Jetzt die Behandlungsroutine für WM_COMMAND

          case WM_COMMAND:
               switch(LOWORD(wParam))
               {
                case ID_NEU:

                                 /* Auf den Eintrag "Datei/Neu" geklickt ?
                                    Dann eine Nachrichtenbox ausgeben.
                                    Dient als Vorbereitung für spätere
                                    Lektionen, wo die Buttons zum leben erweckt
                                    werden */


                                 MessageBox(NULL, "Button 'Neu' geklickt", "Menütest", MB_OK);
                     return 0;
                case ID_OEFFNEN:
                                 MessageBox(NULL, "Button 'Öffnen' geklickt", "Menütest", MB_OK);
                     return 0;
                case ID_SPEICHERN:
                     MessageBox(NULL, "Button 'Speichern' geklickt", "Menütest", MB_OK);
                     return 0;
                case ID_BEENDEN:
                     MessageBox(NULL,
                                "Button 'Beenden' geklickt - Programm wird beendet",
                                "Menütest",MB_OK);

                     /* Nachdem bei der Nachrichtenbox auf "OK" geklickt wurde
                     - Raus aus unserer Anwendung */


                     SendMessage(hwnd, WM_CLOSE, 0, 0);
                     return 0;

                     /* Es folgen ein paar Spielchen mit SetWindowText() */

                case ID_ITEM1:
                     SetWindowText(hwnd, "Diesen Text in die Titelleiste");
                     return 0;
                case ID_ITEM2:
                     SetWindowText(hwnd, "Und jetzt diesen Text");
                     return 0;
                case ID_ITEM3:
                     SetWindowText(hwnd, "");
                     return 0;
               }
               return 0;

               case WM_DESTROY:
                     PostQuitMessage(0);
               return 0;
         }
         return DefWindowProc (hwnd, message, wParam, lParam);
}


--
Gruß
void*
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
10.01.2003, 16:19 Uhr
void*
Generic Pointer
(Operator)


Wenn wir alles übernommen haben, geht es daran die Headerdatei zu schreiben. // resource.h

C++:
        #define ID_NEU             300
        #define ID_OEFFNEN         301
        #define ID_SPEICHERN       302
        #define ID_BEENDEN         303
        #define ID_ITEM1           304
        #define ID_ITEM2           305
        #define ID_ITEM3           306



Hier ist darauf zu achten, das der letzte Eintrag mit "Return" abzuschließen ist. Beim VC++ wird falls dies nicht geschehen ist ein Fehler beim kompilieren ausgegeben. Als letztes folgt die Resourcen Datei. Das Anlegen der Datei geschieht am besten mit einem Texteditor und wird dann mit der Extension *.rc abgespeichert.

C++:
// resource.rc
#include "resource.h"

USERMENUE MENU
BEGIN
    POPUP "&Datei"
    BEGIN
        MENUITEM "&Neu",                        ID_NEU
        MENUITEM "&Öffnen",                     ID_OEFFNEN
        MENUITEM "&Speichern",                  ID_SPEICHERN
        MENUITEM SEPARATOR
        MENUITEM "B&eenden",                    ID_BEENDEN
    END
    POPUP "&Ein paar Spielchen"
    BEGIN
        MENUITEM "&Diesen Text in die Titelleiste", ID_ITEM1
        MENUITEM "&Und jetzt diesen Text",          ID_ITEM2
        MENUITEM "&Wir machen die Titelleiste leer",ID_ITEM3
    END
END



Wir schauen uns als erstes unsere Resourcedatei an. Als erstes wird die Headerdatei resource.h eingebunden, danach folgt der Abschnitt zum erzeugen des Menüs. Zu Beginn wird ein Handle für unser Menü festgelegt: USERMENU. Danach folgt ein Bezeichner für die Struktur, die wir erzeugen wollen, beim erzeugen eines Menüs ist dies MENU. Die Art, also die Beschreibung unseres Menüs erfolgt dann innerhalb eines BEGIN - END Blockes. Durch den Befehl POPUP wird ein neuer übergeordneter Punkt in der Menüleiste erzeugt. Um den im Beispiel erzeugten übergeordneten Punkt "Datei" mit weiteren Enträgen zu füllen, folgt ein weiterer BEGIN - END Block. In diesem werden nun mit "MENUITEM Name , Handle" unsere Untereinträge erzeugt. Das Zeichen & läßt den darauffolgenden Buchstaben unterstrichen erscheinen. Es ist somit möglich mit der Tastenkombination ALT + unterstrichener Buchstabe den Untereintrag auszuwählen. Das Handle ist eine Zugriffszahl. Diese Zahl kann man (wie in dem Beispiel) durch #define-Befehle ersetzen. Das wird in unserer resource.h bewerkstelligt. Die Untereinträge werden somit einer eindeutigen ID_ zugeordnet und können durch diese angesprochen werden. Eigentlich ist unser Menü schon fertig und braucht nur noch angezeigt werden. Das erledigen wir in der Fensterklasse unserer Anwendung. Wir setzen den Handle des Menüs (USERMENUE) in die Fensterklasse wc ein:
wc.lpszMenuName = "USERMENUE";

Wenn wir jetzt unser Programm starten, stellen wir fest eine Menüzeile wird erzeugt und dargestellt. Um die Menüeinträge "scharf" zu machen müssen wir für die Windowsmessage Command (WM_COMMAND) eine Behandlung schreiben. Innerhalb der Verzweigung wird zwischen den einzelnen Menüpunkten unterschieden. Dazu benutzen wird das Makro LOWORD(wParam). Was die Menüpunkte z.Zt ausführen dürfte sich beim genauen hinsehen oder nach einem Test von selbst erklären.
Wir wollen nun unser Programm mit ein Paar Hot-Key's (Shortcuts - Tastenkombinationen) ausstatten. Damit die Tastenkombinationen verwendet werden können, müssen wir diese in unserer resource.rc Datei festlegen.
--
Gruß
void*
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
10.01.2003, 16:19 Uhr
void*
Generic Pointer
(Operator)



C++:
#include "resource.h"
USERMENUE MENU
BEGIN
    POPUP "&Datei"
    BEGIN
        MENUITEM "&Neu",                        ID_NEU
        MENUITEM "&Öffnen",                     ID_OEFFNEN
        MENUITEM "&Speichern",                  ID_SPEICHERN
        MENUITEM SEPARATOR
        MENUITEM "B&eenden",                    ID_BEENDEN
    END
    POPUP "&Ein paar Spielchen"
    BEGIN
        MENUITEM "&Diesen Text in die Titelleiste", ID_ITEM1
        MENUITEM "&Und jetzt diesen Text",          ID_ITEM2
        MENUITEM "&Wir machen die Titelleiste leer",ID_ITEM3
    END
END
SCHNELLTASTEN ACCELERATORS
BEGIN
        "S", ID_SPEICHERN, VIRTKEY, ALT
        "Q", ID_BEENDEN, VIRTKEY, ALT
END



Als erstes wird wieder das Handle für die Tastenkombination festgelegt, in unserem Fall SCHNELLTASTEN gefolgt von der Struktur ACCELERATORS, welche gefüllt werden muß. Im BEGIN-END Block weisen wir nun der ID_SPEICHERN die Tastenkombination S+ALT zu. Selbiges erledigen wir für die ID_BEENDEN. Drückt der Anwender die Tastenfolge "S+ALT" wird eine WM_COMMAND gesendet, wobei die ID_SPEICHERN jetzt in LOWORD(wParam) enthalten ist.
--
Gruß
void*
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
10.01.2003, 16:19 Uhr
void*
Generic Pointer
(Operator)


Nun müssen wir in unserer menue.cpp am Anfang die Tastenkombi mit folgenden Zeilen einbinden (Auszug aus menue.cpp):


C++:
#include <windows.h>

// Den Header für die Resourcen einbinden

#include "resource.h"


LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain (HINSTANCE hInstance,
                        HINSTANCE hPrevInstance,
                        PSTR szCmdLine,
                        int iCmdShow)
{
   // Hier laden wir unsere Tastenfolge

   HACCEL hAccel = LoadAccelerators(hInstance, "SCHNELLTASTEN");

   // Weiter mit der Anwendung

   MSG............
   ...............



Schauen wir unseren Eintrag etwas genauer an. HACCEL hAccel

Erzeugt ein Handle auf unsere Accelerator Tabelle. LoadAccelerators()

läd unsere erzeugte Accelerator Tabelle. Die Funktion ist wie folgt definiert
HACCEL LoadAccelerators(

HINSTANCE hInstance, // Handle der Anwendungs-Instanz (aus der
// WinMain()
LPCTSTR lpTableName // Tabellenname String ("SCHNELLTASTEN")
);


Gut. Wir haben nun unsere Tastenfolge geladen. Was aber damit anfangen?. Um auf die Tastenkombination reagieren zu können müssen wir an unserer Nachrichtenschleife etwas verändern:

C++:
............
............
while (GetMessage(&msg, NULL, 0, 0)== TRUE)
    {
                // Hier unser Eintrag

                if(!TranslateAccelerator(hWnd, hAccel, &msg))
                {
                        TranslateMessage(&msg);
                        DispatchMessage(&msg);
                }
    }

    return msg.wParam;
.............
.............



Die Funktion TranslateAccelerator()

überprüft ob eine gewählte Tastenkombination einer Menüresource zugeordnet ist und ist wie folgt aufgebaut:
int TranslateAccelerator(

HWND hWnd, // Handle unseres (enzigen) Fenster
HACCEL hAccTable, // Handle der Accelerator Tabelle
LPMSG lpMsg // Adresse unserer Messagestruktur
);
--
Gruß
void*
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
005
10.01.2003, 16:20 Uhr
void*
Generic Pointer
(Operator)


Die Shortcuts werden somit nicht als normale Tastendrücke behandelt.
An dieser Stelle eine kleine Hilfestellung für die Nutzer des Borland Compiler BCC32. VC++ und die LCC User welche zusätzlich die Entwicklungsumgebung Wedit Win32 nutzen werden jetzt jubeln. Das Programm macht das was es soll. Wer aber nicht mit dem Umgang des Borland Compiler vertraut ist kann leicht eine kleine Glaubenskrise bekommen. Das kompilieren des Quelltextes, der Resourcen und das Linken zu einem fertigen Programm ist nicht so ganz ohne. Folgende Arbeitsschritte sind notwendig:
1. Resourcen kompilieren
2. Sourcecode kompilieren
3. Linken zu einer lauffähigen Exe


Nun wollen wir uns die Arbeit natürlich erleichtern. Ich habe aus diesem Grund eine Datei mit dem Namen make.bat angelegt und die ganze Sache automatisiert.
@echo off

rem Falls nicht schon in der Autoexec.bat geschehen
rem den Path bekanntmachen in dem sich unser Compiler befindet

PATH=EBorland\BCC55\BIN;EBorland\BCC55\INCLUDE;EBorland\BCC55\LIB;

rem Für den Fall das schon eine Exe erzeugt wurde, diese löschen

if exist *.exe del *.exe

rem 1. Resourcen-Compiler Anweisung (erstellen der *.res-Dateien)

echo.
echo Compile Resources ...
brc32.exe -r resource.rc

rem 2. Compiler Anweisung (erzeugt eigene *.obj-Dateien)

echo.
echo Compile Sources ...
bcc32.exe -c -w -w-par menue.cpp

rem 3. Linker Anweisung (linkt startup-obj, eigene *.obj und *.res)

echo.
echo Linking Resources ...
ilink32.exe -r -C -x -aa c0w32.obj menue.obj, menue.exe,,import32.lib cw32.lib,,resource.res

rem Entferne die temporären Dateien

if exist *.obj del *.obj
if exist *.tds del *.tds
if exist *.res del *.res
if exist *.ilc del *.ilc
if exist *.ild del *.ild
if exist *.ilf del *.ilf


Die zusätzlichen gelinkten *.obj und *.lib Dateien werden benötigt um ein lauffähiges Programm zu erzeugen. Weitere Schalterinformation bitte der Dokumentation des Compilers entnehmen. Die Batch-Datei kann nun in eurem Projektordner ausgeführt werden. Ich hoffe das hilft euch für den Anfang etwas weiter.
Als nächstes wollen wir unser eigenes Icon unserem Programm hinzufügen. Was wir brauchen ist zunächst ein geeigentes Icon, welches man mit einem Resourceneditor selbst erstellen kann. Wer nichts passendes hat kann auf seinem System die Suchfunktion nutzen und sich alle Dateien mit der Extension *.ico heraus filtern lassen. Gut das Icon haben wir nun. Wir kümmern uns zunächst um die Einbindung. In unserer Resourcendatei fügen wir am Anfang folgende Zeile ein: IDI_ICON ICON DISCARDABLE "api.ico"

IDI_ICON ist das Icon-Handle was mittels #define IDI_ICON 101 im Header der Resourcendatei festgelegt wurde. Der Name der Icondatei wurde von mir frei gewählt. Bei manchen Compilern kann es sich erforderlich machen, daß der Pfad zum Icon mit angegeben werden muß IDI_ICON ICON DISCARDABLE "C:/icons/icon1.ico". Der Header sieht nun wie folgt aus:

C++:
// resource.h

        #define IDI_ICON           101
        #define ID_NEU             300
        #define ID_OEFFNEN         301
        #define ID_SPEICHERN       302
        #define ID_BEENDEN         303
        #define ID_ITEM1           304
        #define ID_ITEM2           305
        #define ID_ITEM3           306  



Die Einbindung im Quellcode um das Anwendungssymbol zu verändern, geschieht wie folgt:

C++:
   .................
   .................

   WNDCLASSEX wc={0};

   wc.cbSize= sizeof (WNDCLASSEX);
   static char szAppName[]= "Menü Erstellung";
   wc.style = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc = WndProc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 0;
   wc.hInstance = hInstance;
   wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
   wc.hCursor = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
   wc.lpszMenuName = "USERMENUE";
   wc.lpszClassName = szAppName;

   //wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

   wc.hIconSm = LoadIcon(wc.hInstance, (LPCTSTR)IDI_ICON);

   // Das war es schon

   RegisterClassEx(&wc);

   .................
   .................  


--
Gruß
void*
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
006
10.01.2003, 16:20 Uhr
void*
Generic Pointer
(Operator)


Eigentlich sollte nach einem Testlauf jetzt das Anwendungssymbol in gewünschter Form erscheinen, gleichzeitig wurde der erzeugten Exe-Datei unser Icon zugewiesen, was ein Blick in den Exepfad zeigt.
Um einen eigenen Cursor zu laden, ist die Vorgehensweise analog zum laden eines Icon. Die entsprechende Zeile in der *.rc Datei gestaltet sich dann so:

C++:
#include "resource.h"

IDI_ICON ICON DISCARDABLE     "api.ico"
IDC_HAND CURSOR DISCARDABLE   "hand.cur"
.............
.............



Der Header sieht dann so aus:

C++:
// resource.h

        #define IDI_ICON           101
        #define IDC_HAND           105
        #define ID_NEU             300
        #define ID_OEFFNEN         301
        #define ID_SPEICHERN       302
        #define ID_BEENDEN         303
        #define ID_ITEM1           304
        #define ID_ITEM2           305
        #define ID_ITEM3           306  



Um den Cursor im Programm zu laden schreiben wir unsere WinMain() - Funktion etwas um:

C++:
   .............
   .............
   WNDCLASSEX wc={0};

   wc.cbSize= sizeof (WNDCLASSEX);
   static char szAppName[]= "Menü Erstellung";
   wc.style = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc = WndProc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 0;
   wc.hInstance = hInstance;
   wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

   //wc.hCursor = LoadCursor(NULL, IDC_ARROW);

   wc.hCursor =LoadCursor(wc.hInstance,(LPCTSTR)IDC_HAND);

   //der Cursor wurde umgeändert

   wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
   wc.lpszMenuName = "USERMENUE";
   wc.lpszClassName = szAppName;
   //wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
   wc.hIconSm = LoadIcon(wc.hInstance, (LPCTSTR)IDI_ICON);

   RegisterClassEx(&wc);
   ............
   ............  


Wenn jetzt mit der Maus in den Arbeitsbereich des Fensters gegangen wird, sollte der neue Cursor eigentlich dargestellt werden.
Es folgen erst einmal die Listings für die einzelnen Dateien:

C++:
// menue.cpp
#include <windows.h>

// Den Header für die Resourcen einbinden

#include "resource.h"


LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain (HINSTANCE hInstance,
                        HINSTANCE hPrevInstance,
                        PSTR szCmdLine,
                        int iCmdShow)
{
   MSG msg;

   WNDCLASSEX wc={0};

   wc.cbSize= sizeof (WNDCLASSEX);
   static char szAppName[]= "Menü Erstellung";
   wc.style = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc = WndProc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 0;
   wc.hInstance = hInstance;
   wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
   wc.hCursor = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);

   // Handle für unser Menü setzten

   wc.lpszMenuName = "USERMENUE";
   wc.lpszClassName = szAppName;
   wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

   RegisterClassEx(&wc);

   HWND hWnd = CreateWindowEx(WS_EX_CLIENTEDGE,
                              szAppName,
                              "Menü Erstellung",
                              WS_OVERLAPPEDWINDOW,
                              CW_USEDEFAULT,
                              CW_USEDEFAULT,
                              CW_USEDEFAULT,
                              CW_USEDEFAULT,
                              NULL,
                              NULL,
                              hInstance,
                              NULL);

    ShowWindow(hWnd, iCmdShow);
    UpdateWindow(hWnd);

    while (GetMessage(&msg, NULL, 0, 0)== TRUE)
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }

    return msg.wParam;
}

LRESULT CALLBACK WndProc (HWND hwnd,
                          UINT message,
                          WPARAM wParam,
                          LPARAM lParam)

{
        HDC hdc;
        PAINTSTRUCT paintstruct;
        switch(message)
         {
          case WM_PAINT:
                  hdc = BeginPaint (hwnd, &paintstruct);

                  // Einen Text im Anwendungsfenster ausgeben

                  TextOut (hdc, 250, 200, "Eigene Menüs erzeugen.", 22);

                  EndPaint (hwnd, &paintstruct);
                  return 0;

          // Jetzt die Behandlungsroutine für WM_COMMAND

          case WM_COMMAND:
               switch(LOWORD(wParam))
               {
                case ID_NEU:

                                 /* Auf den Eintrag "Datei/Neu" geklickt ?
                                    Dann eine Nachrichtenbox ausgeben.
                                    Dient als Vorbereitung für spätere
                                    Lektionen, wo die Buttons zum leben erweckt
                                    werden */


                                 MessageBox(NULL, "Button 'Neu' geklickt", "Menütest", MB_OK);
                     return 0;
                case ID_OEFFNEN:
                                 MessageBox(NULL, "Button 'Öffnen' geklickt", "Menütest", MB_OK);
                     return 0;
                case ID_SPEICHERN:
                     MessageBox(NULL, "Button 'Speichern' geklickt", "Menütest", MB_OK);
                     return 0;
                case ID_BEENDEN:
                     MessageBox(NULL,
                                "Button 'Beenden' geklickt - Programm wird beendet",
                                "Menütest",MB_OK);

                     /* Nachdem bei der Nachrichtenbox auf "OK" geklickt wurde
                     - Raus aus unserer Anwendung */


                     SendMessage(hwnd, WM_CLOSE, 0, 0);
                     return 0;

                     /* Es folgen ein paar Spielchen mit SetWindowText() */

                case ID_ITEM1:
                     SetWindowText(hwnd, "Diesen Text in die Titelleiste");
                     return 0;
                case ID_ITEM2:
                     SetWindowText(hwnd, "Und jetzt diesen Text");
                     return 0;
                case ID_ITEM3:
                     SetWindowText(hwnd, "");
                     return 0;
               }
               return 0;

               case WM_DESTROY:
                     PostQuitMessage(0);
               return 0;
         }
         return DefWindowProc (hwnd, message, wParam, lParam);
}


--
Gruß
void*
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
007
10.01.2003, 16:21 Uhr
void*
Generic Pointer
(Operator)


Wenn wir alles übernommen haben, geht es daran die Headerdatei zu schreiben. // resource.h

C++:
        #define ID_NEU             300
        #define ID_OEFFNEN         301
        #define ID_SPEICHERN       302
        #define ID_BEENDEN         303
        #define ID_ITEM1           304
        #define ID_ITEM2           305
        #define ID_ITEM3           306



Hier ist darauf zu achten, das der letzte Eintrag mit "Return" abzuschließen ist. Beim VC++ wird falls dies nicht geschehen ist ein Fehler beim kompilieren ausgegeben. Als letztes folgt die Resourcen Datei. Das Anlegen der Datei geschieht am besten mit einem Texteditor und wird dann mit der Extension *.rc abgespeichert.

C++:
// resource.rc
#include "resource.h"

USERMENUE MENU
BEGIN
    POPUP "&Datei"
    BEGIN
        MENUITEM "&Neu",                        ID_NEU
        MENUITEM "&Öffnen",                     ID_OEFFNEN
        MENUITEM "&Speichern",                  ID_SPEICHERN
        MENUITEM SEPARATOR
        MENUITEM "B&eenden",                    ID_BEENDEN
    END
    POPUP "&Ein paar Spielchen"
    BEGIN
        MENUITEM "&Diesen Text in die Titelleiste", ID_ITEM1
        MENUITEM "&Und jetzt diesen Text",          ID_ITEM2
        MENUITEM "&Wir machen die Titelleiste leer",ID_ITEM3
    END
END



Wir schauen uns als erstes unsere Resourcedatei an. Als erstes wird die Headerdatei resource.h eingebunden, danach folgt der Abschnitt zum erzeugen des Menüs. Zu Beginn wird ein Handle für unser Menü festgelegt: USERMENU. Danach folgt ein Bezeichner für die Struktur, die wir erzeugen wollen, beim erzeugen eines Menüs ist dies MENU. Die Art, also die Beschreibung unseres Menüs erfolgt dann innerhalb eines BEGIN - END Blockes. Durch den Befehl POPUP wird ein neuer übergeordneter Punkt in der Menüleiste erzeugt. Um den im Beispiel erzeugten übergeordneten Punkt "Datei" mit weiteren Enträgen zu füllen, folgt ein weiterer BEGIN - END Block. In diesem werden nun mit "MENUITEM Name , Handle" unsere Untereinträge erzeugt. Das Zeichen & läßt den darauffolgenden Buchstaben unterstrichen erscheinen. Es ist somit möglich mit der Tastenkombination ALT + unterstrichener Buchstabe den Untereintrag auszuwählen. Das Handle ist eine Zugriffszahl. Diese Zahl kann man (wie in dem Beispiel) durch #define-Befehle ersetzen. Das wird in unserer resource.h bewerkstelligt. Die Untereinträge werden somit einer eindeutigen ID_ zugeordnet und können durch diese angesprochen werden. Eigentlich ist unser Menü schon fertig und braucht nur noch angezeigt werden. Das erledigen wir in der Fensterklasse unserer Anwendung. Wir setzen den Handle des Menüs (USERMENUE) in die Fensterklasse wc ein:
wc.lpszMenuName = "USERMENUE";

Wenn wir jetzt unser Programm starten, stellen wir fest eine Menüzeile wird erzeugt und dargestellt. Um die Menüeinträge "scharf" zu machen müssen wir für die Windowsmessage Command (WM_COMMAND) eine Behandlung schreiben. Innerhalb der Verzweigung wird zwischen den einzelnen Menüpunkten unterschieden. Dazu benutzen wird das Makro LOWORD(wParam). Was die Menüpunkte z.Zt ausführen dürfte sich beim genauen hinsehen oder nach einem Test von selbst erklären.
Wir wollen nun unser Programm mit ein Paar Hot-Key's (Shortcuts - Tastenkombinationen) ausstatten. Damit die Tastenkombinationen verwendet werden können, müssen wir diese in unserer resource.rc Datei festlegen.

C++:
#include "resource.h"
USERMENUE MENU
BEGIN
    POPUP "&Datei"
    BEGIN
        MENUITEM "&Neu",                        ID_NEU
        MENUITEM "&Öffnen",                     ID_OEFFNEN
        MENUITEM "&Speichern",                  ID_SPEICHERN
        MENUITEM SEPARATOR
        MENUITEM "B&eenden",                    ID_BEENDEN
    END
    POPUP "&Ein paar Spielchen"
    BEGIN
        MENUITEM "&Diesen Text in die Titelleiste", ID_ITEM1
        MENUITEM "&Und jetzt diesen Text",          ID_ITEM2
        MENUITEM "&Wir machen die Titelleiste leer",ID_ITEM3
    END
END
SCHNELLTASTEN ACCELERATORS
BEGIN
        "S", ID_SPEICHERN, VIRTKEY, ALT
        "Q", ID_BEENDEN, VIRTKEY, ALT
END


--
Gruß
void*
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
008
10.01.2003, 16:21 Uhr
void*
Generic Pointer
(Operator)


Als erstes wird wieder das Handle für die Tastenkombination festgelegt, in unserem Fall SCHNELLTASTEN gefolgt von der Struktur ACCELERATORS, welche gefüllt werden muß. Im BEGIN-END Block weisen wir nun der ID_SPEICHERN die Tastenkombination S+ALT zu. Selbiges erledigen wir für die ID_BEENDEN. Drückt der Anwender die Tastenfolge "S+ALT" wird eine WM_COMMAND gesendet, wobei die ID_SPEICHERN jetzt in LOWORD(wParam) enthalten ist.
Nun müssen wir in unserer menue.cpp am Anfang die Tastenkombi mit folgenden Zeilen einbinden (Auszug aus menue.cpp):


C++:
#include <windows.h>

// Den Header für die Resourcen einbinden

#include "resource.h"


LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain (HINSTANCE hInstance,
                        HINSTANCE hPrevInstance,
                        PSTR szCmdLine,
                        int iCmdShow)
{
   // Hier laden wir unsere Tastenfolge

   HACCEL hAccel = LoadAccelerators(hInstance, "SCHNELLTASTEN");

   // Weiter mit der Anwendung

   MSG............
   ...............



Schauen wir unseren Eintrag etwas genauer an. HACCEL hAccel

Erzeugt ein Handle auf unsere Accelerator Tabelle. LoadAccelerators()

läd unsere erzeugte Accelerator Tabelle. Die Funktion ist wie folgt definiert
HACCEL LoadAccelerators(

HINSTANCE hInstance, // Handle der Anwendungs-Instanz (aus der
// WinMain()
LPCTSTR lpTableName // Tabellenname String ("SCHNELLTASTEN")
);


Gut. Wir haben nun unsere Tastenfolge geladen. Was aber damit anfangen?. Um auf die Tastenkombination reagieren zu können müssen wir an unserer Nachrichtenschleife etwas verändern:

C++:
............
............
while (GetMessage(&msg, NULL, 0, 0)== TRUE)
    {
                // Hier unser Eintrag

                if(!TranslateAccelerator(hWnd, hAccel, &msg))
                {
                        TranslateMessage(&msg);
                        DispatchMessage(&msg);
                }
    }

    return msg.wParam;
.............
.............



Die Funktion TranslateAccelerator()

überprüft ob eine gewählte Tastenkombination einer Menüresource zugeordnet ist und ist wie folgt aufgebaut:
int TranslateAccelerator(

HWND hWnd, // Handle unseres (enzigen) Fenster
HACCEL hAccTable, // Handle der Accelerator Tabelle
LPMSG lpMsg // Adresse unserer Messagestruktur
);


Die Shortcuts werden somit nicht als normale Tastendrücke behandelt.
An dieser Stelle eine kleine Hilfestellung für die Nutzer des Borland Compiler BCC32. VC++ und die LCC User welche zusätzlich die Entwicklungsumgebung Wedit Win32 nutzen werden jetzt jubeln. Das Programm macht das was es soll. Wer aber nicht mit dem Umgang des Borland Compiler vertraut ist kann leicht eine kleine Glaubenskrise bekommen. Das kompilieren des Quelltextes, der Resourcen und das Linken zu einem fertigen Programm ist nicht so ganz ohne. Folgende Arbeitsschritte sind notwendig:
1. Resourcen kompilieren
2. Sourcecode kompilieren
3. Linken zu einer lauffähigen Exe


Nun wollen wir uns die Arbeit natürlich erleichtern. Ich habe aus diesem Grund eine Datei mit dem Namen make.bat angelegt und die ganze Sache automatisiert.
@echo off

rem Falls nicht schon in der Autoexec.bat geschehen
rem den Path bekanntmachen in dem sich unser Compiler befindet

PATH=EBorland\BCC55\BIN;EBorland\BCC55\INCLUDE;EBorland\BCC55\LIB;

rem Für den Fall das schon eine Exe erzeugt wurde, diese löschen

if exist *.exe del *.exe

rem 1. Resourcen-Compiler Anweisung (erstellen der *.res-Dateien)

echo.
echo Compile Resources ...
brc32.exe -r resource.rc

rem 2. Compiler Anweisung (erzeugt eigene *.obj-Dateien)

echo.
echo Compile Sources ...
bcc32.exe -c -w -w-par menue.cpp

rem 3. Linker Anweisung (linkt startup-obj, eigene *.obj und *.res)

echo.
echo Linking Resources ...
ilink32.exe -r -C -x -aa c0w32.obj menue.obj, menue.exe,,import32.lib cw32.lib,,resource.res

rem Entferne die temporären Dateien

if exist *.obj del *.obj
if exist *.tds del *.tds
if exist *.res del *.res
if exist *.ilc del *.ilc
if exist *.ild del *.ild
if exist *.ilf del *.ilf


Die zusätzlichen gelinkten *.obj und *.lib Dateien werden benötigt um ein lauffähiges Programm zu erzeugen. Weitere Schalterinformation bitte der Dokumentation des Compilers entnehmen. Die Batch-Datei kann nun in eurem Projektordner ausgeführt werden. Ich hoffe das hilft euch für den Anfang etwas weiter.
Als nächstes wollen wir unser eigenes Icon unserem Programm hinzufügen. Was wir brauchen ist zunächst ein geeigentes Icon, welches man mit einem Resourceneditor selbst erstellen kann. Wer nichts passendes hat kann auf seinem System die Suchfunktion nutzen und sich alle Dateien mit der Extension *.ico heraus filtern lassen. Gut das Icon haben wir nun. Wir kümmern uns zunächst um die Einbindung. In unserer Resourcendatei fügen wir am Anfang folgende Zeile ein: IDI_ICON ICON DISCARDABLE "api.ico"

IDI_ICON ist das Icon-Handle was mittels #define IDI_ICON 101 im Header der Resourcendatei festgelegt wurde. Der Name der Icondatei wurde von mir frei gewählt. Bei manchen Compilern kann es sich erforderlich machen, daß der Pfad zum Icon mit angegeben werden muß IDI_ICON ICON DISCARDABLE "C:/icons/icon1.ico". Der Header sieht nun wie folgt aus:

C++:
// resource.h

        #define IDI_ICON           101
        #define ID_NEU             300
        #define ID_OEFFNEN         301
        #define ID_SPEICHERN       302
        #define ID_BEENDEN         303
        #define ID_ITEM1           304
        #define ID_ITEM2           305
        #define ID_ITEM3           306  



Die Einbindung im Quellcode um das Anwendungssymbol zu verändern, geschieht wie folgt:

C++:
   .................
   .................

   WNDCLASSEX wc={0};

   wc.cbSize= sizeof (WNDCLASSEX);
   static char szAppName[]= "Menü Erstellung";
   wc.style = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc = WndProc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 0;
   wc.hInstance = hInstance;
   wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
   wc.hCursor = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
   wc.lpszMenuName = "USERMENUE";
   wc.lpszClassName = szAppName;

   //wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

   wc.hIconSm = LoadIcon(wc.hInstance, (LPCTSTR)IDI_ICON);

   // Das war es schon

   RegisterClassEx(&wc);

   .................
   .................  



Eigentlich sollte nach einem Testlauf jetzt das Anwendungssymbol in gewünschter Form erscheinen, gleichzeitig wurde der erzeugten Exe-Datei unser Icon zugewiesen, was ein Blick in den Exepfad zeigt.
Um einen eigenen Cursor zu laden, ist die Vorgehensweise analog zum laden eines Icon. Die entsprechende Zeile in der *.rc Datei gestaltet sich dann so:

C++:
#include "resource.h"

IDI_ICON ICON DISCARDABLE     "api.ico"
IDC_HAND CURSOR DISCARDABLE   "hand.cur"
.............
.............



Der Header sieht dann so aus:

C++:
// resource.h

        #define IDI_ICON           101
        #define IDC_HAND           105
        #define ID_NEU             300
        #define ID_OEFFNEN         301
        #define ID_SPEICHERN       302
        #define ID_BEENDEN         303
        #define ID_ITEM1           304
        #define ID_ITEM2           305
        #define ID_ITEM3           306  



Um den Cursor im Programm zu laden schreiben wir unsere WinMain() - Funktion etwas um:

C++:
   .............
   .............
   WNDCLASSEX wc={0};

   wc.cbSize= sizeof (WNDCLASSEX);
   static char szAppName[]= "Menü Erstellung";
   wc.style = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc = WndProc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 0;
   wc.hInstance = hInstance;
   wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

   //wc.hCursor = LoadCursor(NULL, IDC_ARROW);

   wc.hCursor =LoadCursor(wc.hInstance,(LPCTSTR)IDC_HAND);

   //der Cursor wurde umgeändert

   wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
   wc.lpszMenuName = "USERMENUE";
   wc.lpszClassName = szAppName;
   //wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
   wc.hIconSm = LoadIcon(wc.hInstance, (LPCTSTR)IDI_ICON);

   RegisterClassEx(&wc);
   ............
   ............  


Wenn jetzt mit der Maus in den Arbeitsbereich des Fensters gegangen wird, sollte der neue Cursor eigentlich dargestellt werden.
--
Gruß
void*
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 <     [ FAQ C / C++ (WinAPI, Konsole) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: