Components
From: Lode Deleu <101612.1454@compuserve.com>
> Is it possible to create an array of components?
I'm using a LED component for a status display, and I'd like to be able to access it via:
First of all, you'l need to declare the array:
LED : array[1..10] of TLed; (TLed being your led component type)
if you would create the LED components dynamically, you could do this during a loop like this :
for counter := 1 to 10 do begin LED[counter]:= TLED.Create; LED[counter].top := ... LED[counter].Left := ... LED[counter].Parent := Mainform; {or something alike} end;
If the components already exist on your form (visually designed), you could simply assign them to the array like this:
leds := 0; for counter := 0 to Form.Componentcount do begin if (components[counter] is TLED) then begin inc(leds); LED[leds] := TLED(components[counter]); end end;
This however leaves you with a random array of LED's, I suggest you give each LED a tag in the order they should be in the array, and then fill the array using the tag :
for counter := 0 to Form.Componentcount do begin if (components[counter] is TLED) then begin LED[Component[counter].tag] := TLED(components[counter]); end end;
if you need a two dimensional array, you'll need to find another trick to store the index, I've used the hint property a number of times to store additional information.
From: baisa@tor.hookup.net (Brad Aisa)
In article <4uevrf$331@duke.telepac.pt>, delphinidae@mail.telepac.pt (Claudio Tereso) wrote:
>i need to find the component index in the parent's order.
>i tried to modify prjexp.dll but with success?
>does any one have an idea?
Here is a function that does this. It gets the parent control, and then iterates through its children, looking for a match. This has been tested and works.
{ function to return index order of a component in its parent's component collection; returns -1 if not found or no parent } function IndexInParent(vControl: TControl): integer; var ParentControl: TWinControl; begin {we "cousin" cast to get at the protected Parent property in base class } ParentControl := TForm(vControl.Parent); if (ParentControl <> nil) then begin for Result := 0 to ParentControl.ControlCount - 1 do begin if (ParentControl.Controls[Result] = vControl) then Exit; end; end; { if we make it here, then wasn't found, or didn't have parent} Result := -1; end;
From: m.a.vaughan@larc.nasa.gov (Mark Vaughan)
Var MyButton : TButton; MyButton := TButton.Create(MyForm); // MyForm now "owns" MyButton with MyButton do BEGIN Parent := MyForm; // here MyForm is also the parent of MyButton height := 32; width := 128; caption := 'Here I Am!'; left := (MyForm.ClientWidth - width) div 2; top := (MyForm.ClientHeight - height) div 2; END;
Borland also publishes one of their TechInfo sheets on this subject.
Look for
ti2938.asc Creating Dynamic Components at Runtime
which you can get from Borland's web site or ftp site.
From: "Hustin Olivier" <ohu@eortc.be>
Definition of memo's properties
memo.onchange:=memo1Change; procedure TForm1.Memo1Change(Sender: TObject); begin panel1.caption:='Content has been changed'; end;
From: Mark Pritchard <pritchma@ozemail.com.au>
Here is a free one (took around half an hour to put together, it doesn't grab the parent font correctly, but I couldn't be bothered putting any more time into it) -
unit IDSLabel; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls; type TIDSLabel = class(TBevel) private { Private declarations } FAlignment : TAlignment; FCaption : String; FFont : TFont; FOffset : Byte; FOnChange : TNotifyEvent; procedure SetAlignment( taIn : TAlignment ); procedure SetCaption( const strIn : String); procedure SetFont( fntNew : TFont ); procedure SetOffset( bOffNew : Byte ); protected { Protected declarations } constructor Create( compOwn : TComponent ); override; destructor Destroy; override; procedure Paint; override; public { Public declarations } published { Published declarations } property Alignment : TAlignment read FAlignment write SetAlignment default taLeftJustify; property Caption : String read FCaption write SetCaption; property Font : TFont read FFont write SetFont; property Offset : Byte read FOffset write SetOffset; property OnChange : TNotifyEvent read FOnChange write FOnChange; end; implementation constructor TIDSLabel.Create; begin inherited Create(compOwn); FFont := TFont.Create; with compOwn as TForm do FFont.Assign(Font); Offset := 4; Height := 15; end; destructor TIDSLabel.Destroy; begin FFont.Free; inherited Destroy; end; procedure TIDSLabel.Paint; var wXPos, wYPos : Word; begin {Draw the bevel} inherited Paint; {Retreive the font} Canvas.Font.Assign(Font); {Calculate the y position} wYPos := (Height - Canvas.TextHeight(Caption)) div 2; {Calculate the x position} wXPos := Offset; case Alignment of taRightJustify: wXPos := Width - Canvas.TextWidth(Caption) - Offset; taCenter: wXPos := (Width - Canvas.TextWidth(Caption)) div 2; end; Canvas.Brush := Parent.Brush; Canvas.TextOut(wXPos,wYPos,Caption); end; procedure TIDSLabel.SetAlignment; begin FAlignment := taIn; Invalidate; end; procedure TIDSLabel.SetCaption; begin FCaption := strIn; if Assigned(FOnChange) then FOnChange(Self); Invalidate; end; procedure TIDSLabel.SetFont; begin FFont.Assign(fntNew); Invalidate; end; procedure TIDSLabel.SetOffset; begin FOffset := bOffNew; Invalidate; end; end.
From: Mark Pritchard <pritchma@ozemail.com.au>
In the OnSelectCell event, this works fine (every even column is editable) -
if Col mod 2 = 0 then grd.Options := grd.Options + [goEditing] else grd.Options := grd.Options - [goEditing];
From: Eddie Shipman <eshipman@slip.net>
Found the answer on my own on Delphi WWW Forum.
Var ScrollMessage:TWMVScroll; ScrollMessage.Msg:=WM_VScroll; for i := Memo1.Lines.Count DownTo 0 do begin ScrollMessage.ScrollCode:=sb_LineUp; ScrollMessage.Pos:=0; Memo1.Dispatch(ScrollMessage); end;
From: "James D. Rofkar" <jim_rofkar%lotusnotes1@instinet.com>
Darren Clements wrote: > How can I put a Bitmap in a StringGrid cell?
In your StringGrid's OnDrawCell event handler, place some code that resembles:
with StringGrid1.Canvas do begin {...} Draw(Rect.Left, Rect.Top, Image1.Picture.Graphic); {...} end;
Using the Draw() or StretchDraw() method of TCanvas should do the trick. BTW, Image1 above is a TImage with a bitmap already loaded into it.
Haakon Eines <haakon.eines@finale.no>
Here's a little TreeView-component that might be a little bit faster than the default TTreeView from Borland. You also have the ability to set the items text to bold (It's implemented as methods of the treeview, but should have been a TTreeNode's property. Since I can't release VCL source code - methods it is).
Some timings: TTreeView: 128 sec. to load 1000 items (no sorting)* 270 sec. to save 1000 items (4.5 minutes!!!) THETreeView: 1.5 sec. to load 1000 items - about 850% faster!!! (2.3 seconds with sorting = stText)* 0.7 sec. to save 1000 items - about 3850% faster!!!NOTES:
unit HETreeView; {$R-} // Made by: HÃ¥kon Eines // EMail: haakon.eines@finale.no // Date: 21.01.1997 // Description: A Speedy TreeView? (* TTREEVIEW: 128 sec. to load 1000 items (no sorting)* 270 sec. to save 1000 items (4.5 minutes!!!) THETREEVIEW: 1.5 sec. to load 1000 items - about 850% faster!!! (2.3 seconds with sorting = stText)* 0.7 sec. to save 1000 items - about 3850% faster!!! NOTES: - All timings performed on a slow 486SX 33 MhZ, 20 Mb RAM. - * If the treeview is empty, loading takes 1.5 seconds, else add 1.5 seconds to clear 1000 items (a total loading time of 3 seconds). This is also the case for the TTreeView component (a total of 129.5 seconds). The process of clearing the items, is a call to SendMessage(hwnd, TVM_DELETEITEM, 0, Longint(TVI_ROOT)). *) interface uses SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, CommCtrl, tree2vw; type THETreeView = class(TTreeView) private FSortType: TSortType; procedure SetSortType(Value: TSortType); protected function GetItemText(ANode: TTreeNode): string; public constructor Create(AOwner: TComponent); override; function AlphaSort: Boolean; function CustomSort(SortProc: TTVCompare; Data: Longint): Boolean; procedure LoadFromFile(const AFileName: string); procedure SaveToFile(const AFileName: string); procedure GetItemList(AList: TStrings); procedure SetItemList(AList: TStrings); //'Bold' should have been a property of TTreeNode, but... function IsItemBold(ANode: TTreeNode): Boolean; procedure SetItemBold(ANode: TTreeNode; Value: Boolean); published property SortType: TSortType read FSortType write SetSortType default stNone; end; procedure Register; implementation function DefaultTreeViewSort(Node1, Node2: TTreeNode; lParam: Integer): Integer; stdcall; begin {with Node1 do if Assigned(TreeView.OnCompare) then TreeView.OnCompare(Node1.TreeView, Node1, Node2, lParam, Result) else} Result := lstrcmp(PChar(Node1.Text), PChar(Node2.Text)); end; constructor THETreeView.Create(AOwner: TComponent); begin inherited Create(AOwner); FSortType := stNone; end; procedure THETreeView.SetItemBold(ANode: TTreeNode; Value: Boolean); var Item: TTVItem; Template: Integer; begin if ANode = nil then Exit; if Value then Template := -1 else Template := 0; with Item do begin mask := TVIF_STATE; hItem := ANode.ItemId; stateMask := TVIS_BOLD; state := stateMask and Template; end; TreeView_SetItem(Handle, Item); end; function THETreeView.IsItemBold(ANode: TTreeNode): Boolean; var Item: TTVItem; begin Result := False; if ANode = nil then Exit; with Item do begin mask := TVIF_STATE; hItem := ANode.ItemId; if TreeView_GetItem(Handle, Item) then Result := (state and TVIS_BOLD) <> 0; end; end; procedure THETreeView.SetSortType(Value: TSortType); begin if SortType <> Value then begin FSortType := Value; if ((SortType in [stData, stBoth]) and Assigned(OnCompare)) or (SortType in [stText, stBoth]) then AlphaSort; end; end; procedure THETreeView.LoadFromFile(const AFileName: string); var AList: TStringList; begin AList := TStringList.Create; Items.BeginUpdate; try AList.LoadFromFile(AFileName); SetItemList(AList); finally Items.EndUpdate; AList.Free; end; end; procedure THETreeView.SaveToFile(const AFileName: string); var AList: TStringList; begin AList := TStringList.Create; try GetItemList(AList); AList.SaveToFile(AFileName); finally AList.Free; end; end; procedure THETreeView.SetItemList(AList: TStrings); var ALevel, AOldLevel, i, Cnt: Integer; S: string; ANewStr: string; AParentNode: TTreeNode; TmpSort: TSortType; function GetBufStart(Buffer: PChar; var ALevel: Integer): PChar; begin ALevel := 0; while Buffer^ in [' ', #9] do begin Inc(Buffer); Inc(ALevel); end; Result := Buffer; end; begin //Delete all items - could have used Items.Clear (almost as fast) SendMessage(handle, TVM_DELETEITEM, 0, Longint(TVI_ROOT)); AOldLevel := 0; AParentNode := nil; //Switch sorting off TmpSort := SortType; SortType := stNone; try for Cnt := 0 to AList.Count-1 do begin S := AList[Cnt]; if (Length(S) = 1) and (S[1] = Chr($1A)) then Break; ANewStr := GetBufStart(PChar(S), ALevel); if (ALevel > AOldLevel) or (AParentNode = nil) then begin if ALevel - AOldLevel > 1 then raise Exception.Create('Invalid TreeNode Level'); end else begin for i := AOldLevel downto ALevel do begin AParentNode := AParentNode.Parent; if (AParentNode = nil) and (i - ALevel > 0) then raise Exception.Create('Invalid TreeNode Level'); end; end; AParentNode := Items.AddChild(AParentNode, ANewStr); AOldLevel := ALevel; end; finally //Switch sorting back to whatever it was... SortType := TmpSort; end; end; procedure THETreeView.GetItemList(AList: TStrings); var i, Cnt: integer; ANode: TTreeNode; begin AList.Clear; Cnt := Items.Count -1; ANode := Items.GetFirstNode; for i := 0 to Cnt do begin AList.Add(GetItemText(ANode)); ANode := ANode.GetNext; end; end; function THETreeView.GetItemText(ANode: TTreeNode): string; begin Result := StringOfChar(' ', ANode.Level) + ANode.Text; end; function THETreeView.AlphaSort: Boolean; var I: Integer; begin if HandleAllocated then begin Result := CustomSort(nil, 0); end else Result := False; end; function THETreeView.CustomSort(SortProc: TTVCompare; Data: Longint): Boolean; var SortCB: TTVSortCB; I: Integer; Node: TTreeNode; begin Result := False; if HandleAllocated then begin with SortCB do begin if not Assigned(SortProc) then lpfnCompare := @DefaultTreeViewSort else lpfnCompare := SortProc; hParent := TVI_ROOT; lParam := Data; Result := TreeView_SortChildrenCB(Handle, SortCB, 0); end; if Items.Count > 0 then begin Node := Items.GetFirstNode; while Node <> nil do begin if Node.HasChildren then Node.CustomSort(SortProc, Data); Node := Node.GetNext; end; end; end; end; //Component Registration procedure Register; begin RegisterComponents('Win95', [THETreeView]); end; end.
"David Zajac" <dzajac@HiWAAY.net>
Keep in mind that when a property is an object, it has memory associated with it.When you are changing the value of a bitmap property, somehow the memory associated with the old value has to be freed, and new memory allocated. By convention in Delphi, that's what an "Assign" method does. The code below works.
implementation {$R *.DFM} var n: integer; // It'll be zero when the program starts procedure TForm1.Button1Click(Sender: TObject); var Image: TBitmap; begin // Changes the bitmap in BitBtn1 Image:= TBitmap.Create; if n < ImageList1.Count then ImageList1.GetBitmap(n, Image); {end if} BitBtn1.Glyph.Assign(Image) // NOTE: Assign is used to change an object property inc(n,2); // Button Bitmaps hold two images! if n > ImageList1.Count then n:= 0; {end if} Image.Free; end; procedure TForm1.Button2Click(Sender: TObject); begin // adds a new button bitmap to ImageList1 if OpenDialog1.Execute then ImageList1.FileLoad(rtBitMap,OpenDialog1.FileName,clBtnFace); label1.Caption:= 'ImageCount = ' + IntToStr(ImageList1.Count); end;
From: Chris Jobson <chrisj@rcp.co.uk>
Just write an OnDrawPanel handler for the StatusBar something like
procedure TForm1.StatusBar1DrawPanel(StatusBar: TStatusBar; Panel: TStatusPanel; const Rect: TRect); begin with statusbar1.Canvas do begin Brush.Color := clRed; FillRect(Rect); TextOut(Rect.Left, Rect.Top, 'Panel '+IntToStr(Panel.Index)); end; end;
Gary McGhee <gmcghee@wt.com.au>
The following code provides a function called DuplicateComponents that duplicates any given component and its child components at run time. It tries to emulate copying and pasting a component at design time. The new component is created with the same parentage and owner as the original and all new component names are similar (but different) to their original. This is provided as is and may have bugs that I haven't found yet. It is provided because it contains techniques that are not commonly known and may be of use to people struggling with similar problems.This procedure is very useful when you want to design a section of an interface once that will appear n times at run time. You just design it once visually all on a TPanel or other component as a parent, and then do "newpanel := DuplicateComponents(designedpanel)".
uses SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls, IniFiles, TypInfo, Debug; type TUniqueReader = Class(TReader) LastRead: TComponent; procedure ComponentRead(Component: TComponent); procedure SetNameUnique( Reader: TReader; Component: TComponent; var Name: string ); end; implementation procedure TUniqueReader.ComponentRead( Component: TComponent ); begin LastRead := Component; end; procedure TUniqueReader.SetNameUnique( // sets the name of the read component to something like "Panel2" if "Panel1" already exists Reader: TReader; Component: TComponent; // component being read var Name: string // Name to use and modify ); var i: Integer; tempname: string; begin i := 0; tempname := Name; while Component.Owner.FindComponent(Name) <> nil do begin Inc(i); Name := Format('%s%d', [tempname, i]); end; end; function DuplicateComponents( AComponent: TComponent // original component ): TComponent; // returns created new component procedure RegisterComponentClasses( AComponent: TComponent ); var i : integer; begin RegisterClass(TPersistentClass(AComponent.ClassType)); if AComponent is TWinControl then if TWinControl(AComponent).ControlCount > 0 then for i := 0 to (TWinControl(AComponent).ControlCount-1) do RegisterComponentClasses(TWinControl(AComponent).Controls[i]); end; var Stream: TMemoryStream; UniqueReader: TUniqueReader; Writer: TWriter; begin result := nil; UniqueReader := nil; Writer := nil; try Stream := TMemoryStream.Create; RegisterComponentClasses(AComponent); try Writer := TWriter.Create(Stream, 4096); Writer.Root := AComponent.Owner; Writer.WriteSignature; Writer.WriteComponent(AComponent); Writer.WriteListEnd; finally Writer.Free; end; Stream.Position := 0; try UniqueReader := TUniqueReader.Create(Stream, 4096); // create reader // should probably move these routines into theconstructor UniqueReader.OnSetName := UniqueReader.SetNameUnique; UniqueReader.LastRead := nil; if AComponent is TWinControl then UniqueReader.ReadComponents( // read in components and sub-components TWinControl(AComponent).Owner, TWinControl(AComponent).Parent, UniqueReader.ComponentRead ) else UniqueReader.ReadComponents( // read in components AComponent.Owner, nil, UniqueReader.ComponentRead ); result := UniqueReader.LastRead; finally UniqueReader.Free; end; finally Stream.Free; end; end;
From: adam@adamr.ftech.co.uk (Adam Redgewell)
Bart Mertens <b_mertens@roam.agfa.be> wrote: Hi, I've got a form with a treeview and a memo component on it. They are both aligned to take up the entire client area. I'd like to put a splitter bar between them so I can make one wider and the other smaller or vice versa. Which control can do this or how can I do this?Assuming your treeview is meant to be on the left and the memo on the right, you need to do the following:
type TForm1 = class(TForm) TreeView1: TTreeview; Panel1: TPanel; Panel2: TPanel; Memo1: TMemo; procedure Panel1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure Panel1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure Panel1MouseMove(Sender: TObject; Shift:TShiftState; X, Y: Integer); private Resizing: Boolean; public ... end; procedure TForm1.Panel2MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin Resizing:=true; end; procedure TForm1.Panel2MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin Resizing:=false; end; procedure TForm1.Panel2MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin if Resizing then begin TreeView1.Width:=TreeView1.Width+X; // Prevent occasional strange repaint errors on resize: Panel1.Invalidate; end; end;
From: keeper@mindspring.com (Mark R. Johnson)
I have seen the question "how do you add controls to TTabbedNotebook or TNotebook at run-time?" several times here and elsewhere. Well, after finally getting a few spare minutes to check into it, I have stumbled across the solution:TTabbedNotebook
Adding controls to a TTabbedNotebook during design time is a pretty simple task. All you need to do is set the PageIndex or ActivePage property to the page you want to add controls to, and begin dropping the controls onto the TTabbedNotebook.
Adding controls to a TTabbedNotebook during run-time is also very simple. However, there is no mention what-so-ever in the Delphi documentation on how to do this. To make matters worse, the TTabbedNotebook source code is not included when you purchase the Delphi VCL source. Thus, we are left with a mystery. Fortunately, I have stumbled across the solution.
The first step to solving this mystery was to take a look at \DELPHI\DOC\TABNOTBK.INT, the interface section of the TABNOTBK.PAS unit where TTabbedNotebook is defined. A quick examination will reveal the TTabPage class, which is described as holding the controls for a given page of the TTabbedNotebook.
The second clue to solving this case comes from observation that the Pages property of TTabbedNotebook has a type of TStrings. It just so happens that Delphi's TStrings and TStringList classes provide both Strings and Objects property pairs. In other words, for every string in TStrings, there is a corresponding Objects pointer. In many cases, this extra pointer is ignored, but if you're like me, you're thinking "Ah-hah!"
After a quick little test in code, sure enough, the Objects property points to a TTabPage instance -- the one that corresponds to the page name in the Strings property. Bingo! Just what we were looking for. Now see what we can do:
{ This procedure adds places a button at a random location on the } { current page of the given TTabbedNotebook. } procedure AddButton(tabNotebook : TTabbedNotebook); var tabpage : TTabPage; button : TButton; begin with tabNotebook do tabpage := TTabPage(Pages.Objects[PageIndex]); button := TButton.Create(tabpage); try with button do begin Parent := tabpage; Left := Random(tabpage.ClientWidth - Width); Top := Random(tabpage.ClientHeight - Height); end; except button.Free; end; end;
The process of adding controls to a TNotebook is almost exactly the same as that for TTabbedNotebook -- only the page class type is TPage instead of TTabPage. However, if you look in DELPHI\DOC\EXTCTRLS.INT for the type declaration for TPage, you won't find it. For some reason, Borland did not include the TPage definition in the DOC files that shipped with Delphi. The TPage declaration *IS* in the EXTCTRLS.PAS unit that you get when you order the VCL source, right where it should be in the interface section of the unit. Here's the TPage information they left out:
TPage = class(TCustomControl) private procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST; protected procedure ReadState(Reader: TReader); override; procedure Paint; override; public constructor Create(AOwner: TComponent); override; published property Caption; property Height stored False; property TabOrder stored False; property Visible stored False; property Width stored False; end;
{ This procedure adds places a button at a random location on the } { current page of the given TNotebook. } procedure AddButton(Notebook1 : TNotebook); var page : TPage; button : TButton; begin with Notebook1 do page := TPage(Pages.Objects[PageIndex]); button := TButton.Create(page); try with button do begin Parent := page; Left := Random(page.ClientWidth - Width); Top := Random(page.ClientHeight - Height); end; except button.Free; end; end;
From: "Eric Lawrence" <deltagrp@keynetcorp.net>
>>How can I save an entire stringgrid with all cells to a file?
Procedure SaveGrid; var f:textfile; x,y:integer; begin assignfile (f,'Filename'); rewrite (f); writeln (f,stringgrid.colcount); writeln (f,stringgrid.rowcount); For X:=0 to stringgrid.colcount-1 do For y:=0 to stringgrid.rowcount-1 do writeln (F, stringgrid.cells[x,y]); closefile (f); end; Procedure LoadGrid; var f:textfile; temp,x,y:integer; tempstr:string; begin assignfile (f,'Filename'); reset (f); readln (f,temp); stringgrid.colcount:=temp; readln (f,temp); stringgrid.rowcount:=temp; For X:=0 to stringgrid.colcount-1 do For y:=0 to stringgrid.rowcount-1 do begin readln (F, tempstr); stringgrid.cells[x,y]:=tempstr; end; closefile (f);
By handle OnScroll event ,follow code can synchronize two scroll boxes on different panels: (You have two scrollboxes in TMainForm:ScrollBar1 and ScrollBar2)
procedure TMainForm.ScrollBar1Scroll(Sender: TObject; ScrollCode: TScrollCode; var ScrollPos: Integer); begin ScrollBar2.Position:=ScrollPos; end; procedure TMainForm.ScrollBar2Scroll(Sender: TObject; ScrollCode: TScrollCode; var ScrollPos: Integer); begin ScrollBar1.Position := ScrollPos; end;
If the user keeps either key pressed and the change of the item (ComboBox.OnChange) takes a long(ish) time an annoying delay will be noticed. As a "work around" I would like to react to the change of the ItemIndex only after a short period of time, e.g. 100 ms.Here's an example. Written in D2, but technique should work OK in D1 too. Just a simple form with a combo and a label. You probably should consider using Yield in addition to the call to Application.ProcessMessages, to avoid slowing down the PC when the forms message queue is empty.
unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; const // Just some message constant PM_COMBOCHANGE = WM_USER + 8001; // 500 ms CWantedDelay = 500; type TForm1 = class(TForm) ComboBox1: TComboBox; Label1: TLabel; procedure ComboBox1Change(Sender: TObject); private procedure PMComboChange(var message : TMessage); message PM_COMBOCHANGE; public end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.ComboBox1Change(Sender: TObject); begin PostMessage(Handle, PM_COMBOCHANGE, 0, 0); end; procedure TForm1.PMComboChange(var message : TMessage); const InProc : BOOLEAN = FALSE; StartTick : LONGINT = 0; begin if InProc then begin // Update the starting time for the delay StartTick := GetTickCount; end else begin // We're in the loop InProc := TRUE; // Initial starting time StartTick := GetTickCount; // Wait until wanted time has elapsed. // If proc gets called again, starting time will change while GetTickCount - StartTick < CWantedDelay do Application.ProcessMessages; // Increment a counter, just for illustration of when to do the actual OnChange work Label1.Caption := IntToStr ( StrToIntDef ( Label1.Caption, 0 ) + 1); // We're finished with the loop InProc := FALSE; end; end; end.
From: "Volker Schneider" <vschneid@arco.met.fu-berlin.de<
Procedure DoSomethingWithEditControls; Var K: Integer; EditArray: Array[0..99] of Tedit; begin Try For K:= 0 to 99 do begin EditArray[K]:= TEdit.Create(Self); EditArray[K].Parent:= Self; SetSomeOtherPropertiesOfTEdit; Left:= 100; Top:= K*10; OnMouseMove:= WhatToDoWhenMouseIsMoved; end; DoWhateverYouWantToDoWithTheseEdits; Finally For K:= 0 to 99 do EditArray[K].Free; end;
Is there any way at all of making a tab on a tabbed notebook unselectable? i.e not allowing the user to click and see its contents?[Mike O'Hanlon, TMike@zoo.co.uk]
Yes, this is possible. The simplest way to do it is to remove the relevant page of the TabbedNotebook with something like:
with TabbedNotebook do Pages.Delete(PageIndex);
J := 0; with TabbedNotebook do for I := 0 to ComponentCount - 1 do if Components[I].ClassName = 'TTabButton' then begin Components[I].Name := ValidIdentifier(TTabbedNotebook( Components[I].Owner).Pages[J]) + 'Tab'; Inc(J); end;
function ValidIdentifier (theString: str63): str63; {----------------------------------------------------------} { Turns the supplied string into a valid Pascal identifier } { by removing all invalid characters, and prefixing with } { an underscore if the first character is numeric. } {----------------------------------------------------------} var I, Len: Integer; begin Len := Length(theString); for I := Len downto 1 do if not (theString[I] in LettersUnderscoreAndDigits) then Delete(theString, I, 1); if not (theString[1] in LettersAndUnderscore) then theString := '_' + theString; ValidIdentifier := theString; end; {ValidIdentifier}
with TabbedNotebook do begin TabIdent := ValidIdentifier(Pages[PageIndex]) + 'Tab'; TControl(FindComponent(TabIdent)).Enabled := False; { Switch to the first enabled Tab: } for I := 0 to Pages.Count - 1 do begin TabIdent := ValidIdentifier(Pages[I]) + 'Tab'; if TControl(FindComponent(TabIdent)).Enabled then begin PageIndex := I; Exit; end; end; {for} end; {with TabbedNotebook}
with TabbedNotebook do for I := 0 to Pages.Count - 1 do begin TabIdent := ValidIdentifier(Pages[I]) + 'Tab'; if not TControl(FindComponent(TabIdent)).Enabled then TControl(FindComponent(TabIdent)).Enabled := True; end; {for}
Please email me and tell me if you liked this page.