Visual Component Library
In Delphi 2 nicht gekapselte TListView-Styles
Kann mir jemand sagen, wie ich in einem ListView die ganze Zeile selektiert angezeigt werden kann? (nicht nur den Eintrag in der 1. Kolonne)? Es handelt sich vermutlich wieder um einen versteckten API-Befehl.
In Delphi haben die Borländer solche Sachen einfach vergessen. :-) Gib in deiner FormCreate-Routine folgendes ein:
SendMessage(ListView.Handle,$1036,0,LVS_EX_FULLROWSELECT); |
wobei du LVS_EX_FULLROWSELECT als Konstante mit einem Wert von $20 deklarieren mußt.
Es gibt auch noch andere schöne, nicht von Delphi unterstützte Styles:
LVS_EX_GRIDLINES = $1; LVS_EX_SUBITEMIMAGES = $2; LVS_EX_CHECKBOXES = $4; LVS_EX_TRACKSELECT = $8; LVS_EX_HEADERDRAGDROP = $10; LVS_EX_FULLROWSELECT = $20; LVS_EX_ONECLICKACTIVATE = $40; LVS_EX_TWOCLICKACTIVATE = $80; { folgende erst ab Internet Explorer 4.0 } LVS_EX_FLATSB = $100; LVS_EX_REGIONAL = $200; LVS_EX_INFOTIP = $400; LVS_EX_UNDERLINEHOT = $800; LVS_EX_UNDERLINECOLD = $1000; LVS_EX_MULTIWORKAREAS = $2000;
Du mußt dabei nur beachten, daß du die Styles mit OR verknüpfst z.B:
SendMessage(ListView.Handle,$1036,0,LVS_EX_FULLROWSELECT or LVS_EX_HEADERDRAGDROP); |
Den Signalton bei Eingabe von [Enter] in einem Edit-Feld uterdrücken
Eine Eingabe soll in einem Edit-Feld durch [Enter] bestätigt werden. Dabei ertönt jedesmal, das unter "Akustische Signale" (Systemsteuerung) eingestellte, sogenannte Standardsignal oder aber ein "Plopp" aus dem PC-Speaker. Wie kann man das abstellen?
Zeilenumbrüche in einzeiligen Eingabefeldern sind logischerweise nicht erlaubt und deshalb ertönt bei Eingabe von [Enter] ein Fehlerton. Fang den Tastendruck im "OnKeyPress"-Handler des Edit-Controls ab und lösche ihn bei Bedarf (nämlich wenn es die [Enter]-Taste ist):
if Key=#13 then // #13 = [Enter] Key:=#0; |
Wie ermittle ich die Cursorposition (Zeile und Spalte) in einem TRichEdit?
Hier eine Prozedur, die die Zeile und Spalte des Textcursors ermittelt:
procedure TMDIChild.PositionAnzeigen; var CurLine, CurCol: Integer; x, y: string; begin { Memo ist entw. TMemo oder TRichEdit } CurLine := SendMessage (Memo.Handle, EM_LINEFROMCHAR, Memo.SelStart, 0); CurCol := SendMessage (Memo.Handle, EM_LINEINDEX, CurLine, 0); CurCol := Memo.SelStart - CurCol; Inc (CurLine); Inc (CurCol); str(CurLine, x); str(CurCol, y); StatusBar1.Panels[0].Text := x + ' : ' + y; {Ausgabe in Statuszeile} end; |
Wie kann ich zur ersten oder letzten Zeile im Memo scrollen?
Dafür gibt es die EM_LineScroll-Nachricht:
{zur ersten Zeile:} Memo1.Perform(EM_LineScroll, 0 , -Memo1.Lines.Count-1); {zur letzen Zeile:} Memo1.Perform(EM_LineScroll, 0 , Memo1.Lines.Count-1); |
Memo1.Perform(EM_ScrollCaret, 0, 0); |
Wie fügt man an einer bestimmten Position Text in ein TMemo oder TRichEdit ein?
Die Einfügeposition wird mit der Eigenschaft SelStart festgelegt. Der einzufügende Text wird der Eigenschaft SelText übergeben.
Mit der Eigenschaft SelLength kann man die Länge des markierten Textes im Memo festlegen. SelLength muß auf Null gesetzt werden, um keinen Text im Memo zu überschreiben:
Memo.SelStart:=Einfuegeposition; Memo.SelLength:=0; Memo.SelText:='Einzufügender Text'; |
Wie wechselt man in einem TMemo zwischen Einfüge- und Überschreibmodus?
TEdits, TMemos oder TRichEdits bieten von Hause aus keinen Überschreibmodus, d.h. wenn man mit der Tastatur Buchstaben eintippt, werden diese immer in den Text an der aktuellen Einügeposition (Memo.SelStart) eingefügt. Der folgende Text wird dabei um eine Position nach hinten gerückt.
Um wahlweise einen Überschreibmodus zu bieten, bei dem das erste Zeichen an der aktuellen Einfügeposition durch den neu eingetippten Buchstaben ersetzt wird, kann man so vorgehen:
Man erstellt eine TForm (Form1) und darauf z.B. ein TMemo (Memo1) und eine TCheckBox (CheckBox1), mit der man zwischen zwischen Einfüge- und Überschreibmodus wechseln kann. Dann weist man dem OnKeyPress-Ereignis des Memos folgende Methode zu:
procedure TForm1.Memo1KeyPress(Sender: TObject; var Key: Char); var Zeile : string; CursorPos : integer; begin if CBUeberschreiben.Checked then begin if Key<#32 then Key:=#0 else begin {Aktuelle Einfügeposition merken} CursorPos:=Memo1.SelStart; {Memotext in einer string-Variablen zwischenspeichern} Zeile:=Memo1.Text; {Falls sich an der aktuellen Cursorposition ein Windows-Zeilenumbruch befindet, müssen zwei Zeichen (#13=CR und #10=LF) gelöscht werden:} if (Zeile[CursorPos+1]=#13) and (Zeile[CursorPos+2]=#10) then delete(Zeile,CursorPos+1,1); {Das Zeichen an der Einfügeposition löschen} delete(Zeile,CursorPos+1,1); {Memotext aus der string-Variablen zurückladen} Memo1.Text:=Zeile; Memo1.SelStart:=CursorPos; end; end; end; |
procedure TForm1.Memo1KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); begin {mit der [Einfg]-Taste zwischen Einfüge- und Überschreibmodus wechseln} if (Key=VK_Insert) and (Shift=[]) then CBUeberschreiben.Checked:=not CBUeberschreiben.Checked; end; |
Den blinkenden Cursor in einem deaktivierten TEdit oder TMemo verstecken
Wenn ich ein TEdit, TMemo oder TRichEdit deaktiviere, bleibt trotzdem ein blinkender Cursor im jeweiligen Control, der den Eindruck vermittelt, man könne noch Eingaben machen. Da das aber nicht möglich ist, möchte ich auch keinen Cursor haben. Wie kann man den abstellen?
Probier folgendes:
{Cursor verstecken} HideCaret(Memo1.Handle); {Cursor verstecken} ShowCaret(Memo1.Handle). |
Falls es generell ohne Cursor sein sollte, kannst Du ja eine Ableitung von TMemo bilden, die den Cursor nach dem OnEnter rauswirft. [Volker Heinrich]
Die maximale Textlänge eines TRichEdit erhöhen
Wenn ich zu einem RichEdit mittels Lines.Add Zeilen hinzufüge, ist irgendwann Schluß. Hat auch die TRichEdit-Komponente (wie TMemo) eine Maximallänge?
Im Prinzip nicht. Die Längenbeschränkung bei TRichEdit ist jedenfalls jenseits normalerweise nutzbarer Grenzen. Die RichEdit-Komponente besitzt aber wie auch TMemo und TEdit die Eigenschaft MaxLength, die die maximale Textlänge begrenzt. Per Voreinstellung hat diese immer den Wert "0". Das wird sowohl für TEdit und TMemo, als auch für TRichEdit als Maximalgröße von 32 Kilobyte interpretiert.
Nach der Erstellung kann ein RichEdit also erstmal nicht größer werden, als ein TMemo. Während bei diesem die 32kB aber bereits den höchtmöglichen Wert darstellen (zumindest bis Delphi 3), kann man der MaxLength-Eigenschaft der RichEdit-Komponente einfach einen höheren Wert zuweisen:
RichEdit1.MaxLength:=2147483647; //damit kann das Teil 2^31 Byte groß werden. |
Vorsicht, Richedits mit sehr großen Texten neigen zu schneckenhaftem Verhalten!
Wie kann ich mit der HTML-Komponente in Delphi 4 ein lokales Dokument öffnen?
Ich habe es folgendermaßen versucht: "HTML1.RequestDoc('file://F:\index.htm');", das funktioniert aber leider nicht.
Probier es mal so:
HTML1.RequestDoc('file:///F:\index.htm'); //oder: HTML1.RequestDoc('file://localhost/F:\index.htm'); |
Der dritte Schrägstrich steht einfach für LocalHost
Wie kann man bei einer Grid-Komponente für jede Zelle einen anderen Hint anzeigen?
Ich möchte für jede Zelle eines StringGrids einen eigenen Hint anzeigen. Der Hinweistext wird aber erst aktualisiert, wenn der Mauszeiger das Grid verlässt. Wie kann ich einen neuen Hinweis anzeigen, wenn der Mauszeiger über eine neue Zelle bewegt wird?
Damit der Hint wieder auftaucht, muß man nur Application.CancelHint aufrufen - die MouseMove-Methode sieht dann wie folgt aus:
var LastCol, LastRow : longint; procedure TForm1.StringGridMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); var ACol, ARow: longint; begin StringGrid.MouseToCell(X, Y, ACol, ARow); StringGrid.Hint:='Dieser Hinweis gilt nur für die Zelle ' +IntToStr(ACol)+':'+IntToStr(ARow); if (ACol<>LastCol) or (ARow<>LastRow) then begin Application.CancelHint; LastCol:=ACol; LastRow:=ARow; end; end; |
Wie kann man in einer StringGrid-Komponente mehrzeiligen Text ausgeben?
Dazu muß man das Zeichnen der Zelleninhalte selber übernehmen. Zu diesem Zweck schreibt man eine Methode für das OnDrawCell-Ereignis des StringGrids. In dieser Methode kann man direkt auf das Ausgaberechteck der jeweiligen Zelle zugreifen, es wird in der Variablen "Rect" übergeben.
Für die Ausgabe von mehrzeiligem Text ist "DrawText" ist die Funktion der Wahl. Mit dem Parameter DT_CalcRect kann man die Größe des Ausgaberechtecks ermitteln, der Parameter DT_WordBreak sorgt für den Zeilenumbruch.
Hier mal ein paar Beispielzeilen aus einer Druckroutine eines meiner Programme:
outRect:=Rect; {Dimension des Ausgaberechtecks ermitteln (wird in outRect zurückgegeben):} DrawText(StringGrid.Canvas.Handle, PCHar(SText), length(SText), outRect, DT_CalcRect or DT_WordBreak or DT_NoClip); {Text mehrzeilig und horizontal zentriert im Recteck outRect ausgeben:} DrawText(StringGrid.Canvas.Handle, PChar(SText), length(SText), outRect, DT_Center or DT_WordBreak); {Text einzeilig und zentriert im Recteck outRect ausgeben:} DrawText(StringGrid.Canvas.Handle, PChar(SText), length(SText), outRect, DT_VCenter or DT_Center or DT_SingleLine); |
Wie kann man in einer ListBox eine horizontale Scrollbar anzeigen?
Man sendet eine "LB_SetHorizontalExtent"-Nachricht an die ListBox. Das kann man z.B. in der
OnCreate-Methode des übergeordneten Formulars machen::
procedure TForm1.FormCreate(Sender: TObject); begin SendMessage(Listbox1.Handle, LB_SetHorizontalExtent, 1000, Longint(0)); end; |
Wie kann man in einem TreeView einen bestimmten Knoten suchen?
Aufgrund der Datenstruktur eines TreeViews bieten sich bei der Arbeit mit TreeNodes grundsätzlich rekursive Routinen an. Diese Routine durchsucht alle Kinder eines vorgegebenen Knotens "Root" rekursiv nach einem Knoten mit dem gesuchten Text "Name":
function FindNode(Root: TTreeNode; Name: string): TTreeNode; var Temp: TTreeNode; begin Result := Root.GetFirstChild; while Result <> nil do begin if Result.Text = Name then Exit; Temp := FindNode(Result, Name); if Temp <> nil then begin Result := Temp; Exit; end; Result := Root.GetNextChild(Result); end; end; {Robert Roßmair} |
Die Hints in einem TreeView abschalten
Ich möchte gerne die automatische Hint-Anzeige eines TreeViews abschalten, also die "Tooltips", die erscheinen, wenn ein Node nicht vollständig im Fenster angezeigt wird. Wer weiß welche Möglichkeiten ich habe?
Ab Delphi4 hat TTreeView dafür die Eigenschaft "Tooltips". In Delphi3 wurde diese Eigenschaft nicht gekapselt, man muß sich deshalb mit der API-Funktion "SetWindowLong" behelfen:
const TVS_NoTooltips = $80; begin with TreeView1 do SetWindowLong(Handle, GWL_Style, GetWindowLong(Handle, GWL_Style) or TVS_NoTooltips); end; {Michael Hanel} |
Wie kann man andere Komponenten auf einer TStatusBar plazieren?
TStatusBar bietet von sich aus keine Möglichkeit, andere Komponenten auf ihr zu plazieren. In vielen Programmen ist das aber eine gern genutzte Möglichkeit, um Zusatzinformationen wie z.B. eine Fortschrittanzeige anzuzeigen.
Es gibt zwei Möglichkeiten, trotzdem Komponenten auf einer StatusBar zu plazieren. Die erste Möglichkeit ist, zur Laufzeit die StatusBar als Parent einer Komponente festzulegen. Folgendes Beispiel positioniert eine TProgressBar exakt in eines der angegebenen Panels einer Statusbar:
procedure TForm1.FormShow(Sender: TObject); var ARect : TRect; begin Statusbar1.Perform(SB_GetRect, 0, integer(@ARect)); {SB_GetRect benötigt die Unit CommCtrl 0 = das erste Panel der Statusbar; 1 = das Zweite usw. in ARect wird das Anzeigerechteck zurückgegeben} with ProgressBar1 do begin Top := ARect.Top; Left := ARect.Left; Width := ARect.Right-ARect.Left; Height := ARect.Bottom-ARect.Top; Parent := Statusbar1; end; end; {Alex Schlecht} |
unit FreundlicheStatusBar; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, DsgnIntf; type TFriendlyStatusBar = class(TStatusBar) public constructor Create(AOwner: TComponent); override; end; procedure Register; implementation constructor TFriendlyStatusBar.Create(AOwner: TComponent); begin inherited Create(AOwner); ControlStyle:= ControlStyle + [csAcceptsControls]; end; procedure Register; begin RegisterComponents('FreeWare', [TFriendlyStatusBar]); end; end. |
Wie man die Anzeige des Fokus-Rechtecks bei OwnerDraw-Listboxen verhindert
Ich benutze eine TListBox im Ownerdraw-Modus. Da habe ich mir nun viel Mühe gegeben, um auch ja hübsche Programmoberfläche zu haben und dann kommt Windows und knallt mir einfach sein Focus-Rechteck auf den aktiven Eintrag meiner TListBox. Wie kann ich das verhindern?
Das Problem liegt in TCustomListBox.CNDrawItem. Diese Methode fängt die CN_DRAWITEM messages ab und zeichnet einen Eintrag, wenn einer der OwnerDraw-Styles ausgewählt ist. Du mußt eine eigene Komponente von TListbox ableiten und diese Methode überschreiben. Da sie in TCustomListBox private ist, bleibt
dir nichts anderes übrig, als den Quelltext der Methode zu kopieren und komplett in deine neue Komponente zu übernehmen. Du mußt in dem kopierten Quelltext alle Verweise auf private Felder in Verweise auf Eigenschaften ändern und die Zeile "if odFocused in State then DrawFocusRect(hDC, rcItem);" auskommentieren.
Die Komponente könnte also etwa so aussehen, wie in diesem Beispiel von Oliver Stör:
interface TMyListbox = class(TListbox) private procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM; end; implementation procedure TMyListBox.CNDrawItem(var Message: TWMDrawItem); var State: TOwnerDrawState; begin with Message.DrawItemStruct^ do begin State := TOwnerDrawState(LongRec(itemState).Lo); Canvas.Handle := hDC; Canvas.Font := Font; Canvas.Brush := Brush; if (Integer(itemID) >= 0) and (odSelected in State) then begin Canvas.Brush.Color := clHighlight; Canvas.Font.Color := clHighlightText end; if Integer(itemID) >= 0 then DrawItem(itemID, rcItem, State) else Canvas.FillRect(rcItem); // if odFocused in State then DrawFocusRect(hDC, rcItem); Canvas.Handle := 0; end; end; |
Eine Liste aller VCL Component Messages und Component Notifications
Component Messages (CM_) sind Nachrichten, die ausschließlich von der VCL generiert werden, Component Notifications (CN_) sind dagegen reflektierte Windows Messages.
Der Sinn ist, dass Windows oft Nachrichten an das Elternfenster eines Controls anstelle des Controls selbst versendet. Die VCL konvertiert diese einfach zu Component Notifications und sendet sie dann wieder an das Control für das die Message eigentlich bestimmt war.
Mike Lischke hat eine Liste aller Component Messages und Component Notifications der Delphi-VCL zusammengestellt. Ich habe diese Liste zur besseren Übersicht formatiert und man kann sie als Dokument im Rich Text Format (25 kB) laden.