I am looking at creating autoform filler for Delphi, and obviously need a good method to capture which input boxes are the login boxes on each site so was wondering if I use a Twebbrowser component and load the page then click on the username and password boxes on the particular sites if I can then extract the form name and input box names that I clicked on.
In Short I need delphi to capture the name of the selected input box on a web page loaded into a twebbrowser component.
Any good methods to capture this information from the page loaded in a twebbrowser page would be appreciated!.
The following code shows how to find an INPUT element named 'input1':
var
E : IHtmlElement;
D : IHtmlDomNode;
Doc2 : IHtmlDocument2;
Doc3 : IHtmlDocument3;
All : IHTMLElementCollection;
i : Integer;
begin
Doc3 := WebBrowser1.Document as IHtmlDocument3;
D := Doc3.GetElementByID('input1') as IHtmlDomNode;
if D <> Nil then begin
...
If you need to find more than one INPUT element or you wish to pattern-match the
name of the INPUT element(s), you can do this by retrieving the document's
IHtmlDocument2 interface and then iterating its all collection:
Doc2 := WebBrowser1.Document as IHtmlDocument2;
All := Doc2.all;
for i := 0 to All.Length - 1 do begin
E := All.Item(Null, i) as IHtmlElement;
// Test E and do what you like with it
end;
You could use a function like this to find the parent FORM element of an INPUT element
function GetParentFormElement(E : IHtmlElement) : IHtmlElement;
begin
Result := Nil;
while E <> Nil do begin
if CompareText(E.tagName, 'form') = 0 then begin
Result := E;
exit;
end;
E := E.parentElement;
end;
end;
and use it like this:
E := D as IHtmlElement;
E := GetParentFormElement(E);
Assert(E <> Nil);
not all forms have a name or id so how do I get the number or reference of a parent form if there are a number of forms in a page?
Equally, not all INPUT elements are contained in a FORM one. TBH, I don't know of a robust way of doing what you want which will survive a page's author making changes to it. Anyway, there must be some way of identifying a given INPUT element, otherwise the server wouldn't be able to extract the user's response, mustn't there? So it's just a question of figuring out what that might be for a particular page. It it is not in the attributes of the element, then maybe you could look for the text of a nearby text element - after all, there must be some kind of prompt to the user to tell them what to fill in where. But this is really a different issue than the substance of your original q, which I hope I have answered. If you need more help on this specific point, I suggest you ask in a new q. Make sure you include details (code) of what you've already tried, as qs lacking them tend not to be received well at SO.
Sorry about my formatting, new to posting here!. Reduced version.
procedure TForm5.Button2Click(Sender: TObject);
var
Document: IHTMLdocument2;
MyEl: IHTMLElement;
begin
MyEl := (WebBrowser1.Document as IHTMLDocument2).activeElement;
If MyEl.tagName = 'INPUT' then
begin
edit2.Text := MyEl.getAttribute('Name', 0);
end;
end;
Related
I created a report using FastReport, but the only way I know to get data to that report is from a database, I want to get data from a TEdit and I don't want to store anything, just writing in TEdit + click on the button (fastreport.preview) + print and Done.
How can I do that ?
Please explain am new with Delphi and FastReport.
You can use the OnGetValue event of your TfrxReport component as follows:
procedure TForm1.frxReport1GetValue(const VarName: string; var Value: Variant);
begin
if(VarName = 'MyVariable') then
begin
Value := Edit1.Text;
end;
end;
Then you just need to add a memo item to the report and set its value to [MyVariable].
One possible approach is to access the TfrxReport and TfrxMemoView components at runtime. Note, that when you don't have a dataset, the Master Data band won't be printed, so you should use another band.
You may use the following code as a basic example. Just place one TfrxReportTitle band (named 'ReportTitle1') and one TfrxMemoView text object (named 'Memo1') on your TfrxReport component.
procedure TfrmMain.btnReportClick(Sender: TObject);
var
memo: TfrxMemoView;
band: TfrxReportTitle;
begin
// Get the band
band := (rptDemo.Report.FindObject('ReportTitle1') as TfrxReportTitle);
// Create a memo
memo := TfrxMemoView.Create(band);
memo.CreateUniqueName;
memo.ParentFont := True;
memo.Text := edtReport.Text;
memo.SetBounds(100, 1, 100, 16);
memo.HAlign := haLeft;
memo.AutoWidth := False;
// Use existing memo
memo := (rptDemo.Report.FindObject('Memo1') as TfrxMemoView);
memo.Text := edtReport.Text;
// Preview report
rptDemo.ShowReport(False);
end;
Notes: This is a working example, tested with FastReport 4.7.
if Length(idStrArray)>0 then
begin
with DataModule4.ADQueryTemp do
begin
Close;
SQL.Clear;
SQL.Add('SELECT id, pato, ftest, res FROM tbl ');
SQL.Add('WHERE id IN ('+idStrArray+')');
Open;
(rprMasterDataFish as Tfrxmasterdata).DataSet := frxDst_Multi;
(rprMasterDataFish as Tfrxmasterdata).DataSetName := 'Multi';
end;
end;
Hello,
I have TfrxDBDataset component. I can add fields from table like above. But i also want to add fields and values manually at runtime.
I have text file like this :
id note
1 sample
2 sample
I want to read this text file and insert note to frxDst_Multi. Is this possible ?
I dont want to create a new column as note in tbl. Because, i have too many mysql server.
Thanks in advice,
You can't add fields to a dataset while it is open, so you have to do it before
it is opened, either in code or using the TDataSet fields editor. If you are
doing it in code, you can add the field in the dataset's BeforeOpen
event.
The next problem is that is you don't want to field to be bound to the table the
dataset accesses, you need to add it as a calculated field and set its value
in the dataset's`OnCalcFields' event - see example below.
Ideally, the added field would be a TMemoField, but unfortunately a TMemoField
can't be a calculated field (FieldKind = ftMemo). So probably the best thing you can do is to make it a String field, but then you will need to
give it a fixed maximum size and truncate the field's value at that size when you calculate its value.
Btw, I don't know whether your TfrxDBDataset supports the fkInternalCalc fieldkind, but if it does, then you could try adding the note field as a TMemoField instead of a TStringField one.
The one thing I haven't been able to do is to load the field's value from
an external file because you haven't said in your q how to determine the
name of the file which is to be read.
Obviously, the IsNull check in the frxDst_MultiCalcFields event is to avoid the overhead of reloading the file if its contents have already been read.
const
NoteFieldSize = 4096;
procedure TForm1.AddNoteField;
var
NoteField : TField;
begin
if frxDst_Multi.FindField('Note') = Nil then begin
NoteField := TStringField.Create(frxDst_Multi);
NoteField.FieldName := 'Note';
NoteField.Size := NoteFieldSize;
NoteField.FieldKind := fkCalculated;
NoteField.DataSet := frxDst_Multi;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
frxDst_Multi.Open;
end;
procedure TForm1.frxDst_MultiCalcFields(DataSet: TDataSet);
var
S : String;
begin
if DataSet.FieldByName('Note').IsNull then begin
S := 'a note'; // replace by code to set the field value
DataSet.FieldByName('Note').AsString := Copy(S, 1, NoteFieldSize);
end;
end;
procedure TForm1.frxDst_MultiBeforeOpen(DataSet: TDataSet);
begin
AddNoteField;
end;
I use Delphi 7 with a number of third party components. My main stub application loads a number of DLLs, which are various modules like creditors, debtors, purchase orders, and so on.
I have an issue with FindComponent(). 99% of the time, it works how it should. But not for the code below.
I was trying to create a form reports, where I keep all the details of the reports selection criteria in a table, and then create the criteria on the fly. In theory, it should work perfectly, but for some reason after creating the components, FindComponent() cannot find them.
try
for i := gbSelectionCriteria.ComponentCount - 1 downto 0 do begin
ShowMessage(gbSelectionCriteria.Components[i].Name);
gbSelectionCriteria.Components[i].Free;
end;
// The above loop to remove the components from the groupbox works fine
// Creating the components works
fSysData.tbSelectionCriteria.First;
while not fSysData.tbSelectionCriteria.EOF do begin
case fSysData.tbSelectionCriteriaComponentType.AsInteger of
1 : begin // TMyAdvEdit
with TMyAdvEdit.Create(gbSelectionCriteria) do begin
Visible := False;
Parent := gbSelectionCriteria;
Name := fSysData.tbSelectionCriteriaName.AsString;
Left := fSysData.tbSelectionCriteriaLeft.AsInteger;
Top := fSysData.tbSelectionCriteriaTop.AsInteger;
Width := fSysData.tbSelectionCriteriaWidth.AsInteger;
LabelCaption := fSysData.tbSelectionCriteriaCaption.AsString;
LabelPosition := AdvEdit.lpLeftCenter;
LabelAlwaysEnabled := True;
LabelTransparent := True;
EditType := MyEditType[fSysData.tbSelectionCriteriaDataType.AsInteger];
Text := '';
OnClick := GetClickEvent(fSysData.tbSelectionCriteriaOnClickEvent.AsString);
OnDblClick := GetClickEvent(fSysData.tbSelectionCriteriaOnDblClickEvent.AsString);
OnKeyPress := GetKeyPressEvent(fSysData.tbSelectionCriteriaOnKeyPressEvent.AsString);
Visible := True;
// at this point findComponent finds nothing
if FindComponent(Name) <> nil then
ShowMessage(Name+' Created');
end;
edEdit.OnClick := GetClickEvent(fSysData.tbSelectionCriteriaOnClickEvent.AsString);
edEdit.OnDblClick := GetClickEvent(fSysData.tbSelectionCriteriaOnDblClickEvent.AsString);
edEdit.OnKeyPress := GetKeyPressEvent(fSysData.tbSelectionCriteriaOnKeyPressEvent.AsString);
edEdit.Visible := True;
if FindComponent(edEdit.Name) <> nil then
ShowMessage(edEdit.Name+' Created');
end;
2 : begin
end;
3 : begin
end;
4 : begin
end;
5 : begin
end;
6 : begin
end;
7 : begin
end;
8 : begin
end;
end;
fSysData.tbSelectionCriteria.Next;
end;
if fSysData.tbSysReports.Locate('ReportID', TAdvOfficeRadioButton(Sender).Tag, []) then begin
ReportData.ReportID := TAdvOfficeRadioButton(Sender).Tag;
ReportData.RepName := fSysData.tbSysReportsReportName.AsString;
ReportData.RepTitle := fSysData.tbSysReportsReportTitle.AsString;
ReportData.RepModule := fSysData.tbSysReportsModule.AsString;
ReportData.RepOrientation := fSysData.tbSysReportsReportOrientaton.AsString;
ReportData.RepPageIndex := fSysData.tbSysReportsCriteriaPageIndex.AsInteger;
end;
finally
end;
The Process of the reports is:
User clicks a button
Radio buttons are created from the button click
User clicks a radio button
Report criteria is created from the radio button click
User enters data or DblClicks to select data from a list.
User Clicks Preview button to view Report - this is where FindComponent fails and returns nil..
All the code worked before when I had created all the criteria at design time, then added the code above.
The code below is part of what needs to be added to the query to retrieve the data for the report:
if Length(TMyAdvEdit(FindComponent('edQuoteReference')).Text) > 0 then
qryTempTable.SQL.Add(' and q.UserReference = "' + TMyAdvEdit(FindComponent('edQuoteReference')).Text + '"');
This is the first time FindComponent() fails and goes no further.
I have tried various ways to create the components, but each of them results in an Access Violation because the component is nil.
I have looked everywhere, and tried everything I can think of, for a solution to this problem.
FindComponent searches for components owned by the subject of the method call. You call FindComponent on the form, and so look for the component amongst those components owned by the form. But the control you search for is not owned by the form, it is owned by gbSelectionCriteria, which is what you passed to the control's constructor as the Owner argument.
If you wish to use FindComponent in the way you do you therefore need to make the form be the owner of the controls that you create. Then when you call FindComponent on the form, it can find the control because it is the owner. Pass Self to the control's constructor to make this come to pass:
TMyAdvEdit.Create(Self)
I'm having to make some reasonably large guesses here. Perhaps this code actually resides in a data module rather than a form. But the essential principle will be as I say.
Firstly I do apologize if this is in the wrong spot..
Thanks for the response and the answer, I have been doing this for a lot of years and I can't believe I missed something so small.
this,
if FindComponent(Name) <> nil then
should have been this,
if gbSelectionCriteria.FindComponent(Name) <> nil then
I don't normally use with, it was just one way to test create the component.
I set the components visibility to false before and then to true after it is created to stop flicker as it creates.
Thanks again..
I would like that user could write only numbers from 1 to 49 in edit box. I know how to exclude letters and has possibility to put only numbers but I can't limit it to specific numbers (e.g from 1 to 49 - like in lottery game).
I added KeyDown event to edit box and put this code:
if not (KeyChar in ['1'..'9']) then
begin
ShowMessage('Invalid character');
KeyChar := #0;
end;
How can I modify it?
Following David's advice, a pattern I often use looks something like this :
function Validate1To49(AStr : string; var Value : integer) : boolean;
begin
result := TryStrToInt(AStr, Value) and
(Value >= 1) and (Value <= 49);
end;
procedure TForm1.Edit1Change(Sender: TObject);
var
tmp : integer;
begin
if Validate1To49(Edit1.Text, tmp) then
Edit1.Color := clWhite
else
Edit1.Color := clRed;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
theValue : integer;
begin
if Validate1To49(Edit1.Text, theValue) then begin
// go ahead and do something with "theValue"
end else
ShowMessage('Value not valid');
end;
Here if the user inputs anything invalid there is immediate visual feedback that is not intrusive like a modal message box. Here I coloured the edit box red, but you can also show/hide or change the colour of a warning label above the edit box with a message detailing the expected input, use a green checkmark, or whatever else seems sensible
This has the benefit that the user can see immediately whether or not their inputs are valid. The validation methods can be wrapped up so that they can be re-used when the user attempts to initiate an action requiring those inputs. At this point I feel it's fine to use a modal messagebox because the user has plainly missed the obvious cues already in front of them. Alternatively, when validating in the OnChange handler you can simply disable any action controls (like buttons, etc) that would allow the user to proceed. This requires validating all input controls required for the action - again, usually you would wrap the entire validation action into a single method for sensible re-use.
For simple values like integers, a good SpinEdit control can be useful (the VCL one is included in the Samples package - not always installed by default). The above pattern is more flexible, however and can be used for any type of input. A SpinEdit won't provide any feedback, however - the user will simply type and nothing will show up. They may wonder whether the application is broken if there is no clear guidance visible about what the input element will accept.
The same question can also be answered in this way also by writing a OnKeyPress event for a Edit box. By this way the user will not be able to enter the number greater than the limit which we define.
procedure TfrmCourse.edtDurationKeyPress(Sender: TObject; var Key: Char);
var
sTextvalue: string;
begin
if Sender = edtDuration then
begin
if (Key = FormatSettings.DecimalSeparator) AND
(pos(FormatSettings.DecimalSeparator, edtDuration.Text) <> 0) then
Key := #0;
if (charInSet(Key, ['0' .. '9'])) then
begin
sTextvalue := TEdit(Sender).Text + Key;
if sTextvalue <> '' then
begin
if ((StrToFloat(sTextvalue) > 12) and (Key <> #8)) then
Key := #0;
end;
end
end;
end;
Given a nested TClientDataSet, how could I find the link field name on the detail TClientDataSet?
I'm copying data from one TClientDataSet to another (record by record) and I would like to automatically ignore the link field.
I could also copy the data using the TClientDataSet.Data property but I still would need to clear the link and key fields.
You can check the "DataSetField" property of the detail/nested dataset, or access the "NestedDataSet" property of the master dataset.
Sample code to get the "link" field name:
function GetCDSDLinkFieldName(cds: TClientDataSet): string;
var
i: Integer;
cdsDetail: TClientDataSet;
begin
Result := EmptyStr;
cdsDetail := nil;
if Assigned(cds.DataSetField) then
cdsDetail := cds;
if not Assigned(cdsDetail) and (cds.FieldCount > 0) then
begin
i := 0;
while not Assigned(cdsDetail) and (i < cds.FieldCount) do
begin
if cds.Fields[i].DataType = ftDataSet then
cdsDetail := TClientDataSet(TDataSetField(cds.Fields[i]).NestedDataSet);
Inc(i);
end;
end;
if Assigned(cdsDetail) then
Result := cdsDetail.DataSetField.FieldName;
end;
Invoking example:
procedure ...
begin
ShowMessage(GetCDSDLinkFieldName(cdsMaster));
ShowMessage(GetCDSDLinkFieldName(cdsDetail));
end;
P.S.: 2 years later I don't believe this answer will help the author of the question, but maybe can help others that search for the same subject.