how can use dynamic array in delphi - arrays

i use dynamic array in delphi.
var
var
frame3:array[0..10] of TFrame3
procedure TForm1.Button1Click(sender:TObject);
begin
frame3[count] := TFrame3.create(self);
gridpanel2.insertcontrol(frame3[count]);
but this code is 'A component named Frame3 alredy exists.'
this error what can ido?

If you need multible instances of TFrame3 you need to give it a new name after you created it.
so change
frame3[count] := TFrame3.create(self);
gridpanel2.insertcontrol(frame3[count]);
To
frame3[count] := TFrame3.create(self);
frame3[count].Name := 'Frame3_' + InttoStr(Count);
gridpanel2.insertcontrol(frame3[count]);

The other issue is that it is not seen in your code how you are changing count loop variable. Is it defined in advance?
You need to do something like this:
procedure TForm1.Button1Click(sender:TObject);
var count:byte;
begin
for count:=1 to 10 do
begin
frame3[count] := TFrame3.create(self);
...
end;
or use any other way to set count before each array member (class instance) creation. This code will even probably not require to set Name property at all.

Related

Delphi setting up AdoConnection in runtime gives access violation

I'm trying to code a function that creates a TADOQuery dynamically. With this function, I'm able to change its SQL.Text property in the parameter of the function. How my function call should work:
procedure TDlgMain.FormCreate(Sender: TObject);
var
Q: TADOQuery;
begin
Q := NewQuery('select * from Utenti');
Q.Open;
end;
Here the code of the function and a screenshot of the access violation error, is there a way to solve this?
function NewQuery(Conn: TADOConnection; SQL: String): TADOQuery; overload;
function NewQuery(SQL: String): TADOQuery; overload;
function TDMDB.NewQuery(Conn: TADOConnection; SQL: String): TADOQuery;
begin
Result := TADOQuery.Create(nil);
Result.Connection := Conn;
Result.SQL.Text := SQL;
end;
function TDMDB.NewQuery(SQL: String): TADOQuery;
begin
Result := NewQuery(DBConn, SQL);
end;
The error indicates that the TDataModule is not yet created at the time you attempt to use it to create the query via DMDB.NewQuery().
The reason for the error is two folded.
First, when using the IDE to first create a form (DlgMain: TDlgMain in your case) and then a data module (TDMDB: TDataModule). This places the module creation after the form creation in the project file (.dpr) as folllows: (to see the .dpr file, select menu Project - View source)
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TDlgMain, DlgMain);
Application.CreateForm(TDMDB, DMDB);
Application.Run;
end.
Secondly, as you attempt to create the connection already in the main forms OnCreate() event, when the data module is not yet created, the result is the AV you see.
You can correct the error by moving the creation of your data module before the creation of your main form:
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TDMDB, DMDB); // Create this before the form
Application.CreateForm(TDlgMain, DlgMain);
Application.Run;
end.
A side note, in case you wonder:
You may be aware that the first form created via Application.CreateForm() becomes the main form, and that is still valid. The data module is not a form, and thus your DlgMain is still the first created form and becomes the main form.

Delphi XE10 Filtering database table using FORMAT LIKE

I want filtering my database using fdtable. I write this code fdtable2.Filter := Format('PRODUK LIKE ''%s%%''', [edit1.Text]); but it was so sensitive with Uppercase and Lowercase. How can i write the code so when i write 'emon' in the edit.text, it filtering the word like 'Lemon' into the database. Thank you
Have you added the
fdtable2.Filtered := true;
in your Code?
You also have a mistake in your code!
fdtable2.Filter := Format('PRODUK LIKE ''%s%%''', [edit1.Text]);
This Code will Result in "PRODUK LIKE 'SOME_TEXT%%'"
you should set the s to the right Position to get it work properly.
This should work:
fdtable2.Filter := Format('PRODUK LIKE ''%%s%''', [edit1.Text]);
fdtable2.Filtered := true;

Executing a stored procedure twice using TAdoStoredProc in Delphi

I'm using Delphi 10.1 Berlin and I needed to use TAdoStoredProc in my project. I don't use any non-visual component in my forms or data modules. All my connection and db components are creating at run-time.
My Question: When I'm trying to execute TAdoStoredProc, it executes twice and my insert, update processes have worked twice.
var
mSp: TADOStoredProc;
i: Integer;
begin
mSp := TADOStoredProc.Create(nil);
mSp.Connection := conn;
mSp.ProcedureName := spname;
mSp.CommandTimeout := 600;
mSp.parameters.Refresh;
for i := 0 to parameters.Count - 1 do
begin
mSp.parameters.ParamByName(parameters[i].Name).Value := parameters[i].Value;
end;
mSp.ExecProc;
mSp.Open;
Result := mSp;
What should I do or change? Thanks.
As I see that your code required some return values. So you should use TAdoStoredProc like TAdoQuery. You don't need to use ExecProc function for execute, try to use only Open function.
You can just check out TADOStoredProc.ExecProc Method documentation from here.
If an application is only interested in the result set returned by a stored procedure, call Open method for the TADOStoredProc component or set its Active property to true.

Show Files in a ListBox Delphi

Im trying to create an mp3 player that later I may used as base to create a karaoke the only problem so far is that I want to show the *.mp3 files of a directory that the users select on a ListBox without having to add then manually 1 by 1 and that it only shows the song name not the current path. I have find some ways like using large functions or instead of a ListBox using a ComboBox but it isn't a easier way or transfer the files from the ComboBox to the Listbox?
Try this code it will add file name to ListBox and save its path to TStringList with same Index so you can use get both everywhere in your code
var
Form1: TForm1;
FilePath: TStringList;
implementation
{$R *.dfm}
procedure FindFiles(FilesList: TStrings; FilesPath: TStrings;
StartDir, FileMask: string);
var
SR: TSearchRec;
DirList: TStringList;
IsFound: Boolean;
i: integer;
begin
if StartDir[length(StartDir)] <> '\' then
StartDir := StartDir + '\';
IsFound := FindFirst(StartDir + FileMask, faAnyFile - faDirectory, SR) = 0;
while IsFound do
begin
FilesPath.Add(StartDir + SR.Name);
FilesList.Add(SR.Name);
IsFound := FindNext(SR) = 0;
end;
FindClose(SR);
DirList := TStringList.Create;
IsFound := FindFirst(StartDir + '*.*', faAnyFile, SR) = 0;
while IsFound do
begin
if ((SR.Attr and faDirectory) <> 0) and (SR.Name[1] <> '.') then
DirList.Add(StartDir + SR.Name);
IsFound := FindNext(SR) = 0;
end;
FindClose(SR);
for i := 0 to DirList.Count - 1 do
FindFiles(FilesList, FilePath, DirList[i], FileMask);
DirList.Free;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FilePath.Free;
end;
procedure TForm1.sButton1Click(Sender: TObject);
begin
FilePath := TStringList.Create;
FindFiles(sListBox1.Items, FilePath, EditStartDir.Text, '*.mp3');
{FilePath is where full file path saved, EditStartDir is where to search}
end;
procedure TForm1.sListBox1Click(Sender: TObject);
begin
If sListBox1.ItemIndex > -1 then
sEdit2.Text := FilePath.Strings[sListBox1.ItemIndex];
end;
end.
If you are interested only in listing all the files from specific folder without their icons then you might want to use FileListBox component from Win 3.1 tab.
You can easily define contents from which directory are shown in it by setting the Directory property. Note this property is not exposed to Object inspector to be used at design time but it is perfectly accessible at runtime.
The mentioned component also allows defining filters and even file attributes to show only files that you desire.
The only limitation of mentioned component is that it won't allow you to show files from subfolders which is something a lot of end users would probably like.
For that you would have to search for the files on your own and add them to your desired component one by one.
If you want to only display the filename instead of the whole file path you can use ExtractFileName procedure but keep in mind that you will still have to somehow keep the full path somewhere so that you know which file to open when certain file is selected on your desired component.
There are also some components available which mimic the file browsing behavior similar to Windows Explorer like TShellTreeView or TShellListView that shipped with older versions of Delphi.
And quick search on Google shows that there are even more similar components available.

DELPHI class with dynamic array has problem with SetLength()

I need to create a class containing an array of record objects but trying to use SetLength raise an Access Violation errror.
Consider the following example of a tree object with fruits.
type
TFruit = record
color: string;
weight: double;
end;
type
TObjectTree = class
Public
Fruits: array of TFruit;
constructor Create;
procedure AddFruit;
end;
In the implementation, when trying to resize the array of Fruit objects or initializing to nil generates problems.
constructor TObjectTree.Create;
begin
inherited Create;
Fruits:=nil; //Raises an error
end;
procedure TObjectTree.AddFruit(FruitColor: string; FruitWeight: integer);
begin
SetLength(Fruits, Length(Fruits)+1); //Raises an error (when I comment Fruits:=nil; in the constructor)
Fruits[Length(Fruits)].color:=FruitColor;
Fruits[Length(Fruits)].weight:=FruitWeight;
end;
How can I use dynamic arrays in a class?
Replace
Fruits[Length(Fruits)].color:=FruitColor;
Fruits[Length(Fruits)].weight:=FruitWeight;
with
Fruits[High(Fruits)].color:=FruitColor;
Fruits[High(Fruits)].weight:=FruitWeight;
then it works.
Something tells me you've neglected to create an instance of TObjectTree. You've declared a TObjectTree variable, but you've either not called TObjectTree.Create, or you've called it directly on the variable you declared instead of assigning a new value to that variable:
var
Tree: TObjectTree;
begin
// This is wrong.
Tree.Create;
// This is right.
Tree := TObjectTree.Create;
Without properly instantiating TObjectTree, there is no valid memory to back the Fruits field you attempt to use, so assigning a value to it gives an error.
As an addition to the answers of iamjoosy and Rob Kennedy, I would code this like so:
procedure TObjectTree.AddFruit(FruitColor: string; FruitWeight: integer);
var
NewCount: Integer;
begin
NewCount := Length(Fruits)+1;
SetLength(Fruits, NewCount);
Fruits[NewCount-1].color := FruitColor;
Fruits[NewCount-1].weight := FruitWeight;
end;
It is clearer, in my view, to call Length() just once.
You do not need to assign Fruits := nil in the constructor since that happens automatically. All fields are zero-initialised when an object is instantiated. That said, Fruits := nil should not raise an error. If it does it is probably a result of a memory corruption due to the out-of-bounds array accessing.
A further point to make is that enabling range checking would have resulted in an informative error that would have explained the problem. This is much more helpful than relying on access violations. I can't recommend range checking highly enough.
Finally, the SetLength(..., Length(...)+1) pattern typically leads to very inefficient memory usage and can lead to performance problems for large lists. If you have Delphi 2009+, I would recommend using TList<TFruit> instead.

Resources