Hey guy's so am making this program in delphi that loads a list of usernames i want to add them all to the Memo without using selectall i want to use the for loop to learn how it works as you can see i tried by failed it selects both but only adds the last one which is weird xD Any help would be great thanks guys
procedure TForm1.Button1Click(Sender: TObject);
begin
Memo1.Lines.Clear;
Listbox1.Items.LoadFromFile('names.txt');
end;
procedure TForm1.Button2Click(Sender: TObject);
var
I: Integer;
begin
for I:=Listbox1.Items.Count-1 downto 0 do
begin
ListBox1.ItemIndex:=I;
Memo1.Lines.Add(ListBox1.Items.Strings[1]);
end
end;
end;
end.
Well, you add the item with index 1 every time. You presumably mean:
for I:=Listbox1.Items.Count-1 downto 0 do
Memo1.Lines.Add(ListBox1.Items[I]);
This adds in reverse order. If you want the items in the same order it is simply:
Memo1.Lines.Assign(ListBox1.Items);
Related
I try to create new unit Ado_Op , in this unit i try to create a procedure like this :
procedure CloseAllTables ();
Var I : Integer; T : TADOTable;
begin
for I := 1 to ComponentCount-1 do
if Components[i] is TADOTable then
begin
T := FindComponent(Components[i].Name) as TADOTable;
T.Close;
end;
T.Destroy;
end;
Error :
ComponentCount inaccessible.
Note : I'm using Delphi 10 Seattle.
The compiler error you report is just the beginning of your problems. There are quite a few more. I see the following problems, with item 1 being the one noted in the question:
You need to supply an object on which to refer to the properties ComponentCount and Components[].
You are erroneously using one based indexing.
You needlessly call FindComponent to find the component that you already have.
You call Destroy once only, on whichever object you found last. Or on an uninitialized variable if you don't find any. The compiler should warn of this, and I do hope you have warnings and hints enabled, and heed them.
Based on the comments you are trying to call the Close method on each table owned by a form. Do that like so:
procedure CloseAllTables(Owner: TComponent);
var
i: Integer;
begin
for i := 0 to Owner.ComponentCount-1 do
if Owner.Components[i] is TADOTable then
TADOTable(Owner.Components[i]).Close;
end;
If you wish to destroy all of these components too, which I doubt, then you would need to run the loop in descending order. That's because when you destroy an component, it is removed from its owners list of components. That code would look like this, assuming that there was no need to call Close on an object that is about to be destroyed.
procedure DestroyAllTables(Owner: TComponent);
var
i: Integer;
begin
for i := Owner.ComponentCount-1 downto 0 do
if Owner.Components[i] is TADOTable then
Owner.Components[i].Free;
end;
This is driving me nuts ....
I fetch the names of my databases to populate the cxComboBox1:
procedure TForm1.FormShow(Sender: TObject);
var
I: Integer;
DBList: TStringDynArray;
begin
DBList := TDirectory.GetFiles(ExtractFilePath(ParamStr(0)), '*.abs', TSearchOption.soAllDirectories);
for I := 0 to Length(DBList) - 1 do
begin
cxCombobox1.Properties.Items.Add(DBList[I]);
end;
end;
This works OK.The list of my databases show in the cxCombobox1.
In the second cxCombobox I populate the table names that belong to the database
displayed in cxCombobox1.
procedure TForm1.cxComboBox1PropertiesChange(Sender: TObject);
var
TABLES: TStringList;
i: integer;
begin
if ABSTable1.Active = True then
ABSTable1.Close;
cxComboBox2.properties.Items.Clear;
TABLES := TStringList.Create;
ABSDatabase1.DatabaseFileName:=cxCombobox1.Text;
try
ABSDatabase1.Open;
ABSDatabase1.GetTablesList(TABLES);
for i:= 0 to TABLES.Count-1 do
cxComboBox2.properties.Items.Add(TABLES[i]);
finally
TABLES.free;
end;
end;
This basically works ok. Selecting the database in the cxComboBox1 populates the cxComboBox2
with pertinent tables.
So the general idea is to open the table when selected in the cxComboBox2.
And I did :
procedure TForm1.ABSTable1BeforeOpen(DataSet: TDataSet);
begin
ABSTable1.DatabaseName:= ABSDatabase1.DatabaseName;
ABSTable1.TableName := cxComboBox2.Text;
end;
And on combobox2 change event I did:
procedure TForm1.cxComboBox2PropertiesChange(Sender: TObject);
begin
cxGrid1DBTableView1.ClearItems;
ABSTable1.Open;
cxGrid1DBTableView1.DataController.CreateAllItems;
end;
This works ok. But only when I open the tables from the selected database (combobox1)
that are displayed in the combobox2.
Say I have the table opened and then go select another database in the combobox1
I get the error "Missing ABSTable1.Tablename" !
What am I missing here? Where is my table name getting lost ?
If I replace the combobox2 on change event with a button :
procedure TForm1.cxButton2Click(Sender: TObject);
begin
if ABSTable1.Active = True then
ABSTable1.Close;
cxGrid1DBTableView1.ClearItems;
ABSTable1.Open;
cxGrid1DBTableView1.DataController.CreateAllItems;
end;
then everything works....
This happens because you call Items.Clear() in cxComboBox1PropertiesChange which raises a change event which in return results in a call to cxComboBox2PropertiesChange.
You should use Items.BeginUpdate() before any update and Items.EndUpdate() when you finished the update to avoid raising events on each updated step / item.
I have been trying to get a list of menu sub-items from a standard Windows application using the UIAutomationCore library imported as a TLB into Delphi - i.e.
File -> New | Exit
Help -> About
I can get the application menu, and then the top-level items into a list (i.e. in the example above, 'File' and 'Help', but I cannot get a list of ANY controls that are under these menuitems. My code is as below - the FElement represents the actual menuitem I am checking.
The length of the collection returned by FindAll is always 0. I have tried expanding the menuitem prior to this code, but it seems to have no effect.
UIAuto.CreateTrueCondition(condition);
FItems := TObjectList<TAutomationMenuItem>.create;
self.Expand;
sleep(3000);
// Find the elements
self.FElement.FindAll(TreeScope_Descendants, condition, collection);
collection.Get_Length(length);
for count := 0 to length -1 do
begin
collection.GetElement(count, itemElement);
itemElement.Get_CurrentControlType(retVal);
if (retVal = UIA_MenuItemControlTypeId) then
begin
item := TAutomationMenuItem.Create(itemElement);
FItems.Add(item);
end;
end;
I can see examples of this in C#, and they are not really doing anything different from the code above (as far as I can see)
Thanks in advance
Update : It looks very similar to this question
Update2 : In this example it is trying to do this for another Delphi application. However, if I try the same thing on notepad (for example), the same issue occurs.
Update3 : Using Inspect (and then using UI Automation), I have the following structure ...
Name = Exit
Ancestors = File (menu) Form1 (pane)
I have also tried this after expanding the menu (file), and the same thing is happening (or not happening).
I think you have the following two issues:
The menus will not list the sub menu items unless the menu is expanded
If you're trying to automate your own application, you have to do it in a thread.
The following works for me:
// Careful: the code might not be 100% threadsafe, but should work for the purpose of demonstration
const
UIA_MenuItemControlTypeId = 50011;
UIA_ControlTypePropertyId = 30003;
UIA_NamePropertyId = 30005;
UIA_ExpandCollapsePatternId = 10005;
procedure TForm1.Button1Click(Sender: TObject);
begin
TThread.CreateAnonymousThread(procedure begin CoInitializeEx(nil, 2); FindItems(true); CoUninitialize; end).Start;
end;
procedure TForm1.FindItems(Recurse: Boolean);
var
UIAuto: TCUIAutomation;
condition: IUIAutomationCondition;
collection: IUIAutomationElementArray;
Length: Integer;
Count: Integer;
itemElement: IUIAutomationElement;
retVal: Integer;
val: WideString;
ExpandCollapsePattern: IUIAutomationExpandCollapsePattern;
FElement: IUIAutomationElement;
begin
UIAuto := TCUIAutomation.Create(nil);
UIAuto.CreateTrueCondition(condition);
// Find the elements
UIAuto.ElementFromHandle(Pointer(Handle), FElement);
FElement.FindAll(TreeScope_Descendants, condition, collection);
collection.Get_Length(length);
for Count := 0 to length - 1 do
begin
collection.GetElement(Count, itemElement);
itemElement.Get_CurrentControlType(retVal);
if (retVal = UIA_MenuItemControlTypeId) then
begin
ItemElement.Get_CurrentName(val);
TThread.Synchronize(nil,
procedure
begin
memo1.lines.Add(val);
end);
itemElement.GetCurrentPattern(UIA_ExpandCollapsePatternId, IInterface(ExpandCollapsePattern));
if Assigned(ExpandCollapsePattern) then
begin
ExpandCollapsePattern.Expand;
if Recurse = True then
FindItems(False);
end;
end;
end;
UIAuto.Free;
end;
With the code below I am trying to reload a DirectionsResult back into a TGMDirections.
procedure Form2.Button2Click(Sender: TObject);
var
DR: TDirectionsResult;
i: Integer;
begin
DR:= TDirectionsResult.Create(Form1.FDirection, 0);
DR.XMLData.BeginUpdate;
for i:= 0 to Memo1.Lines.Count - 1 do
begin
DR.XMLData.Append(Memo1.Lines[i]);
end;
DR.XMLData.EndUpdate;
ShowMessage(Form1.FDirection.DirectionsResult[0].Routes[0].Leg[0].EndAddress);
end;
All seems well until the ShowMessage where I get a List out of bounds message.
I take it that the DR has not been created or the Memo has not loaded into the DirectionsResult.
Further adaption has confirmed the DirectionsResult[0] does not exist.
Help with the correction would be greatly appreciated.
You can't add a TDirectionsResult to DirectionsResult array programatically, you need to invoke Execute method from TGMDirections object.
However you can do something like this
procedure TForm1.Button1Click(Sender: TObject);
var
DR: TDirectionsResult;
begin
DR:= TDirectionsResult.Create(GMDirection1, 1);
DR.XMLData.Text := Memo1.Lines.Text;
ShowMessage(DR.Routes[0].Leg[0].EndAddress);
end;
That is, you can work without problems with your object and you can access to all properties and methods.
Note the assignation between XMLData and Memo.Lines, don't assign line to line because the control of XML is made on OnChange event of XMLData.
Regards.
I don't know exactly how to explain my question. Here is my try to explain: the function FindNext(SearchRec) will get me the next file from a directory. In my application I am looking sometimes go to backward a few files from my current SearchRec index. So how can I do that?
So I am looking for the oppose of FindNext(SearchRec) a function like FindBackward(SearchRec)
There's no such function. You'll need to keep track of previous hits in a list, say, and do the back tracking using that list.
I suggest to place them in an array of TSearchRec
SearchRecArr:array of TSearchRec;
Then when you reach a certain file, get the SearchRec that you need from the Array.
for example, this is an example where I placed in some folder 3 text files of names (z, z1, & z2).
then If I reached 'z2.txt' I will read SearchRec 2 steps backwards:
procedure TForm2.Button1Click(Sender: TObject);
var
SearchRec:TSearchRec;
SearchRecArr:array of TSearchRec;
i:integer;
begin
i:=-1;
if FindFirst('C:\Users\zeina.shehab\Desktop\New folder\z*.txt',faAnyFile,SearchRec)=0 then
begin
repeat
SetLength(SearchRecArr,length(SearchRecArr)+1);
SearchRecArr[high(SearchRecArr)]:=SearchRec;
inc(i);
if SearchRec.Name='z2.txt' then
caption:=SearchRecArr[i-2].Name;
until (FindNext(SearchRec)<>0);
end;
end;
I wrote my own function. Here is the code which works very well for me and it is very efficient for thousands of files(because it doesn't slow my playback algorithm).
Procedure GetBackward(var SRInitial:TSearchRec; iForwardSpeed:integer);
var SR:TSearchRec;
iIndex:integer;
vLastFiles:Array of String;
begin
SetLength(vLastFiles,Trunc(iForwardSpeed));
FindFirst(sPath+'*.txt',faAnyFile,SR);
while (FindNext(SR) = 0)and(SR.Name <> SRInitial.Name) do
begin
for iIndex := 0 to high(vLastFiles)-1 do
vLastFiles[iIndex]:=vLast[iIndex+1];
vLastFiles[high(vLastFiles)]:=SR.Name;
end;
//Fewer than ForwardSpeed
if vLastFiles[0] = '' then
begin
Exit;
end;
FindClose(SR);
FindClose(SRInitial);
FindFirst(sPath+'*.'+cbType.Text,faAnyFile,SRInitial);
while (FindNext(SRInitial) = 0)and(SRInitial.Name <> vLastFiles[0]) do
;
end;
The function was modified.