|
![]() |
Vor
![]() Nun ja, man entwickelt (sich) weiter, man probiert aus und vor allem: Man fängt an, Bücher zu lesen. Zum Beispiel den "Petzold", das Standardwerk für NonVCL-Entwickler. Nun gut, ich wollte ja nicht Werbung für dieses Buch machen (es ist aber wirklich super!), sondern langsam zum eigentlichen Thema kommen. Charles Petzold stellt im Kapitel 11 (Dialogfelder) sein Programm HexCalc - einen Hexadezimalrechner - vor und beantwortet so ganz nebenbei meine Frage nach dem Dialog mit Klassennamen. Da mich neben dem Thema an sich auch der Code begeistert hat, habe ich das Programm nach Delphi übersetzt. ![]() Das im folgenden wiedergegebene Programm HEXCALC darf man wohl ohne Übertreibung als Gipfel der Faulheit bezeichnen: Es enthält überhaupt keinen Aufruf von CreateWindow, kümmert sich keinen Deut um Nachrichten des Typs WM_PAINT, interessiert sich weder für Gerätekontexte noch für Mausereignisse und umfaßt insgesamt weniger als 150 Zeilen Quelltext. Dennoch kommt dabei ein Hexadezimalrechner mit 10 Funktionen sowie einer kompletten Tastatur- und Mausschnittstelle heraus, der sich in Abbildung 10.5 [...] bewundern lässt.
Zunächst geht es um die Registrierung der Fensterklasse und die Erstellung des Dialogfeldes. Im Ressourceneditor wird für den Dialog der Klassenname "HexCalc" eingetragen; wird der Dialog über ein Script erstellt, sieht das ganze so aus:
Code:
Der Fensterprozedur-Zeiger lpfnWndProc wird einer herkömmlichen Window-Prozedur (WndProc) zugewiesen, die sonst übliche Dialogprozedur fehlt. Zudem ist der Wert DLGWINDOWEXTRA der Member-Variable cbWndExtra wichtig, diese reserviert einige zusätzliche Bytes für die Nutzung einer Dialog-Ressource. Im Aufruf von CreateDialog wird schließlich der String "HexCalc" (definiert in der Konstante szAppName) der Fensterklasse übergeben. Die sonst nötige Adresse der Dialogprozedur bleibt leer, da das Programm die Nachrichten selbst verarbeitet.
100 DIALOGEX 0, 0, 205, 130
STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | DS_NOFAILCREATE | DS_CENTER | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Hexadezimalrechner" CLASS "HexCalc" FONT 8, "MS Shell Dlg"
Delphi-Quellcode:
Die Programmlogik kommt ganz ohne die sonst notwendigen Konstanten IDC_CLOSE und weitere aus. Zahlenwerte merken muss sich trotzdem niemand, die Lösung ist verblüffend einfach: Für die IDs der Schaltflächen wird einfach der ASCII-Code der jeweiligen Taste benutzt, über welche die Funktionen auch ausgeführt werden können. In den meisten Fällen entspricht der Code direkt der Buttonbeschriftung. Daraus ergibt sich, dass die Nachrichten WM_CHAR und WM_COMMAND letztendlich die gleiche Funktion aufrufen können. Programmlogik ist dabei nur für einige Spezialfälle notwendig: Die Taste "=" kann auch über die Eingabetaste angesprochen werden und statt der Rücktaste ist auch Pfeil-Links möglich.
var
WndClass: TWndClassEx; msg: TMsg; begin WndClass.cbSize := SizeOf(TWndClassEx); WndClass.style := CS_HREDRAW or CS_VREDRAW; WndClass.lpfnWndProc := @WndProc; WndClass.cbClsExtra := 0; WndClass.cbWndExtra := DLGWINDOWEXTRA; WndClass.hInstance := HInstance; WndClass.hIcon := LoadIcon(hInstance, MAKEINTRESOURCE(100)); WndClass.hCursor := LoadCursor(0, IDC_ARROW); WndClass.hbrBackground := COLOR_APPWORKSPACE; WndClass.lpszMenuName := nil; WndClass.lpszClassName := szAppName; RegisterClassEx(WndClass); InitCommonControls; hDialog := CreateDialog(hInstance, MAKEINTRESOURCE(100), 0, nil); while GetMessage(msg,0,0,0) do begin TranslateMessage(msg); DispatchMessage(msg); end; ExitCode := msg.wParam; DestroyWindow(hDialog); end.
Delphi-Quellcode:
Egal, ob die Maus oder die Tastatur verwendet wird: Der entsprechende Code wird an die Prozedur HandleKey weitergeleitet, welche die Eingabe verarbeitet. Bei der Tastatureingabe wird dazu noch kurzzeitig der Button gedrückt, um eine optische Rückmeldung zu erhalten. Leider funktioniert dies nur bis einschl. Windows 2000, so dass ich mich entschlossen habe, bei Windows XP oder darüber den Button kurz zu deaktivieren. Die Funktion IsWindowsXPOrHigher liefert True, wenn die Windows-Version mindestens Version 5.1 ist.
function WndProc(hwnd: HWND; uMsg: UINT; wParam: WPARAM;
lParam: LPARAM): LRESULT; stdcall; var hButton: THandle; cCommand: CHAR; begin case uMsg of // [...] WM_KEYDOWN: If wParam = VK_LEFT then HandleKey(VK_BACK); WM_CHAR: begin cCommand := UpCase(Chr(wParam)); If wParam = VK_RETURN then cCommand := '='; hButton := GetDlgItem(hWnd, Ord(cCommand)); If hButton <> 0 then begin HandleKey(Ord(cCommand)); If IsWindowsXPOrHigher then EnableWindow(hButton, False) else SendMessage(hButton, BM_SETSTATE, 1, 0); Sleep(100); If IsWindowsXPOrHigher then EnableWindow(hButton, True) else SendMessage(hButton, BM_SETSTATE, 0, 0); end else begin MessageBeep(0); end; end; WM_COMMAND: begin SetFocus(hWnd); {$IFDEF CloseOnESC} if(wParam = IDCANCEL) then SendMessage(hDlg,WM_CLOSE,0,0) else {$ENDIF} if HIWORD(wParam) = BN_CLICKED then begin HandleKey(LoWord(wParam)); end; end; end; Result := DefWindowProc(hwnd, uMsg, wParam, lParam); end; Der Befehl SetFocus im WM_COMMAND-Zweig ist nötig, damit das Hauptfenster auch weiterhin die WM_CHAR-Nachrichten erhält, wenn der Rechner mit der Maus bedient wurde. Die Prozedur HandleKey habe ich erstellt, um den Programmablauf etwas logischer zu gestalten, eine "durchgehende Case-Anweisung", wie sie bei C (switch ohne break) vorhanden ist, gibt es bei Delphi nicht. Die Prozedur dient zum Einlesen der Tasten, Erstellen der Zahlen zum Rechnen und Setzen der globalen Variablen iOperation und bNewNumber, die bestimmen, welche Rechenoperation durchgeführt werden soll und ob eine neue Zahleneingabe begonnen werden soll. Die beiden Funktionen IsHex und HexCharToInt habe ich hinzugefügt, da Delphi die C-Funktionen isdigit und isxdigit nicht kennt.
Delphi-Quellcode:
Die Funktion CalcIt schließlich führt die eigentlichen Berechnungen durch und gibt das Ergebnis zurück. Auch hier musste der Code angepasst werden, da es die von C bekannten bedingten Anweisungen in Delphi nicht gibt - diese geringere Effizienz wird allerdings mit drastisch verbesserter Lesbarkeit belohnt.
procedure HandleKey(Key: Integer);
function IsHex(C: Char): Boolean; // [...] function HexCharToInt(Key: Byte): Cardinal; // [...] begin If Key = VK_ESCAPE then begin iNumber := 0; ShowNumber(hDialog, iNumber); end else if Key = VK_BACK then begin iNumber := iNumber div 16; ShowNumber(hDialog, iNumber); end else if IsHex(Chr(Key)) then begin if bNewNumber then begin iFirstNum := iNumber; iNumber := 0; end; bNewNumber := False; if (iNumber <= MAXDWORD shr 4) then begin iNumber := 16 * iNumber + HexCharToInt(Key); ShowNumber(hDialog, iNumber); end else MessageBeep(0); end else begin If not bNewNumber then begin iNumber := CalcIt(iFirstNum, iOperation, iNumber); ShowNumber(hDialog, iNumber); end; bNewNumber := True; iOperation := Key; end; end;
Delphi-Quellcode:
Bestandteil des Programms ist auch die Unit HexCalcUtils. Diese existiert lediglich, um nicht die Units CommCtrl und SysUtils verwenden zu müssen, damit das Kompilat schön klein bleibt.
function CalcIt(iFirstNum: Cardinal; iOperation: Integer;
iNum: Cardinal): Cardinal; begin Case Chr(iOperation) of '=': Result := iNum; '+': Result := iFirstNum + iNum; '-': Result := iFirstNum - iNum; '*': Result := iFirstNum * iNum; '&': Result := iFirstNum and iNum; '|': Result := iFirstNum or iNum; '<': Result := iFirstNum shl iNum; '>': Result := iFirstNum shr iNum; 'X': Result := iFirstNum xor iNum; '%': If iNum <> 0 then Result := iFirstNum mod iNum else Result := MAXDWORD; '/': If iNum <> 0 then Result := iFirstNum div iNum else Result := MAXDWORD; else Result := 0; end; end; Noch ein paar Worte zum eigentlichen Programm: Es verwendet die Infix-Notation, funktioniert also mit Berechnungen in der Form 3 + 4 = wie der normale Windows-Rechner. Neben den vier Grundrechenarten beherrscht das Programm den Divisionsrest, bitweises AND, OR und XOR sowie bitweises Links- und Rechtsschieben. Der Rechner arbeitet mit 32-Bit-Darstellung (DWORD). Auf Division und Modulo durch 0 reagiert das Programm mit der Anzeige FFFF FFFF. Die Tastenkombinationen:
Code:
So, dann wünsche ich viel Spaß beim Hex-Rechnen
Funktion Taste
============================== 0..9 0..9 A..F A..F + + - - * * / ÷ oder / Mod % And & Or | Xor X Shl < Shr > = = oder Eingabetaste C (Clear) ESC Korrektur Backspace oder Pfeil-Links ![]() In eigener Sache: Die Wahl des richtigen Forums war nicht leicht. Es ist ein NonVCL-Thema, aber eigentlich auch Open Source und genaugenommen auch ein Tutorial. Wenn's hier nicht passt, möge es bitte ein Moderator verschieben, Cross-posten oder was auch immer. Edit: Nachdem mein "Vorkoster" ein paar Bissen dieses Beitrags probiert hat, konnte ich einige Schreibfehler beseitigen. Danke, Wastl! ![]() |
Turbo Delphi für Win32 |
#2
Ich habe noch einige Bugs behoben.
Version 1.0.1.1
Daniel
Daniel Schuhmann
|
![]() |
Ansicht |
![]() |
![]() |
![]() |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
![]() |
![]() |