How to find a button with "value" in html page (Webbrowser - Delphi) - html

i have a html page that it have 3 forms with 3 submit buttons . buttons have no name but they have value :
<input type="submit" VALUE="Login">
How can i find this button with its value and click on it ?
Thanks

procedure TForm1.Button1Click(Sender: TObject);
var
ovElements: OleVariant;
i: Integer;
begin
ovElements := WebBrowser1.OleObject.Document.forms.item(0).elements;
for i := 0 to (ovElements.Length - 1) do
if (ovElements.item(i).tagName = 'INPUT') and
(ovElements.item(i).type = 'SUBMIT') and
(ovElements.item(i).Value = 'Login') then
ovElements.item(i).Click;
end;

I use in this case my Procedure WB_send_Click_by_Value:
procedure WB_send_Click_by_Value(WB: TWebbrowser;form_nr:nativeint;tag,typ,val: string);
var ovElements: OleVariant;
i: Integer;
begin
ovElements := WB.OleObject.Document.forms.item(form_nr).elements;
for i := 0 to (ovElements.Length - 1) do
begin
if AnsiSameText(ovElements.item(i).tagName,tag) and
AnsiSameText(ovElements.item(i).type,typ) and
AnsiSameText(ovElements.item(i).value,val) then
ovElements.item(i).Click;
end;
end;
I use this Procedure for Buttons in WebPage Formular 1 like:
WB_send_Click_by_Value(Webbrowser1,0,'input','submit','ok');
or e.g. for RadioButtons in Form 2 like:
WB_send_Click_by_Value(Webbrowser1,1,'input','radio','dns');

Related

How can I prevent that a LCL defined focused empty ListBox implementing some kind of search field

I'm working with
FreePascal 3.2.0,
Lazarus 2.0.12,
GTK2 GUI backend
under Ubuntu Linux 18.04.
I have a strange behavior of a TListBox component.
When I focus a empty ListBox by clicking (SetFocus) and
press the [space] key,
some kind of Edit or SearchField appears (..see image below).
The field vanishes after a short period (3 s).
How can I switch off this "feature".
My current work around is to use a handler for the onEnter event to take away the ListBox focus (full code example see below).
procedure TFrmMain.lbxTestEnter(Sender: TObject);
begin
if mUseWorkAround then begin
if LbxTest.Items.Count = 0 then begin
PnlButton.SetFocus;
exit;
end;
end;
end;
I'm not sure if this is the right way.
Full Code Example:
unit UFrmTestListBox;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls,
StdCtrls;
const
PNL_MESSAGE = 'WORK AROUND IS %sACTIVE! ';
type
{ TFrmMain }
TFrmMain = class(TForm)
BtnToggle: TButton;
GbxList: TGroupBox;
GbxLog: TGroupBox;
LbxTest: TListBox;
MmLog: TMemo;
PnlButton: TPanel;
SplMain: TSplitter;
procedure BtnToggleClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure lbxTestEnter(Sender: TObject);
private
mUseWorkAround: Boolean;
mCounter: Integer;
procedure OutLn(S:String);
public
end;
var
FrmMain: TFrmMain;
implementation
{$R *.lfm}
procedure TFrmMain.FormCreate(Sender: TObject);
begin
mUseWorkAround:= false;
mCounter := 0;
PnlButton.Caption:=Format(PNL_MESSAGE,['NOT ']);
end;
procedure TFrmMain.lbxTestEnter(Sender: TObject);
begin
inc(mCounter);
OutLn('');
OutLn(Format('--- %d -----------------------', [mCounter]));
OutLn('ListBox onEnter event');
if mUseWorkAround then begin
OutLn('Work around is active!');
if LbxTest.Items.Count = 0 then begin
OutLn('ListBox is empty, change focus to the button panel.');
PnlButton.SetFocus;
exit;
end;
end else begin
OutLn('Work around NOT active!');
OutLn('ListBox is empty, press [SPACE] & EditField appears.')
end;
end;
procedure TFrmMain.BtnToggleClick(Sender: TObject);
begin
mUseWorkAround:=not mUseWorkAround;
if mUseWorkAround then
PnlButton.Caption:=Format(PNL_MESSAGE,[''])
else
PnlButton.Caption:=Format(PNL_MESSAGE,['NOT ']);
end;
procedure TFrmMain.OutLn(S:String);
begin
MmLog.Lines.BeginUpdate;
MmLog.Lines.Append(S);
MmLog.Lines.EndUpdate;
MmLog.SelStart := Length(MmLog.Lines.Text);
end;
end.

How can I declare a function from within a function in Delphi/FreePascal without nesting them?

I would like to do something like this:
(I want to conserve the functions public so I can access them from other procedures/functions).
The functions are on the same form (frmSequenciador) - I didn't post it for it is huge in its integrity..
function geradorDeVetores():TIntArray;
var
contador: Integer;
vetor: array [1..numMax] of integer;
begin
Randomize;
for contador:=1 to numMax do
begin
if contador = 1 then
vetor[contador]=float_round_down(Random*10);
else vetor[contador]:= ***frmSequenciador.evitaRepeticao(contador, vetor)***;
end;
end;
function evitaRepeticao(pos: integer; vetor:TIntArray):integer;
var
numigual: boolean;
temporario, cont: integer;
begin
numigual:=true;
temporario:= float_round_down(Random*10);
for cont:=1 to pos-1 do
if temporario <> vetor[cont] numigual:=false else numigual:=true;
if numigual=false then evitaRepeticao():=temporario else evitaRepeticao():=***frmSequenciador.evitaRepeticao(pos, vetor)***;
end;
It was a simple matter of removing the frmSequenciador prefix of the functions as Ken White stated.
What I wanted to know is: if two functions are inside the same form unit (thx Jerry), do we need a prefix to call each other?
It seems not. Thanks all!

Delphi XE2 HTML PAGE how can i input value and press a button [duplicate]

i have a html page that it have 3 forms with 3 submit buttons . buttons have no name but they have value :
<input type="submit" VALUE="Login">
How can i find this button with its value and click on it ?
Thanks
procedure TForm1.Button1Click(Sender: TObject);
var
ovElements: OleVariant;
i: Integer;
begin
ovElements := WebBrowser1.OleObject.Document.forms.item(0).elements;
for i := 0 to (ovElements.Length - 1) do
if (ovElements.item(i).tagName = 'INPUT') and
(ovElements.item(i).type = 'SUBMIT') and
(ovElements.item(i).Value = 'Login') then
ovElements.item(i).Click;
end;
I use in this case my Procedure WB_send_Click_by_Value:
procedure WB_send_Click_by_Value(WB: TWebbrowser;form_nr:nativeint;tag,typ,val: string);
var ovElements: OleVariant;
i: Integer;
begin
ovElements := WB.OleObject.Document.forms.item(form_nr).elements;
for i := 0 to (ovElements.Length - 1) do
begin
if AnsiSameText(ovElements.item(i).tagName,tag) and
AnsiSameText(ovElements.item(i).type,typ) and
AnsiSameText(ovElements.item(i).value,val) then
ovElements.item(i).Click;
end;
end;
I use this Procedure for Buttons in WebPage Formular 1 like:
WB_send_Click_by_Value(Webbrowser1,0,'input','submit','ok');
or e.g. for RadioButtons in Form 2 like:
WB_send_Click_by_Value(Webbrowser1,1,'input','radio','dns');

Calling TEdit objects based on DB query

I have a form with 7 TEdit having name EditPhone1, EditPhone2 and so on.
In the same form I query a DB to get data to fill those TEdits. Of course I cannot know in advance how many results the query will return.
How can I call the various TEdit objects when looping on the rowcount of the query?
Use FindComponent to "convert" a component name to the component itself:
var
Edit: TEdit;
I: Integer;
begin
DataSet.First;
I := 1;
while not DataSet.Eof do
begin
Edit := TEdit(FindComponent(Format('EditPhone%d', [I])));
if Edit <> nil then
Edit.Text := DataSet.FieldValues['PhoneNo'];
DataSet.Next;
Inc(I);
end;
Now, this requires to hard-code the EditPhone%d string into the source which results in all kinds of maintainability issues. For example: consider renaming the edits.
Alternative 1:
To not rely on the component names, you could instead make use of TLama's idea and add all the edits to a list:
uses
... , Generics.Collections;
type
TForm1 = class(TForm)
EditPhone1: TEdit;
...
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FEdits: TList<TEdit>;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FEdits := TList<TEdit>.Create;
FEdits.AddRange([EditPhone1, EditPhone2, EditPhone3, EditPhone4, EditPhone5,
EditPhone6, EditPhone7]);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FEdits.Free;
end;
procedure TForm1.ADOQuery1AfterOpen(DataSet: TDataSet);
var
I: Integer;
begin
DataSet.First;
I := 0;
while (not DataSet.Eof) and (I < FEdits.Count) do
begin
FEdits[I].Text := DataSet.FieldValues['PhoneNo'];
DataSet.Next;
Inc(I);
end;
end;
This still requires some maintenance in case of adding edits in future.
Alternative 2:
You could also loop over all edits in the form to find the ones tagged to be added to the list, instead of adding them each explicitly:
procedure TForm1.FormCreate(Sender: TObject);
var
I: Integer;
begin
FEdits := TList<TEdit>.Create;
for I := 0 to ComponentCount - 1 do
if (Components[I] is TEdit) and (TEdit(Components[I]).Tag = 1) then
FEdits.Add(TEdit(Components[I]));
end;
But keeping those tags up to date is another burden.
Alternative 3:
I suggest you use a TDBGrid which is a data-component. Opening the linked dataset will automatically add all phone numbers to the grid. With some settings, the grid may kind of look like a couple of edits below each other.
You can, for example, use Tag property, to find needed component. Set all you TEdit's tag from 1 to 7 (or more), and find component by:
Var I: Integer;
MyEdit : TEdit;
For I = 0 To Self.ComponentCount - 1 Do
if (Self.Components[I] IS TEdit) AND (Self.Components[I] AS TEdit).Tag = YourTag
MyEdit = (Self.Components[I] AS TEdit);
You can also dynamically create so many TEdits, you need, and assign Tag property on creation, and find it this code later in runtime.
I'd suggest using DBCtrlGrid. You place your controls for one row on it, and it repeats the controls for as many rows as your data set has.
Get query result (usually using .RowCount property of TDataset return)
After getting the number of row, do iteration to make TEdit and set the text property
Here is sample of code:
...
For i:=0 to RowCount do
Begin
A:=TEdit.Create(self);
A.Parent:=AForm;
A.Top:=i*14;
A.Text:=ADataset.Field(i).AsString;
End;
...

How to call the OnChange event of "Select" ? (Delphi - WebBrowser)

I'm using Delphi and WebBrowser componenet to navigate a html page . the page have a Combobox . is there any way to call the OnChange event ?
The ComboBox is like this :
<select name="comboname" onchange="Some Javascript codes">
Also , i have used this code :
function TFrmMain.SetComboboxValue(WB: TEmbeddedWB;
SelectName, ItemName: string): Boolean;
var
iForms, iFormItems, iSelectItems: Word;
FormItem: OleVariant;
begin
Result := false;
for iForms := 0 to WB.OleObject.Document.Forms.length - 1 do
begin
FormItem := WB.OleObject.Document.Forms.item(iForms);
for iFormItems := 0 to FormItem.length - 1 do
begin
if (FormItem.item(iFormItems). type = 'select-one') and SameText
(FormItem.item(iFormItems).Name, SelectName) then
begin
for iSelectItems := 0 to FormItem.item(iFormItems).Options.length - 1 do
begin
if SameText(FormItem.item(iFormItems).Options.item(iSelectItems)
.Text, ItemName) then
begin
FormItem.item(iFormItems).SelectedIndex := iSelectItems;
Result := true;
Break;
end;
end;
end;
end;
end;
end;
But it change the value only.
to execute the onchange event you can use the execScript method
check this sample
uses
MSHTML;
var
Doc: IHTMLDocument2;
HTMLWindow: IHTMLWindow2;
begin
Doc := WebBrowser1.Document as IHTMLDocument2;
if not Assigned(Doc) then
Exit;
HTMLWindow := Doc.parentWindow;
if not Assigned(HTMLWindow) then
Exit;
HTMLWindow.execScript('yourfunctioname()', 'JavaScript');
end;
for more info check this excellent article
How to call JavaScript functions in a TWebBrowser from Delphi
Inspired by the response. NET have been using the structures below:
FrameSet Document Elements Item Name Value Change ;
EWB.OleObject.Document.Frames.Item('mainFrame').Document.Forms.Item('invoiceForm').Elements.Item('inputname').Value:= '123456';
or
FrameSet Document Elements Items Lenth;
EWB.OleObject.Document.Forms.Item('invoiceForm').Elements.Length;