I'm working with Delphi 7 code to ensure that comments are entered on a tab have been saved before users can switch tabs.
The tabs are located on a TPageControl, and this code is triggered OnExit
procedure TfCallerInfo.tsChaplainExit(Sender: TObject);
begin
{ Compare the saved DB value with the text in the comments field }
if (dmMain.qChaplainCOMMENTS.AsString <> dbmChapComments.Text) then
begin
ShowMessage ('Please save the comments before proceeding.');
pcDetail.ActivePage := tsChaplain; // Remain on the Current Page
tsChaplain.SetFocus;
end;
end;
When users click on another tab tsInfoRequest for instance, the validation does trigger, but the Active Page becomes tsInfoRequest instead of remaining tsChaplain.
Any idea what I'm doing wrong?
There's probably a better way to do what you're trying to do. Use the TPageControl.OnPageChanging event instead.
procedure TfCallerInfo.pcDetailPageChanging(Sender: TObject;
NewPage: TTabSheet; var AllowChange: Boolean);
begin
if pc.ActivePage = tsChaplain then
begin
AllowChange := (dmMain.qChaplainCOMMENTS.AsString = dbmChapComments.Text);
if not AllowChange then
ShowMessage(...);
end;
end;
By the way, a better test might be
AllowChange := not dmMain.gChaplainCOMMENTS.Modified;
TField.Modified is set to True when the content of the field is changed when it's DataSet is in dsEdit or dsInsert mode, and set to False when it's state changes back to dsBrowse.
Related
I have these controls TDateTimePicker, TComboBox, Tedit and TButton. TButton is disabled by default. What I would like to achieve is to enable TButton when all the other controls are filled or not null.
With the following codes, all the 3 controls starting with TDateTimePicker when filled I don't have any issues it works as expected.
The error comes when I fill TComboxBox followed by TEdit, it enables the TButton even TDateTimePicker is not filled yet. Or vise versa, I will fill TEdit followed by TComboBox, it enables the TButton.
From the codes below, I expect the TButton will not enable unless all the 3 controls are filled.
I've been trying to figure out (all day) how this error come to happen.
I will appreciate anyone there help me figure this out.
procedure TfrmHolidays.EnableSaveButton;
begin
if (edtHolidayName.Text <> NullAsStringValue) and (cmbHolidayType.ItemIndex <> -1)and (dtpHolidayDate.Date <> 0) then
begin
btnHolidaySave.Enabled := True;
end
else
begin
btnHolidaySave.Enabled := False;
end;
end;
procedure TfrmHolidays.dtpHolidayDateChange(Sender: TObject);
begin
EnableSaveButton;
end;
procedure TfrmHolidays.cmbHolidayTypeChange(Sender: TObject);
begin
EnableSaveButton;
end;
procedure TfrmHolidays.edtHolidayNameChange(Sender: TObject);
begin
EnableSaveButton; // triggers enable btnHolidaySave button
end;
By the way, I have more code related to making TDateTimePicker a blank and I supposed there's no issues with that. I also tried nesting within If Statement each condition and I am still getting the error. Further, I tested each condition at a time and It works fine.
Updates:
Here's how I initialized the dtpHolidayDate.Date:
procedure TfrmHolidays.FormCreate(Sender: TObject);
begin
DateTime_SetFormat(dtpHolidayDate.Handle, ' ');
FDTMDateEmpty := True;
end;
procedure TfrmHolidays.dtpHolidayDateCloseUp(Sender: TObject);
begin
DateTime_SetFormat(dtpHolidayDate.Handle, PChar('MMM dd yyyy (ddd)'));
end;
procedure TfrmHolidays.dtpHolidayDateChange(Sender: TObject);
begin
FDTMDateEmpty := False;
EnableSaveButton; // same and updated procedure above
end;
As pointed out in the comments above, you do not have an initialised value for the TDateTimePicker.
What you want is to have it default to a sensible date, rather than set to 0 - that is not at all helpful to users.
I would introduce a Boolean flag that you set yourself once the TDateTimePicker has been set.
You could set this flag in the OnChange event handler.
So something like:
interface
protected
blMyDTFlag: Boolean;
...
implementation
function TfrmHolidays.dtpHolidayDateChange(Sender: TObject);
begin
Self.blMyDTFlag:=True;
end
procedure TfrmHolidays.EnableSaveButton;
begin
if (edtHolidayName.Text <> '') and
(cmbHolidayType.ItemIndex <> -1) and
(Self.dtMyDTFlag) then
btnHolidaySave.Enabled := True
else
btnHolidaySave.Enabled := False;
end;
Although not shown in the question, I can guess that you initialize the date by
dtpHolidayDate.Date := 0;
After this, testing the date against 0 will (most likely) fail because the time portion still contains the time that the control is created.
You can initialize by
dtpHolidayDate.DateTime := 0;
then you can test the date as you do.
Alternatively you can use SameDate to do the comparison.
uses
dateutils;
if (edtHolidayName.Text <> NullAsStringValue) and (cmbHolidayType.ItemIndex <> -1)
and (not SameDate(dtpHolidayDate.Date, 0)) then
I have a FDQuery bound to a FDConnection.
I am displaying the data on my form with DB Data-Aware components.
Whenever i use the FPQuery.Next, .Prior, ... it browses between the results.
Everything is working fine.
Except when i change a value (e.g. John -> Jane) and then use FPQuery.Next to get the next result it saves commits the changed value to the db even tho i didn't FDQuery1.CommitUpdates.
Is there a way to only save changed DataFields when the user presses the nbPost-Button or uses FDQuery1.CommitUpdates and NOT when browsing between results?
Thanks!
Like I said in a comment, the standard TDataset behaviour is to call its .Post method to save changes to the current row before navigating to another one. This happens in the routine TDataSet.CheckBrowseMode in Data.DB.Pas, which is called before any navigation action. This can't be changed without deriving a custom TDataset descendant.
(from Data.DB.Pas)
procedure TDataSet.CheckBrowseMode;
begin
CheckActive;
DataEvent(deCheckBrowseMode, 0);
case State of
dsEdit, dsInsert:
begin
UpdateRecord;
if Modified then Post else Cancel;
end;
dsSetKey:
Post;
end;
end;
Of course, a TDataSet has a BeforePost event, so it might be tempting to try and use that to cancel changes; however, the problem with BeforePost is how to determine the context in which it is being called, so as to be able to tell whether its being called from CheckBrowseMode rather than as a result of the user clicking the Save button.
The simple way around that is to catch the BeforeAction event of your DBNavigator, before it calls a navigation action on the dataset which will trigger the .Post:
procedure TForm1.DBNavigator1BeforeAction(Sender: TObject; Button:
TNavigateBtn);
var
Res : Integer;
DataSet : TDataSet;
begin
DataSet := DBNavigator1.DataSource.DataSet;
case Button of
nbFirst,
nbPrior,
nbNext,
nbLast: begin
if DataSet.State in [dsEdit, dsInsert] then begin
Res := MessageDlg('The current row has unsaved changes. Abandon them?', mtWarning, [mbYes, mbNo], 0);
if Res = mrYes then
DataSet.Cancel
else
DataSet.Post;
end;
end;
end;
end;
Good answer MartynA.
If you do not want to limit on navigator component and have such a check in general you can override TFDQuery.InternalPost like so:
procedure TFDQuery.InternalPost;
begin
if State in [dsEdit, dsInsert] then
begin
if MessageDlg('Save changes?', mtWarning, [mbYes, mbNo], 0) = mrNo then
Cancel();
end;
inherited;
end;
I am currently coding a school project. I apologize in advance for a possibly noob question.
I need to make a system for a fictional NGO, and I need to have all users register and login whenever they use the program.
I use editboxes and maskedits to receive the login data. The login screen has two editboxes, one for the username and one for the password. The registration screen is the same form, resized and with two additional, dynamic editboxes: One for confirming the password and another for the user's e-mail address.
Now, I made the static editboxes to contain a default value: If the user enters the editbox, then the default value disappears. If the user then exits the editbox without entering any values, the default value reappears. I haven't managed to get the dynamic editboxes to do the same.
How does one get OnActivate event handlers such as OnEnter/OnExit to fire when an already created dynamic component is activated?
If you are using a relatively modern Delphi version that supports XP+ Visual Styles, then TEdit has a TextHint property that does exactly what you are looking for, without the need to use any events at all.
procedure TMyForm.FormCreate(Sender: TObject);
var
Edit: TEdit;
begin
Edit := TEdit.Create(Self);
Edit.Parent := ...;
...
Edit.TextHint := 'default text here';
end;
Otherwise, if you really want to use the events, then you can do this instead:
procedure TMyForm.FormCreate(Sender: TObject);
var
Edit: TEdit;
begin
Edit := TEdit.Create(Self);
Edit.Parent := ...;
...
Edit.Text := 'default text here';
Edit.OnEnter := EditEnter;
Edit.OnExit := EditExit;
end;
procedure TMyForm.EditEnter(Sender: TObject);
begin
if TEdit(Sender).Text = 'default text here' then
TEdit(Sender).Text := '';
end;
procedure TMyForm.EditExit(Sender: TObject);
begin
if TEdit(Sender).Text = '' then
TEdit(Sender).Text := 'default text here';
end;
I want to find out if the TChromiumFMX browser component contains text that the user has selected, and if so, retrieve that text, without the user having to copy it to the clipboard (ctrl-c) first.
To improve on TLama's answer:
If you're not using ShowMessage, the anonymous procedure will not always have completed before Button1Click is exited, hence often yielding no results (or too late). Therefore a Done := true as the last line of the procedure can be checked for to see if the value has been retrieved:
procedure TForm1.Button1Click(Sender: TObject);
var Done: boolean;
begin
Done := false;
Chromium1.Browser.GetFocusedFrame.VisitDomProc(
procedure(const document: ICefDomDocument)
begin
SelectedText := document.SelectionAsText;
Done := true
end
);
while not Done do Application.ProcessMessages
end;
You will have to visit DOM, and as a gift you'll receive a reference to the current ICefDomDocument document interface. The ICefDomDocument interface then offers the SelectionAsText method, which returns the current selection as text (if any). In code you may write something like:
procedure TForm1.Button1Click(Sender: TObject);
begin
Chromium1.Browser.GetFocusedFrame.VisitDomProc(
procedure(const document: ICefDomDocument)
begin
ShowMessage(document.SelectionAsText);
end
);
end;
I have a form one which I want to show a file open dialog box before the full form opens.
I already found that I can't do UI related stuff in FormShow, but it seems that I can in FormActivate (which I protect from being called a second time...)
However, if the user cancels out of the file open dialog, I want to close the form without proceeding.
But, a form close in the activate event handler generates an error that I can't change the visibility of the form.
So how does one do some UI related operation during form start up and then perhaps abort the form (or am I trying to stuff a function into the form that should be in another form?)
TIA
It would be best (i think) to show the file open dialog BEFORE you create and show the form. If you want to keep all code together you might add a public class procedure OpenForm() or something:
class procedure TForm1.OpenForm( ... );
var
O: TOpenDialog;
F: TForm1;
begin
O := TOpenDialog.Create();
try
// set O properties.
if not O.Execute then Exit
F := TForm1.Create( nil );
try
F.Filename := O.FIlename;
F.ShowModal();
finally
F.Free();
end;
finally
O.Free();
end;
end;
Set a variable as a condition of the opendialog and close the form on the formshow event if the flag is not set correctly.
procedure TForm1.FormCreate(Sender: TObject);
begin
ToClose := not OpenDialog1.Execute;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
if ToClose then Close();
end;
or even more simply
procedure TForm1.FormShow(Sender: TObject);
begin
if not OpenDialog1.Execute then Close();
end;
If you want to keep the logic conditioning the opening self-contained in the Form, you can put a TOpenDialog in your Form and use a code like this in your OnShow event:
procedure TForm2.FormShow(Sender: TObject);
begin
if OpenDialog1.Execute(Handle) then
Color := clBlue
else
PostMessage(Handle, WM_CLOSE, 0, 0); // NB: to avoid any visual glitch use AlpaBlend
end;
If you don't need this encapsulation, a better alternative can be to check the condition before trying to show the form, for instance by embedding the Form2.Show call in a function that tests all the required conditions first.
Two Ways....
1. using oncreate and onactivate
create a global flag or even 2
var
aInitialized:boolean;
Set the flag to false in the oncreate handler.
aInitialized := false; //we have not performed our special code yet.
Inside onActivate have something like this
if not aInitialized then
begin
//our one time init code. special stuff or whatever
If successful
then set aInitialized := true
else aInitialized := false
end;
And how to close it without showing anything just add your terminate to the formshow. of course you need to test for some reason to close.. :)
Procedure Tmaindlg.FormShow(Sender: TObject);
Begin
If (shareware1.Sharestatus = ssExpired) or (shareware1.Sharestatus = ssTampered) Then
application.Terminate;
End;
In your DPR you will need to add a splash screen type effect. In my case I am showing progress as the application starts. You could also just show the form and get some data.
Code from the splash.pas
Procedure tsplashform.bumpit(str: string);
Begin
label2.Caption := str;
gauge1.progress := gauge1.progress + trunc(100 / items);
update;
If gauge1.progress >= items * (trunc(100 / items)) Then Close;
End;
Program Billing;
uses
Forms,
main in 'main.pas' {maindlg},
Splash in 'splash.pas' {splashform};
{$R *.RES}
Begin
Application.Initialize;
Application.Title := 'Billing Manager';
SplashForm := TSplashForm.Create(Application);
SplashForm.Show;
SplashForm.Update;
splash.items := 5;
SplashForm.bumpit('Loading Main...');
Application.CreateForm(Tmaindlg, maindlg);
SplashForm.bumpit('Loading Datamodule...');
Application.CreateForm(TfrmSingleWorkorder, frmSingleWorkorder);
SplashForm.bumpit('Loading SQL Builder...');
Application.CreateForm(TDm, Dm);
SplashForm.bumpit('Loading Security...');
Application.CreateForm(TSQLForm, SQLForm);
SplashForm.bumpit('Loading Reports...');
Application.CreateForm(Tpickrptdlg, pickrptdlg);
Application.Run;
End.