|
Antwort |
Hi!
Ich dachte mir, da in letzter Zeit vermehrt Fragen im Forum auftauchten, die man sozusagen, bei der Erstellung eines einfachen Texteditors „lernt“, schreibe ich dazu mal ein Tutorial. Da es mein erstes Tutorial ist, bitte ich um Nachsicht und bin für konstruktive Kritik natürlich dankbar. Das Tutorial wurde nur mit Komponenten erstellt, die in Delphi 7 PE enthalten sind. 1. Vorüberlegung Ich möchte zuerst zu bedenken geben, dass wir nicht Word nachprogrammieren wollen (oder zumindest nicht können), sondern einen einfachen kleinen Texteditor schaffen wollen. Ich möchte aber gerne gewisse grundlegende Funktionen wie z.B. Kopieren, Einfügen, Schriftformatierung etc. implementieren. So genug der grauen Theorie, fangen wir an: 2. Erste Schritte: Das Grundlegende Ich nehme ein Form, benenne es als MainForm und speichere es als Mainfrm.pas ab. Ich gebe ihm die Caption Texteditor. Als nächsten Schritt werde ich die oberste Menüzeile (Datei, Bearbeiten, Hilfe) integrieren. Dazu nehme ich die Komponente MainForm vom Register Standard und platziere sie auf dem Form. Durch einen Doppelklick auf die eben platzierte Komponente gelange ich in den Editor für das MainMenu. Ich denke die Bedienung dürft intuitiv sein, ich weise den Feldern in der oberen Zeile jeweils Name und Caption zu (z.B.: Name: Datei, Caption: &Datei, das & bewirkt, dass der folgende Buchstabe einen Unterstrich erhält, sodass dieser Eintrag durch Drücken der Taste des unterstrichenen Buchstabens selektiert werden kann.) Ich hoffe, die Vorgehensweise ist klar geworden. Den genauen Aufbau kann man ja dem Programm entnehmen. Irgendwie fehlt den Menüs allerdings noch etwas das gewohnte Aussehen: die Icons vor den Einträgen fehlen. Um diese hinzubekommen nehme ich eine ImageList von der Registerkarte Win32. In Sie lade ich die Icons in der Reihenfolge, wie ich sie in den Menüs verwenden werde. (Die Icons kann man mit diversen Programmen extrahieren, oder sie auch mit einem Grafikprogramm aus einem Screenshot rausschneiden.) Damit die Einträge auch wissen, welche Icons sie nutzen sollen, weise ich dem MainMenu in der Eigenschaft Images die eben erstellte ImageList1 zu. Jetzt weise ich nur noch jedem Eintrag die entsprechende Nummer aus der ImageList in der Eigenschaft ImageIndex zu. Jetzt sieht es doch schon besser aus, oder? Als nächstes erstelle ich die Menüs mit den schönen Buttons unterhalb der eben erstellten Menüleiste. Hierfür verwende ich die Komponente Toolbar aus dem Register Win32. Ihr Kontextmenü ermöglicht mir das Hinzufügen von Buttons und Trennbereichen. Ich füge 2 dieser Toolbars ein, erstelle jeweils einige Buttons auf ihnen und weise ihnen wiederum die ImageList1 in ihrer Eigenschaft Images zu. Es steht jedem nun frei, welche Buttons er in die Toolbars einfügt, ich habe mich für die unten gezeigte Möglichkeit entschieden. (Dabei habe ich der ImageList noch weitere Bilder zugefügt) Die beiden Comboboxen dienen der späteren Auswahl von Schriftart und Größe, die Schaltfläche ohne Bild für die Farbwahl. (Zum zuweisen der Images kann die Eigenschaft ImageIndex des jeweiligen Buttons genutzt werden.) Das Wichtigste fehlt natürlich noch, das Fenster zum Anzeigen und Editieren des Text, wir wollen ja schließlich einen Texteditor erstellen. Dazu nehme ich die Komponente Richedit vom Registerblatt Win32 und platziere sie auf dem Formular. Ich stelle die Eigenschaft Align auf alClient, was bewirkt, dass das Richedit den gesamten verfügbaren Platz auf dem Formular ausfüllt. Als letztes Element füge ich noch eine Statusleiste ein, die sich ebenfalls auf dem Registerblatt Win32 befindet. Sie rundet das Bild ab und das eben erstellte Fenster erinnert doch schon recht stark an ähnliche Programme wie z.B. WordPad. Damit in Zukunft das Formular maximiert gestartet wird, stelle ich seine Eigenschaft WindowState auf wsMaximized. 3. Die Programmierung Der erste Schritt sollte nochmal etwas Theorie sein. Sie ist vielleicht im Moment etwas nervig, erleichtert die spätere Arbeit aber ungemein. Bei unserer Programmplanung ist es so, dass sowohl über den Menüpunkt als auch über den Button Neu ja die selbe Funktion ausgeführt werden soll. Diese also zweimal zu programmieren ist unnötig und erhöht nur die Programmgröße. Um dies zu vermeiden, stellt uns Delphi eine schöne Komponente zur Seite: die ActionList (Registerkarte Standard). Die Actionlist ist eine nicht sichtare Komponente, die es uns ermöglicht sogenannte Actions zu erstellen und diese dann mehreren Buttons oder Menüeinträgen zuzuweisen. Als erstes wollen wir die Action für das Öffnen eines Dokumentes erzeugen. Allerdings stören wir uns noch daran, dass beim Starten des Programmes bereits Richedit1 in unserem Fenster drinsteht. Um dies zu beheben, klicken wir auf die 3 Punkte der Eigenschaft Lines des Richedits und löschen den Text dann aus dem erscheinenden Fenster raus. Somit ist dieses „Problem“ behoben. Nun aber zum Öffnen einen Dokuments. Wir klicken doppelt auf die ActionList und wählen oben NewAction (erstes Icon) aus. Als Caption geben wir Ö&ffnen an, als Name Aoeffnen (A für Action), als ShortCut Strg+O, als ImageIndex 1, (dafür muss bei der ActionList die Eigenschaft Images wieder auf ImageList1 gestellt sein). Bei Category gebe ich Datei an, dies dient aber nur der Übersichtlichkeit und hat keine weiteren Auswirkungen. Ein Doppelklick auf die eben erstellte Action bringt mich in meinen Quellcode-Editor. An dieser Stelle gebe ich jetzt ein, was ich auch sonst beim Buttonklick eingegeben hätte. Zum Auswählen einer Datei zum Öffnen stellt uns Delphi wiederum eine Hilfe zur Verfügung, den OpenDialog (Registerkarte Dialoge). Wir ziehen ihn auf unser Form und kehren dann zum Quellcode-Editor zurück. Hier geben wir nun ein:
Delphi-Quellcode:
Die erste Zeile bewirkt das Ausführen des OpenDialogs, die 3. überprüft das Vorhandensein der ausgewählten Datei und mit der 5. Zeile wird die Datei in das Richedit geladen. Ich gehe davon aus, dass die Funktionen eigentlich selbsterklärend sind.
If Opendialog1.Execute then
begin if FileExists(OpenDialog1.Filename) then begin Richedit1.Lines.LoadfromFile(OpenDialog1.Filename); end; end; Jetzt weise ich der Eigenschaft Action des entsprechenden Buttons und des entsprechenden Menüeintrags den Wert Aoeffnen zu und probiere das Programm aus. Es funktioniert, aber was mir natürlich noch nicht gefällt ist, dass ich alle Dateien auswählen kann, was zum Teil unschöne Nebeneffekte hat. Um dies zu verhindern nutze ich die Eigenschaft Filter des OpenDialogs und gebe auf der linke Seite an, Textdateien (*.txt, *.rtf), auf der rechten *.txt, *.rtf. Falls jemand doch andere Dateitypen auswählen gebe ich ihm die Möglichkeit, indem ich in der zweiten Zeile eintrage: Alle Dateien und *.* Somit kann man schon Dateien öffnen. Wenn wir diese dann verändert haben wollen wir sie natürlich auch speichern. Dazu nehme ich dann den SaveDialog von der Registerkarte Diaogs stelle die selben Filter ein und setze zusätzlich noch die Option ofOwerridePrompt auf true. Dies bewirkt die übliche Sicherheitsabfrage, wenn eine Datei mit gleichem Dateinamen bereits existiert und sie überschrieben würde. Außerdem gebe ich noch bei DefaultExt *.rtf an, damit die Endung automatisch angefügt wird, wenn der User dies nicht tut. Dann erschaffe ich eine neue Action in der Actionlist, nenne sie Aspeichernu (da es ja für die Speichern unter Funktion ist (mit dem Dialog)) gebe ihr die entsprechende Caption, weise sie dem Menüeintrag Speichern unter zu und klicke doppelt auf sie, um in den Quellcode-Editor zu gelangen. Dort gebe ich analog zum Öffnen ein:
Delphi-Quellcode:
Auch dies funktioniert. Nun möchte ich die Speichern-Funktion implementieren. Der einzige Unterschied ist, dass sie direkt speichert ohne den Dialog, allerdings nur dann, wenn eine Datei geöffnet wurde oder bereits gespeichert wurde.
if SaveDialog1.Execute then
begin if SaveDialog1.Filename<>’’ then // ist Dateiname angegeben? begin Richedit1.Lines.SavetoFile(SaveDialog1.Filename); end; end; Deswegen ist es wichtig, dem oberen Code der Speichern unter Funktion noch folgende Zeile anzufügen: OpenDialog1.Filename:=’’; // kommt unter RichEdit1.Lines. Save......... Es bewirkt, dass beim Speichern ohne Dialog nicht mehr der Dateiname vom Öffnen übernommen wird, wenn bereits unter einem anderen Namen gespeichert wurde. Die Speichern-Action wird genauso wie die anderen angelegt und erhält folgenden Code:
Delphi-Quellcode:
Nachdem nun auch dies geht, erstelle ich eine Action für Neu, die folgenden Code enthält.
if OpenDialog1.Filename<>’’ then
begin Richedit1.Lines.SavetoFile(OpenDialog1.Filename); end else begin if SaveDialog1.Filename<>’’ then begin Richedit1.Lines.SavetoFile(SaveDialog1.Filename); end else begin Aspeichernu.Execute; //falls kein Name zum Speichern vorhanden, Speichern unter aufrufen. end; Richedit1.Lines.Clear; An dieser Stelle möchte ich einschieben, dass es natürlich erforderlich wäre, zu überprüfen, ob Änderungen am Text vorgenommen wurden und dann den User zum speichern aufzufordern. Selbiges tritt beim Öffnen eines anderen Dokumentes auf oder beim Verlassen des Programms. Ich denke aber, dass das hier zu weit führt. Für Interessierte als Denkanstoß: Es gibt folgende Boolean-Variable: Richedit1.Modified. Zur Drucken-Funktion erstelle ich ebenfalls eine neue Action und weise ihr folgenden Code zu: Richedit1.Print(''); //der String in den Klammern dient als Überschrift über dem Ausdruck. Auch hier bleibt anzumerken, dass dieser Ausdruck der allereinfachste, aber auch am Schlimmsten aussehendste ist, da keine Zeilenumbrüche gemacht werden, in der obersten linken Ecke angefangen wird etc. Aber auch das würde zu weit führen, das richtig zu machen. (Auch hier bietet Delphi einen vorgefertigten Dialog an). Als Letztes fehlt im Menü Datei noch der Eintrag Beenden. Für ihn erzeuge ich keine Action, da er nur einmal verwendet wird. Ich klicke ihn an und gebe ein: MainForm.Close; Somit ist unser Datei-Menü vollständig. Im Menü bearbeiten verfahre ich genauso, indem ich Actions definiere (außer für Alles markieren, da sie auch nur einmal vorkommt). Die dabei zur Verwendung kommenden Funktionen sind folgende:
Delphi-Quellcode:
Auch hier fehlen natürlich Überprüfungen, ob überhaupt Text zum Ausschneiden markiert ist oder ob Rückgängig bzw. Wiederholen möglich sind, aber auch das führt zu weit.
Richedit1.Undo;
// Für Redo gibt es leider keine direkt implementierte Funktion, ich werde dies später aufgreifen. Richedit1.CuttoClipboard; //fürs Ausschneiden Richedit1.CopytoClipboard; // fürs Kopieren Richedit1.PasteFromClipboard; // fürs Einfügen Richedit1.Selectall; Als letzter großer Schritt fehlt jetzt die Schriftformatierung (also Fett, Kursiv, Schriftart etc.). Dazu stelle ich als Erstes die Eigenschaft Style der ToolButtons für Fett, Kursiv, Unterstrichen, Links-, Rechtsbündig und Zentriert auf tbscheck, was bewirkt, dass die Buttons sozusagen einrasten, wenn man sie drückt. Zusätzlich stelle ich bei dem Button für Linksbündig die Eigenschaft Down auf true, was bedeutet, dass der Button bereits bei Programmstart als gedrückt angesehen wird, da ja linksbündig die Standardeigenschaft im Richedit ist. Ich weise dabei den Toolbuttons folgenden Code zu:
Delphi-Quellcode:
Anstelle von fsBold verwende ich bei Kursiv fsItalic und bei Unterstreichen fsUnderline.
begin
if ToolButton16.Down then Richedit1.SelAttributes.Style:=Richedit1.SelAttributes.Style + [fsBold] else Richedit1.SelAttributes.Style:=Richedit1.SelAttributes.Style - [fsBold] end; Warum mit + und - ? Richedit1.SelAttributes.Style ist sozusagen eine Liste, die den Status des Zeichen speichert. Deswegen kann man ihr den entsprechenden Status zufügen bzw. wegnehmen. Die if-Abfrage stellt fest, ob es erforderlich ist, die Formatierung zu „entfernen“ oder dazu zu machen. Wie ihr sicherlich feststellen werdet fehlt etwas entscheidendes. Das Programm bekommt gar nicht mit, wenn man beim Bewegen des Cursors an eine Stelle im Text kommt, wo die Formatierung anders ist. Das ist relativ unschön. Also bauen wir in OnSelectionChange folgende Abfrage ein:
Delphi-Quellcode:
Sie überprüft, ob das Zeichen fett ist und setzt dementsprechend den Status des ToolButtons.
if fsBold in Richedit1.SelAttributes.Style then
begin ToolButton16.Down:=true; end else begin ToolButton16.Down:=false; end; Diese Abfrage mache ich ebenfalls für die beiden anderen Buttons. Um den Buttons zum Ausrichten des Textes Leben einzuhauchen verwende ich den folgenden Code: Richedit1.Paragraph.Alignment:=taLeftJustify; Für zentrierte Ausrichtung ist taCenter zuzuweisen, für rechtsbündig taRightJustify. Natürlich muss hier beachtet werden, dass die jeweils anderen Buttons nicht selektiert bleiben dürfen, deshalb dieser Code (anhand des Bsp. Left):
Delphi-Quellcode:
Auch hier ist wieder das Überprüfen des Status bei SelectionChange erforderlich:
ToolButton20.Down:=False;
ToolButton21.Down:=False;
Delphi-Quellcode:
case Richedit1.Paragraph.Alignment of
taLeftJustify: begin Toolbutton19.Down:=true; Toolbutton20.Down:=false; Toolbutton21.Down:=false; end; taCenter: begin Toolbutton20.Down:=true; Toolbutton19.Down:=false; Toolbutton21.Down:=false; end; taRightJustify: begin Toolbutton21.Down:=true; Toolbutton19.Down:=false; Toolbutton20.Down:=false; end; end; Nun zur Schriftgröße. Zuerst gebe ich bei Items der Combobox2 Zahlenwerte an, die später zu Verfügung stehen sollen und gebe bei Text 8 an (Standardeinstellung). Bei OnChange der Combobox gebe ich folgenden Code ein: Richedit1.SelAttributes.Size:=Strtoint(Combobox2.text); Er weist dem selektierten Zeichen die entsprechende Größe zu. Auch hier muss im OnSelectionChange wieder etwas hinzugefügt werden: Combobox2.Text:=Inttostr(Richedit1.SelAttributes.Size); Nun zur Schriftart. Um die Fonts in meine Combobox zu bekommen verwende ich folgenden Code von Swissdelphicenter.ch
Delphi-Quellcode:
function EnumFontClBack(var lp: TEnumLogFont;
var tm: TNewTextMetric; dwType: DWORD; lpData: lParam): Integer; stdcall; begin Result := 1; with TForm1(lpData), Combobox1 do begin Items.Add(lp.elfLogFont.lfFaceName); end; end;
Delphi-Quellcode:
Als Text weise ich der Combobox1 MS Sans Serif zu.
procedure TForm1.FormShow(Sender: TObject);
begin EnumFontFamilies(Canvas.Handle, nil, @EnumFontClBack, Integer(Self)); end; Auch hier der Code wieder bei OnChange: Richedit1.SelAttributes.Name:=Combobox1.Text; Und bei OnSelectionChange des Richedits: Combobox1.Text:=Richedit1.SelAttributes.Name; So, jetzt sind wir mit den Grundfunktionen durch. Was fehlt jetzt noch? Zuerst mal die Wiederholen-Funktion. Auch hier erstellen wir wieder eine Action und zwar mit folgendem Code: Richedit1.Perform(EM_REDO,0,0) // bei Uses Richedit einbinden Sie führt zwar das gewollte Wiederholen aus, aber wie bereits oben erwähnt nützt das aber auch nicht viel für einen reibungslosen Ablauf von Rückgängig/Wiederholen. Ich empfehle hier die Lektüre dieses Tutorials. 4. Ausblick Ich hoffe, ich konnte vielleicht dem ein oder anderen weiterhelfen. Bei sich bietender Gelegenheit werde ich das Tutorial weiterführen. Ciao fkerber Anhang: Die Projektdateien Edit: Fehler beseitigt (Danke an Markus K.) |
Turbo Delphi für Win32 |
#21
In Delphi ist es Problematisch Form-Proceduren so zu definieren...
Geh in deine Form und doppelkleicke im OI unter ereignisse Doppelt auf OnShow und kopier dann den Quelltext da rein
Nikolai Wyderka
|
Zitat |
Turbo Delphi für Win32 |
#22
Zitat von Gabberman:
Ich hab da mal ne Frage.
Ich hab folgenden Quellcode:
Delphi-Quellcode:
Mein Richeditfeld heißt 'schlusssatz'.
AssignFile(H, 'daten/schluss.fbe');
Rewrite(H); Writeln(H, :?: :?: :?: Closefile(H); Gibt es irgend eine Möglichkeit den Inhalt von diesem in der Datei H zu speichern???? Wäre schön wenn ihr mir helfen könntet!!! Entweder gehst du jede Zeile dursch und schreibst sie mit WriteLn rein, oder du packst alles in eine Zeile(sicherlich die unansehnlichere wariante) mit schlussatz.text
Nikolai Wyderka
|
Zitat |
|
#23
Also ich finde das ganze etwas karg
Schau dir mal die Demos bei der RX Library an,das Richedit Tool hat so viele Funktionen das du damit Wordpad kräftig einheizen kannst Ist ähnlich zu Programmieren und ziemlich alle funktionen die das Standard Richedit benutzt sind auch enthalten + Objekte Einbetten etc... Ich verwende die Library schon seit Delphi4 und bin sehr zufrieden mit dem Package an Komponenten. Die Lib ist zwar auch in dem Jedi Pack dabei,der ist aber recht riskant zu Installieren da Delphi schnell mal anzeigt das keine Komponenten mehr Registriert sind. |
Zitat |
|
#24
Wollt mal fragen, ob das auch mit SynEdit geht?
Wenn ich in der Toolbar dem Button für BOLD folgendes zuweise, sagt Delphi:
Delphi-Quellcode:
[Error] Main.pas(348): Undeclared identifier: 'SelAttributes'
procedure TForm2.ToolButton18Click(Sender: TObject);
begin if ToolButton18.Down then SynEdit1.SelAttributes.Style:=SynEdit1.SelAttributes.Style + [fsBold] else SynEdit1.SelAttributes.Style:=SynEdit1.SelAttributes.Style - [fsBold] end; [Error] Main.pas(348): Missing operator or semicolon und was mir aufgefallen ist, wenn ich die fonts in die combobox lade, sind die völlig ungeordnet. Also nicht von A nach Z |
Zitat |
|
#26
Zitat von Störtebeker:
Wollt mal fragen, ob das auch mit SynEdit geht?
Wenn ich in der Toolbar dem Button für BOLD folgendes zuweise, sagt Delphi:
Delphi-Quellcode:
[Error] Main.pas(348): Undeclared identifier: 'SelAttributes'
procedure TForm2.ToolButton18Click(Sender: TObject);
begin if ToolButton18.Down then SynEdit1.SelAttributes.Style:=SynEdit1.SelAttributes.Style + [fsBold] else SynEdit1.SelAttributes.Style:=SynEdit1.SelAttributes.Style - [fsBold] end; [Error] Main.pas(348): Missing operator or semicolon und was mir aufgefallen ist, wenn ich die fonts in die combobox lade, sind die völlig ungeordnet. Also nicht von A nach Z Kenne die Komponente nicht,bin mit der von der RXLib zufrieden auch wenns jetzt auch schon RTF3.0 gibt Nur mal so ne Frage,warum willst du überhaupt noch einen Texteditor Bauen? Es gibt soooooooo viele Editoren wie Word und Kompatible,Quellcode Editoren für alle möglichen Languages usw.... Wenn du das Proggen Lernen willst würd ich dir empfehlen die Grundlagen zu den Technologien selbst zu erarbeiten mit einem möglichst Simplen Editor wie die RTF Komponente von Delphi und einem Guten Buch! Würde es dir jedenfalls Empfehlen den auch die Freien Komponenten haben so Ihre Macken und man muss schnell mal einen Workarround schreiben Beim Toolbutton fängts Beispielsweise schon an Weil toolbutton.down:=not Toolbutton.down funzt nämlich so garnicht,warum auch immer. Ein Trick wäre Z.B down als Externe Boolean Variable auszulagern.denn so wie du das vorhast wird das nix. |
Zitat |
|
#27
Habe ein Problem damit:
Delphi-Quellcode:
Und dann steht als Fehler das hier:
procedure TMainForm.AoeffnenExecute(Sender: TObject);
begin If OpenDialog1.Execute then begin if FileExists(OpenDialog1.Filename) then begin Richedit1.Lines.LoadfromFile(OpenDialog1.Filename); end; end. [Pascal Fehler]Mainfrm.pas(43): E2003 Undifinierter Bezeichner:'OpenDialog1' Was soll ich machen? |
Zitat |
|
#29
daran lag es aber jetzt kommt:
der rote strich bei end; hier kommt der nun end; also beim zweiten end; das letzt im Quelltext und der meint: [Pascal Fehler] Mainfrm.pas(53):E2029 Anweisung erwartet, aber Dateiende gefunden [Pascal Fataler Fehler] Project2.dpr(5): F2063 Verwendete Unit 'Mainfrm.pas' kann nicht compiliert werden |
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
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 |
LinkBack URL |
About LinkBacks |