Buchempfehlung
Mikrocomputertechnik mit Controllern der Atmel AVR-RISC-Familie
Mikrocomputertechnik mit Controllern der Atmel AVR-RISC-Familie
Umfassend, aber leicht verständlich führt dieses Buch in die Programmierung von ATMEL AVR Mikrocontrollern ein. [Mehr Infos...]
FreeBASIC-Chat
Es sind Benutzer im FreeBASIC-Chat online.
(Stand:  )
FreeBASIC bei Twitter
Twitter FreeBASIC-Nachrichten jetzt auch über Twitter erhalten. Follow us!

Tutorial

Erste Schritte in der WinAPI mit Freebasic und FBEdit

von MitgliedstephanbrunkerSeite 8 von 13

Icons und Bitmaps

Zur Abwechselung kümmern wir uns mal um ein schönes Icon für unseren Dialog, das in klein in der Titelzeile und in größer in der Taskleiste angezeigt wird. Der Abstecher im vorherigen Kapitel mit dem Hinzufügen der Resource für das MANIFEST war nicht ganz vergebens, denn auf die gleiche Weise (ohne die Datei vorher im Projektfenster zu erzeugen) fügen wir ein:

Type = ICON; Name = IDC_ICON1; ID = 100

Das gewünschte Icon kopieren wir in den Unterordner \Res unseres Projektes und wählen es aus. Dann kommen drei Schritte: Einmal das Icon definieren, laden und es unserem Dialog zuweisen. Also wieder etwas für WM_INITDIALOG, der Code sieht so aus:

Dialog.bi

#Define IDC_ICON1 100
Dim Shared as HICON hIcon1

Dialog.bas

hIcon1 = LoadImage ( hInstance, Cast(LPCTSTR,IDC_ICON1), IMAGE_ICON, GetSystemMetrics(SM_CXICON),GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR )
SendMessage(hWin, WM_SETICON, ICON_BIG, Cast(LPARAM,hIcon1))

Das Icon enthält intern verschiedene Größen, die in unserem Fall ja für Taskleiste, Titelzeile und Desktop benötigt werden, es reicht aber beim Dialog eine Größe anzugeben, SM_CXICON ist das normale, "große" Icon. Zu den Cast()-Anweisungen: Funktionieren tut das Programm auch so. Aber der Compiler meckert wegen nicht übereinstimmender Datentypen. Um also eine saubere Compilermeldung zu bekommen, werden die Werte eben auf das richtige Format gecastet.

Wenn wir das Icon einem Button zuweisen wollen, braucht es auch nur eine Zeile:

SendDlgItemMessage(hMain,IDC_BTN2,BM_SETIMAGE,Cast(WPARAM,IMAGE_ICON),Cast(LPARAM,hIcon1))

Wenn wir das Icon selbst in einem Iconeditor erzeugen, können wir natürlich auch andere Größen erzeugen und diese (in Pixeln) anstelle von GetSystemMetrics(SM_CXICON) bei LoadImage angeben.

Bitmaps auf unserem Dialog gehen noch einfacher: Wir erzeugen eine Ressource, zum Beispiel

Type = BITMAP; Name = IDC_BMP1; ID = 101

mit einer *.bmp - Datei unserer Wahl, und dann brauchen wir nur das Image-Element im Rescourcen-Editor in unserem Dialog zu verbauen und als Property "Image" unser IDC_BMP1 anzugeben, fertig.

Transparenz bei einer Bitmap funktioniert auch ganz einfach: Wenn die Bitmap eine 32-bit Variante mit integriertem Alpha-Kanal ist, dann werden die transparenten Bereiche auch transparent dargestellt. Wie man die erzeugt, ist genauer beim Toolbar-Tutorial beschrieben.

Fensterklassen

Bisher haben wir unser Programm als Dialog in der Defaultklasse für Dialoge laufen lassen. In Windows gehört jedes Element einer Klasse an, in der globale Eigenschaften hinterlegt sind. Wir hatten zum Beispiel die Hintergrundfarbe 3DBTNFACE für Dialoge im vorherigen Kapitel. Es gibt mehrere Gründe, unsere eigene Fensterklasse anzulegen. Das Template dafür ist etwas anders und enthält gegenüber dem Template ohne Klasse folgende Ergänzungen:

Externer Link!Dialog Class.tpl

'create and register Window Class
Dim wc As WNDCLASSEX
    With wc
        .cbSize         = SizeOf(WNDCLASSEX)
        .style          = CS_HREDRAW or CS_VREDRAW
        .lpfnWndProc        = @WndProc
        .cbClsExtra     = 0
        .cbWndExtra     = DLGWINDOWEXTRA
        .hInstance          = hInstance
        .hbrBackground  = 0
        .lpszClassName      = @"MAIN"
        .lpszMenuName       = 0
        .hIcon          = 0
        .hIconSm            = 0
        .hCursor            = 0
    End With

RegisterClassEx(@wc)

Die Klasse definiert eine eigene Callbackfunction, die lassen wir aber leer und verweisen auf die Callbackfunction für unseren Dialog:

Function WndProc(ByVal hWin As HWND,ByVal uMsg As UINT,ByVal wParam As WPARAM,ByVal lParam As LPARAM) As Integer
    'Callback function for the Window Class =
    'proceed with Callback function for the Dialog
    Return DefDlgProc(hWin,uMsg,wParam,lParam)
End Function

Wir können bei umfangreicheren Programmen natürlich auch globale Funktionen in diese Callbackfunction packen. Dann ist die Property "Class" für unseren Dialog im Rescourceneditor auf MAIN zu setzen oder was immer wir als lpszClassName angeben (kürzer ist aber besser wegen des belegten Speichers). Dann können wir für die Klasse einen Hintergrund, ein Icon, ein keines Icon und einen Cursor definieren. In der einfachsten Version ist das jeweils NULL für den Defaultwert. hbrBackground funktioniert bei unserem Programmfenster nicht, weil es ein Dialog ist - auch welchem Grund auch immer. Interessant sind die Icon (wir müssen eine große und kleine Größe laden) und der Cursor (Auswahl gibt es unter LoadCursor in der MSDN):

        .hIcon  = LoadImage(hInstance,Cast(LPCTSTR,IDC_ICON1),IMAGE_ICON,_
                                GetSystemMetrics(SM_CXICON),GetSystemMetrics(SM_CYICON),_
                                LR_DEFAULTCOLOR)
        .hIconSm    = LoadImage(hInstance,Cast(LPCTSTR,IDC_ICON1),IMAGE_ICON,_
                                GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),_
                                LR_DEFAULTCOLOR)
        .hCursor    = LoadCursor(NULL,IDC_ARROW)

Man kann auch ein Menü für alle Fenster der Klasse angeben. Im Grunde funktioniert bisher auch alles ohne eine eigene Fensterklasse anzugeben.

Den Hintergrund kann man übrigens trotzdem ändern, wenn man bei der WM_CTLCOLORDLG einen passenden Brush zurückgibt, also zum Beispiel GetStockObject(WHITE_BRUSH) oder CreateSolidBrush(&h00AAAAAA), wobei man den Brush bei Programmende allerdings wieder freigeben muss. Cursor ändern geht nach dem gleichen Prinzip: Ein shared HCURSOR, der mit LoadCursor geladen wird. Da SetCursor praktischerweise den vorherigen Cursor speichert, kann man dann zum Beispiel zwischen Sanduhr und Pfeil einfach hin und her wechseln.

Subklassen

Auf die Subklassen kann man indes nicht verzichten. Bestimmte Messages wie Keyboard Input oder Drag&Drop werden nämlich nicht an unser Hauptfenster geschickt, sondern direkt an das Element. Das heißt, wir brauchen dafür eine eigene Callbackfunktion, die diese Messages bearbeitet. Es gibt dafür mehrere Möglichkeiten, von denen mir SetWindowSubclass am besten gefällt. Das Problem dabei ist wieder, dass diese Function aktuell in den Includes nicht dabei ist, aber man kann sie trotzdem aufrufen, da sie in den DLL's enthalten ist und wir sie definieren. Also muss das hier in unsere Dialog.bi:

'Declarations Subclassing
Type SUBCLASSPROC As Function( ByVal As HWND, ByVal As UINT, ByVal As WPARAM, _
                               ByVal As LPARAM, ByVal As UINT_PTR, _
                               ByVal As DWORD_PTR) As LRESULT

Declare Function SetWindowSubclass Alias "SetWindowSubclass" _
                                  ( ByVal As HWND, _
                                    ByVal As SUBCLASSPROC, _
                                    ByVal As UINT_PTR, _
                                    ByVal As DWORD_PTR ) As BOOL

Declare Function RemoveWindowSubclass Alias "RemoveWindowSubclass" _
                                     ( ByVal As HWND, _
                                       ByVal As SUBCLASSPROC, _
                                       ByVal As UINT_PTR ) As BOOL

Declare Function DefSubclassProc Alias "DefSubclassProc" _
                                ( ByVal As HWND, _
                                  ByVal As UINT, _
                                  ByVal As WPARAM, _
                                  ByVal As LPARAM ) As LRESULT

Declare Function GetWindowSubclass Alias "GetWindowSubclass" _
                                  ( ByVal As HWND, _
                                    ByVal As SUBCLASSPROC, _
                                    ByVal As UINT_PTR, _
                                    ByVal As DWORD_PTR Ptr ) As BOOL

Bei der Initalisierung weisen wir unserem Element eine Subklasse zu und bearbeiten dann die Messages dann in einer eigenen Callbackfunktion. Ein Beispiel dafür im nächsten Kapitel, zusammen mit dem "Datei öffnen" - Dialog, und einer Listbox.

 

Gehe zu Seite Gehe zu Seite  1  2  3  4  5  6  7  8  9  10  11  12  13  
Zusätzliche Informationen und Funktionen
  Bearbeiten Bearbeiten  

  Versionen Versionen