I'm working on a patient search app. I have a problem with ADOQuery.Active which does not deactivate when I delete a word in the search bar.
This is my code:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Mask, scControls,
scDBControls, scGrids, scDBGrids, scGPControls, Data.DB, Data.Win.ADODB;
type
TForm1 = class(TForm)
ADOConnection1: TADOConnection;
DataSource1: TDataSource;
Edit1: TEdit;
scDBGrid1: TscDBGrid;
ADOQuery1: TADOQuery;
ADOQuery1PATIENTId: TAutoIncField;
ADOQuery1NAME_PAT: TStringField;
ADOQuery1PRENOM_PAT: TStringField;
procedure Edit1Change(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Edit1Change(Sender: TObject);
begin
if Edit1.Text = '' then
ADOQuery1.Filtered := false
else
begin
ADOQuery1.Active := true;
ADOQuery1.Filtered := False;
ADOQuery1.Filter := 'NAME_PAT' + ' LIKE ' + QuotedStr(Edit1.Text + '%');
ADOQuery1.Filtered := True;
end;
end;
end.
After clean TEdit, I set ADOQuery1.Active := false
The problem here is you are not affecting the right properties according to your first description.
Generally in database applications, you don't need to deactivate the dataset, just write an event for disabling the filter 'Filtered := False' that will be triggered when the filter text input is cleared (by deleting the filter string manually or a "clear text" button).
But, if you need to deactivate the dataset, then you have to set that property in your code.
Like this:
procedure TForm1.Edit1Change(Sender: TObject);
begin
if Edit1.Text = '' then
begin
ADOQuery1.Filtered := False;
ADOQuery1.Active := False;
end
else
begin
ADOQuery1.Active := True;
ADOQuery1.Filtered := False;
ADOQuery1.Filter := 'NAME_PAT' + ' LIKE ' + QuotedStr(Edit1.Text + '%');
ADOQuery1.Filtered := True;
end;
end;
Here is some useful links from Delphi documentation about the "TDataSet.Active" property and how to set filters in datasets:
https://docwiki.embarcadero.com/Libraries/Alexandria/en/Data.DB.TDataSet.Active
https://docwiki.embarcadero.com/Libraries/Alexandria/en/Data.DB.TDataSet.Filter
https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Setting_the_Filter_Property
Related
I have old Delphi (XE6) project for a Firebird 2.5 database with IBX (IBExpress) components, where two IBDatabases controlled by one IBTransaction. When I commit (or rollback) the active transaction, modifications in both databases are committed (or rolled back) too. Firebird server support multidatabase transaction, see: 3.7. Multi-database applications in the Firebird documentation
Now, I need this function with Delphi XE6 FireDAC components, but when I try to run ExecSQL or Open select in both FDQuery, method Prepare in one FDQuery crashes with error:
[FireDAC][Phys][FB]-343. Cannot set default transaction
I was looking for a solution, but my problem is very rare, I did not find anything. FireDAC components support AutoStart and AutoStop transactions, but I have disabled this support, I want to manage the transactions manually. To test, you need two Firebird databases and use any SQL statement.
My test project, only Form and one Button:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, FireDAC.Stan.Intf,
FireDAC.Stan.Option, FireDAC.Stan.Error, FireDAC.UI.Intf, FireDAC.Phys.Intf,
FireDAC.Stan.Def, FireDAC.Stan.Pool, FireDAC.Stan.Async, FireDAC.Phys,
FireDAC.Stan.Param, FireDAC.DatS, FireDAC.DApt.Intf, FireDAC.DApt,
FireDAC.Phys.IBBase, FireDAC.Phys.FB, Data.DB, FireDAC.Comp.DataSet,
FireDAC.Comp.Client, FireDAC.VCLUI.Wait, FireDAC.Comp.UI;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FDTranBeforeCommit(Sender: TObject);
procedure FDTranBeforeRollback(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
FDGUIxWaitCur: TFDGUIxWaitCursor;
FDPhysFBDriv: TFDPhysFBDriverLink;
FDCon1: TFDConnection;
FDCon2: TFDConnection;
FDTrx: TFDTransaction;
FDQry1: TFDQuery;
FDQry2: TFDQuery;
begin
FDGUIxWaitCur := TFDGUIxWaitCursor.Create(nil);
FDPhysFBDriv := TFDPhysFBDriverLink.Create(nil);
FDPhysFBDriv.VendorLib := 'C:\Program Files\Firebird\Firebird_2_5\WOW64\fbclient.dll'; { 32 bit applications on 64 bit Firebird }
FDCon1 := TFDConnection.Create(nil);
FDCon2 := TFDConnection.Create(nil);
FDTrx := TFDTransaction.Create(nil);
FDQry1 := TFDQuery.Create(nil);
FDQry2 := TFDQuery.Create(nil);
try
try
FDTrx.Options.AutoCommit := False;
FDTrx.Options.AutoStart := False;
FDTrx.Options.AutoStop := False;
FDTrx.BeforeCommit := FDTranBeforeCommit;
FDTrx.BeforeRollback := FDTranBeforeRollback;
FDCon1.TxOptions.AutoCommit := False;
FDCon1.TxOptions.AutoStart := False;
FDCon1.TxOptions.AutoStop := False;
FDCon1.Params.Clear;
FDCon1.Params.Add('DriverID=FB');
FDCon1.Params.Add('Database=c:\a\dbone.fdb');
FDCon1.Params.Add('Server=LOCALHOST/3050');
FDCon1.Params.Add('Protocol=TCPIP');
FDCon1.Params.Add('user_name=NAME');
FDCon1.Params.Add('password=passw');
FDCon1.Params.Add('lc_ctype=WIN1250');
FDCon2.TxOptions.AutoCommit := False;
FDCon2.TxOptions.AutoStart := False;
FDCon2.TxOptions.AutoStop := False;
FDCon2.Params.Clear;
FDCon2.Params.Add('DriverID=FB');
FDCon2.Params.Add('Database=c:\a\dbtwo.fdb');
FDCon2.Params.Add('Server=LOCALHOST/3050');
FDCon2.Params.Add('Protocol=TCPIP');
FDCon2.Params.Add('user_name=NAME');
FDCon2.Params.Add('password=passw');
FDCon2.Params.Add('lc_ctype=WIN1250');
FDCon1.Transaction := FDTrx;
FDCon2.Transaction := FDTrx;
FDQry1.Connection := FDCon1;
FDQry1.Transaction := FDTrx;
FDQry2.Connection := FDCon2;
FDQry2.Transaction := FDTrx;
FDCon1.Open;
FDCon2.Open;
if FDTrx.Active then
ShowMessage('trx active');
if not FDTrx.Active then
FDTrx.StartTransaction;
if FDTrx.Active then
ShowMessage('trx active');
FDQry2.Close;
FDQry2.SQL.Text := 'select count(id) from table';
FDQry2.Prepare;
FDQry2.Open;
ShowMessage(Format('DBTwo table records count %d',[FDQry2.Fields[0].AsInteger]));
FDQry2.Close;
if FDTrx.Active then
ShowMessage('trx active');
FDQry1.Close;
FDQry1.SQL.Text := 'select count(id) from table';
FDQry1.Prepare; { An error appears here }
FDQry1.Open;
ShowMessage(Format('DBOne table records count %d',[FDQry1.Fields[0].AsInteger]));
FDQry1.Close;
if FDTrx.Active then
FDTrx.Commit;
except on E: Exception do
begin
ShowMessage(e.Message);
if FDTrx.Active then
FDTrx.Rollback;
end;
end;
finally
FDQry2.Close;
FDQry1.Close;
FDCon2.Close;
FDCon1.Close;
FDQry2.Free;
FDQry1.Free;
FDTrx.Free;
FDCon2.Free;
FDCon1.Free;
FDPhysFBDriv.Free;
FDGUIxWaitCur.Free;
end;
end;
procedure TForm1.FDTranBeforeCommit(Sender: TObject);
begin
ShowMessage('Trx BeforeCommit');
end;
procedure TForm1.FDTranBeforeRollback(Sender: TObject);
begin
ShowMessage('Trx BeforeRollback');
end;
end.
I'm trying to modify the text being pasted inside a TEdit descendant.
When the user paste some text, I want to replace all 'X' chars with an 'Y', without modifying the actual clipboard text content.
I've intercepted the WM_PASTE message, but I'm not aware about any "clean" way to change the text that's being pasted into the control.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TMyEdit = class(Vcl.StdCtrls.TEdit)
private
procedure WMPaste(var Msg: TWMPaste); message WM_PASTE;
end;
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
Clipbrd;
procedure TMyEdit.WMPaste(var Msg: TWMPaste);
begin
inherited;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
Edt : TMyEdit;
begin
Edt := TMyEdit.Create(Self);
Edt.Top := 10;
Edt.Left := 10;
Edt.Parent := Self;
end;
end.
The only working way I've found is to temporarly replace the clipboard content, but I'm looking for a cleaner solution (if there's one...).
procedure TMyEdit.WMPaste(var Msg: TWMPaste);
var
PrevClipboardText : string;
begin
if(IsClipboardFormatAvailable(CF_TEXT)) then
begin
PrevClipboardText := Clipboard.AsText;
try
Clipboard.AsText := StringReplace(Clipboard.AsText, 'X', 'Y', [rfReplaceAll]);
inherited;
finally
Clipboard.AsText := PrevClipboardText;
end;
end else
begin
inherited;
end;
end;
Why not do the obvious thing?
procedure TEdit.WMPaste(var Msg: TWMPaste);
begin
SelText := F(Clipboard.AsText);
end;
where F is your string-transforming function.
I'm trying to execute an sql query asynchronously. I've checked the example code from http://docwiki.embarcadero.com/RADStudio/XE5/en/Asynchronous_Execution_(FireDAC)
and also the example project from directory
..Samples\Object Pascal\Database\FireDAC\Samples\Comp Layer\TFDQuery\ExecSQL\Async
and I think I got the logic inside. But there is one problem - the event QueryAfterOpen never executes and my TDataSource remains always Nil (because it gets Nil inside QueryBeforeOpen - this event executes always). This is all the code from my unit :
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, FireDAC.Stan.Intf,
FireDAC.Stan.Option, FireDAC.Stan.Error, FireDAC.UI.Intf, FireDAC.Phys.Intf,
FireDAC.Stan.Def, FireDAC.Stan.Pool, FireDAC.Stan.Async, FireDAC.Phys,
FireDAC.VCLUI.Wait, FireDAC.Stan.Param, FireDAC.DatS, FireDAC.DApt.Intf, FireDAC.DApt,
Vcl.StdCtrls, Data.DB, FireDAC.Comp.DataSet, FireDAC.Comp.Client,
FireDAC.Phys.MySQLDef, FireDAC.Phys.MySQL;
type
TForm1 = class(TForm)
Button1: TButton;
FDPhysMySQLDriverLink1: TFDPhysMySQLDriverLink;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Query1BeforeOpen(DataSet: TDataSet);
procedure Query1AfterOpen(DataSet: TDataSet);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
Connection1: TFDConnection;
Query1: TFDQuery;
DataSource1: TDataSource;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
QueryFinished: Boolean;
begin
with Query1 do begin
SQL.Text := 'SELECT field1 FROM test_table WHERE idpk=1';
AfterOpen := Query1AfterOpen;
BeforeOpen := Query1BeforeOpen;
ResourceOptions.CmdExecMode := amAsync;
QueryFinished := False;
Open;
repeat
Sleep(100);
if Command.State = csPrepared then begin
// A command is prepared. A result set is not accessible.
// TmpInteger := Query1.FieldByName('field1').AsInteger;
end
else if Command.State = csOpen then begin // A command execution
// is finished. A result set is accessible and not yet fully fetched.
if DataSource1.DataSet <> Nil then begin
// this code never executes because Query1AfterOpen never executes and
// DataSource1.DataSet remains always Nil !!!
QueryFinished := True;
end;
end;
until ((QueryFinished) OR (DataSource1.DataSet <> Nil));
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
SQLConnParams: string;
begin
SQLConnParams := ''; // sql connection parameters removed from here from security
// issues, assume they are correct
Connection1 := TFDConnection.Create(Nil);
Connection1.Params.Text := SQLConnParams;
Query1 := TFDQuery.Create(Nil);
Query1.Connection := Connection1;
DataSource1 := TDataSource.Create(Nil);
DataSource1.DataSet := Query1;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
DataSource1.Free;
Query1.Free;
Connection1.Free;
end;
procedure TForm1.Query1AfterOpen(DataSet: TDataSet);
begin
DataSource1.DataSet := Query1;
Query1.AfterOpen := Nil;
//Query1.ResourceOptions.CmdExecMode := amBlocking;
end;
procedure TForm1.Query1BeforeOpen(DataSet: TDataSet);
begin
DataSource1.DataSet := Nil;
end;
end.
Apparently the code inside the repeat .. until .. loop is infinite unless someone terminates the program. What am I missing in order to execute the code inside Query1AfterOpen (or use another event instead) so I can access the result set after the TFDQuery has finished working ?
Those asynchronous events are synchronized with the main thread via the message loop. As long as you stay inside the Button1Click event no new messages can be handled. Thus the AfterOpen event is stuck inside the message loop.
I don't know what you are trying to achieve, but you should consider placing the relevant code in the AfterOpen event. The repeat-until clause somehow counterfeits the purpose of that async execution.
I have 6 checkbox with edittext at each of checkbox.
I want to show only the selected checkbox with its edittext value in memo.
Here is my code:
//jumCheck is total of selected checkbox
for I := 0 to jumCheck - 1 do
begin
if CheckBox1.Checked then
begin
Memo1.Lines.Append('Gejala: '+CheckBox1.Caption+', Penyakit: '+Edit1.Text);
end
else if CheckBox2.Checked then
begin
Memo1.Lines.Append('Gejala: '+CheckBox2.Caption+', Penyakit: '+Edit2.Text);
end;
end;
And the result is just the first checkbox that i was selected for looping.
Anyone, please help me.
Probably, you need TRadioButtons instead...
Here is the code for dynamically created TEdits and TCheckBoxes:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
const
ElementsCount = 6;
type
TForm1 = class(TForm)
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
chba: array [1 .. ElementsCount] of TCheckBox;
eda: array [1 .. ElementsCount] of TEdit;
procedure CBClick(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
i: byte;
begin
for i := 1 to ElementsCount do
begin
chba[i] := TCheckBox.Create(self);
chba[i].Tag := i; //you can change the code of CBClick
//and know out the sender easier by Tag property
chba[i].Top := (i - 1) * 30 + 1;
chba[i].Left := 1;
chba[i].Caption := 'Some caption ' + inttostr(i);
chba[i].Parent := self;
chba[i].OnClick:= CBClick;
eda[i] := TEdit.Create(self);
eda[i].Top := (i - 1) * 30 + 1;
eda[i].Left := 100;
eda[i].Text := '';
eda[i].Parent := self;
end;
end;
procedure TForm1.CBClick(Sender: TObject);
var
i: byte;
begin
Memo1.Text := '';
for i := 1 to ElementsCount do
begin
if chba[i].Checked then
begin
Memo1.Lines.Append(chba[i].Caption + ' ' + eda[i].Text);
exit;//??? In this case only the first checked will be processed
//Probably, you need TRadioButton's instead
end;
end;
end;
end.
Your code has a few problems:
Using else skips all checkboxes after the first selected checkbox
There is no point in combining for and a list of if-statements. If you have a if-statement for every checkbox, what do you want to iterate with the for over?
Your for starts with 0, but the first checkbox seems to be CheckBox1 (generally it would be better to use more descriptive names)
What you seem to be looking for is the method FindComponent to find a component of a certain name or index.
E.g. it becomes
for I := 1 to jumCheck do
begin
if (FindComponent('CheckBox' + IntToStr(i)) as TCheckBox).Checked then
begin
Memo1.Lines.Append('Gejala: '+(FindComponent('CheckBox' + IntToStr(i)) as TCheckBox).Caption+', Penyakit: '+(FindComponent('Edit' + IntToStr(i)) as TEdit).Text);
end
end;
Problem:
How can I load frame in Form1 or sample container in form ?
FindClass or GetClass is only locality for main form appl-n
I need (maybe) string globaly elemental for TFrameClass, next code:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls, Vcl.ExtCtrls;
type
TFrameClass = class of TFrame;
type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
FFrame: TFrame;
function StrShowFrame(FrameClassName: string;
ParentPanel: TWinControl): Boolean;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses Base1Frame, Base2Frame, Base3Frame;
function TForm1.StrShowFrame(FrameClassName: string;
ParentPanel: TWinControl): Boolean;
var
FrameClass: TClass;
// Current Frame (FrameName)
FrameName: string;
begin
Result := False;
??? GetClass is only locality for main form in appl-n
FrameClass := GetClass(FrameClassName);
if FrameClass = nil then
begin
ShowMessageFmt('Class %s not registered', [FrameClassName]);
Result := False;
Exit;
end;
try
begin
LockWindowUpdate(ParentPanel.Handle);
if Assigned(FFrame) then
if FFrame.ClassType = FrameClass then
begin
Result := True;
Exit;
end
else
FFrame.Destroy; // del previus FrameClass
try
FFrame := TFrameClass(FrameClass).Create(nil);
except
on E:Exception do
begin
Result := True;
E.Create(E.Message);
FFrame := nil;
Exit;
end;
end;
FrameName:= FrameClassName;
Delete(FrameName, 1, 1); // T-...
FFrame.Name := Concat(FrameName, '1');
FFrame.Parent := ParentPanel;
FFrame.Align := alClient;
end;
finally
LockWindowUpdate(0);
end;
Result := True;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
StrShowFrame('TFr_Base1', Panel1);
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
try
if FFrame <> nil then
FFrame.Free
else
ShowMessage('Class not activ');
except
end;
end;
end.
How can I load frame in Form1 or sample container in form ?
FindClass or GetClass is only locality for main form appl-n
I need (maybe) string globaly elemental for TFrameClass.
GetClass() and FindClass() are not local to the MainForm, they are global to the entire RTL as a whole. Any unit can call RegisterClass() and have that class be accessible to any other unit that shares the same instance of the RTL. That last part is important. A DLL cannot register a class that the EXE uses (and vice versa), unless both projects are compiled with Runtime Packages enabled so they share a single RTL instance.