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: "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;
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>
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: "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.
"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.
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.