In the TDatePicker.OnCloseUp event, how to determine if the user clicked Cancel? - delphi

I have a perhaps simple question:
I have a TStringGrid in Delphi 10.4, and a TDatePicker there, with which I want to select a date in the FixedCol. When I click on the header cell, the TDatePicker opens and also shows today's date. I've pre-selected today's date in the TForm.OnCreate event for the TDatePicker. Furthermore, I have an OnCloseUp event for the TDatePicker, which writes the selected date into the cell.
However, it does this both when I confirm with the tick and when I cancel with the X.
How can I query whether the user has clicked Cancel so that I don't then enter a value?
My code is more or less like this:
procedure TForm1.DatePicker1CloseUp(Sender: TObject);
begin
//if DatePicker.Cancel ?? then
// exit;
StringGrid1.Cells[col, row] := FormatDateTime(dateformat, DatePicker1.Date);
end;
I only found, that if I select a date and click OK, I get the selected date. Selecting another date and clicking Cancel returns the predefined date.

Internally the control (TDatePicker) uses the Date property to store the Value.
Before you change the value on component you can load this value and store it.
At the end you can see if there are changes in the Date property, with respect to the initial value.
If there are no changes, it may be due to:
Change has been canceled.
The same date has been selected.
In both cases there is no need to update the TStringGrid.
Test something like this:
...
private
oldValue:tDate;
...
procedure TForm3.DatePicker1Click(Sender: TObject);
begin
oldDate := DatePicker1.Date;
end;
procedure TForm3.DateTimePicker1CloseUp(Sender: TObject);
begin
if DatePicker1.Date <> OldDate then
StringGrid1.Cells[col, row] := FormatDateTime('dd/mm/yyyy', DatePicker1.Date);
end;

After trying I found a solution at least for Mouse Input:
GetCursorPos(MausPos);
MausPos := DatePicker1.ScreenToClient(MausPos);
if (MausPos.X > DatePicker1.Width / 2) then
ShowMessage('Cancel')
else
ShowMessage('OK');
I take the Cursor position relative to the TDatePicker. If the X-Coordinate is on the right half, the cancel Button is pressed and I can exit the function without writing the date to the StringGrid.

Related

How to know when the USER changed the text in a TMemo/TEdit?

I was always bugged by the fact that TMemo (and other similar controls) only have the OnChange event. I would like to know when the USER changed the text, not when the text was changed programmatically.
I know two methods to discriminate between the user changed text and programmatically changed text:
Put OnChange:= NIL before you change the text programmatically. Then restore the OnChange. This is error prone as you need to remember to do it every time you change text from the code (and to which memos/edits needs this special treatment to be applied). Now we know that every time the OnChange is called, the control was edited by user.
Capture the OnKeyPress, MouseDown, etc events. Decide if the text was actually changed and manually call the code that needs to be called when user edited the ext. This could add a big amount of procedures to an already large file.
There is a more elegant way to do it?
You can write a helper procedure to do your option 1, and use it in your framework whenever you want to ensure no OnChange event is triggered when you set the text. e.g.:
type
TCustomEditAccess = class(TCustomEdit);
procedure SetEditTextNoEvent(Edit: TCustomEdit; const AText: string);
var
OldOnChange: TNotifyEvent;
begin
with TCustomEditAccess(Edit) do
begin
OldOnChange := OnChange;
try
OnChange := nil;
Text := AText;
finally
OnChange := OldOnChange;
end;
end;
end;
TMemo has also the Lines property which also triggers OnChange, so you can make another similar procedure that accepts Lines: TStrings argument.
How about using the Modified property?
procedure TForm1.MyEditChange(Sender: TObject);
begin
if MyEdit.Modified then
begin
// The user changed the text since it was last reset (i.e. set programmatically)
// If you want/need to indicate you've "taken care" of the
// current modification, you can reset Modified to false manually here.
// Otherwise it will be reset the next time you assign something to the
// Text property programmatically.
MyEdit.Modified := false;
end;
end;

How to make QR Page Footer or Band to be under Band Details not at the page end?

I need to make report, that has summary after Details had printed, not after every each Detail.
I only know the Page Footer, but it's at page bottom not after the Detail Band.
Is there a QRBand that could go after Detail Band?
Or can you make PageFooter height resize on every page?
Summary Bands print only at the end of the report immediately after the last 'detail' (unless the AlignToBottom property is set to true).
You should add a TQRBand to the report and set the BandType property to rbSummary.
EDIT
If you need to show intermediate results on every page you could add the FooterBand and the SummaryBand.
Summary Bands prints only at the end of the report (last page) and you can use the BeforePrint event of the summary to disable the footer band.
EDIT2
You can also try with a QRGroupBand and a QRFooterBand.
In the GroupBand:
use the Expression property for separating one group of products from another (the band could also be empty)
set appropriately the FooterBand property. This is where you link the header band to the correct footer band and encapsulate your group inside. Place the header band first and then go back and place the footer band by dropping a QRBand component and immediately changing the Type from rbTitle to rbGroupFooter. Once you have done this, you can go back to the QRGroup header and select the right footer band to use.
The FooterBand is where you are printing summary totals for each product.
I was able to solve the problem with the help of quickreport events and datasource events.
In a test project, it is enough to put a tqrlabel in the pagefooter, the rest is written below.
type
TForm1 = class(TForm)
...
private
firstValue: Integer;
public
counter: Integer;
end;
var
Form1: TForm1;
PageSummery: Integer;
procedure TForm1.Button2Click(Sender: TObject);
begin
Application.CreateForm(TForm2, Form2);
adoquery.First;
counter := 0;
firstValue := adoquery.FieldByName('id').AsInteger;
PageSummery := 0;
Form2.QuickRep1.Preview;
Form2.Free;
end;
procedure TForm1.adoqueryAfterScroll(DataSet: TDataSet);
begin
if counter <= 1 then
PageSummery := firstValue + adoquery.FieldByName('id').AsInteger
else
begin
PageSummery := PageSummery + adoquery.FieldByName('id').AsInteger;
form2.QRLabel2.Caption := IntToStr(PageSummery - Form1.adoquery.FieldByName('id').AsInteger);
end;
counter := counter + 1;
end;
Pay attention to the location of the variables in the first form.
The first button is used to fill the query data.
After each increase, the data is placed in the qrlabel so that all the data is saved when the event is triggered.
The final point is in the quickreport event, which occurs before filling the print rows when creating a new page. In this event, a number of variables must be set to zero.
procedure TForm2.QuickRep1StartPage(Sender: TCustomQuickRep);
begin
if Form1.counter = 0 then
PageSummery := 0
else
PageSummery := Form1.adoquery.FieldByName('id').AsInteger;
end;
Add a GroupHeader and a GroupFooter to the report. Make the Expression on the GroupHeader = PAGENUMBER, and set Height = 0; in it's BeforePrint event handler, so that it never shows. Set the GroupHeader's FooterBand property to the GroupFooter band. Then put your page totals or whatever on the GroupFooter. This will print after the detail on every page, and the Summary will print after that on the last page!

How to get notified when the user clicks the header column check box in VirtualTreeView control?

Is there an event notifying about the VirtualTreeView header column check box click? It is the check box highlighted on this picture:
Write a handler for the OnHeaderClick event and check if the HitPosition property of the HitInfo parameter contains the hhiOnCheckbox flag. For example:
procedure TForm1.VirtualTreeHeaderClick(Sender: TVTHeader; HitInfo: TVTHeaderHitInfo);
begin
if hhiOnCheckbox in HitInfo.HitPosition then
begin
if Sender.Columns[HitInfo.Column].CheckState = csCheckedNormal then
ShowMessage('Checked!')
else
ShowMessage('Unchecked!')
end;
end;

Delphi: Name of Method that is called for updating content of TxcDateEdit (or TEdit) control needed

I'm writing a routine which checks the input of an TcxDateEdit (from DevExpress). After a number is typed in, it should check it and try to autocomplete the rest of the content. In this case, if the user types in the day in an empty TcxDateEdit Control it should automatically fill in the current month and year.
The problem is I need to fire this autocompletion method after the number is typed by the user and visually added in the TcxDateEdit Control. So that I can check the actual input. But for this I'm searching for the name of the method which is used by this control to actually update the TcxDateEdit. I don't mean a method which implies focus lost, I mean a method which is called by simply typing in the control after (or while) each typed key is added to the String Variable with the content of this control. I'm pretty sure that a similar method exists even in the common TEdit control. So if somebody could tell me the name of this method I would be very thankful.
Thanks in advanced!
What you ask is possible using the OnChange event of the Properties, as shown in following image
Using this event and the EditingText property for your edit, you can use following code:
uses DateUtils, StrUtils;
procedure TForm1.cxDateEdit1PropertiesChange(Sender: TObject);
var
Month : Word;
Year : Word;
Day : Word;
BarPos : integer;
DateEdit : TcxDateEdit;
begin
DateEdit := (Sender as TcxDateEdit);
BarPos := Pos('/', DateEdit.EditingText);
if BarPos > 0 then
begin
Month := MonthOf(Now);
Year := YearOf(Now);
Day := StrToInt(LeftStr(DateEdit.EditingText, BarPos -1 ));
DateEdit.Date := EncodeDate(Year, Month, Day);
end;
end;
This code automatically assigns current Month and Year when Day is typed in by the user, but you can adapt it to whatever are your requirements.
I tested this on Delphi 2006 with version 6.42 of the DevExpress ExpressEditors Library.

Having problems with adoTable

i would like to be able to ensure that if a table (in this case adotHours) enters Edit or insert mode and the user clicks the save button but accidentally posts the same value under the Hours column as was already entered, a message appears Please enter another value, otherwise if the user enters a different value, another piece of code is used.
I have tried the following method but it doesn't work,- whatever the user enters the message Please enter another value appears.
procedure TfrmLabour.Button6Click(Sender: TObject);
var
i,j, t: String;
begin
Edit1.Text := adotHours['Hours'];
j := Edit1.Text;
adotHours.Post;
Edit2.Text := adotHours['Hours'];
t := Edit2.Text;
if t = j then
showmessage ('Please enter another value')
else begin.....
end;
while i know it may not be the most elegant code, my thinking was that once the value had been posted to adotHours Hours through the connected DBgrid, if it was a different value to before t would become the new value and therefore adotHours['Hours'] would be different and allow the else begin. Suggestions?
Try checking the entered value against the OldValue property.
adotHours.FieldByName('Hours').OldValue
adotHours.FieldByName('Hours').NewValue will be Unassigned if the field value is not modified. In that case, OldValue will contain the (unmodified) value.
You could use something like this:
if adotHours.FieldByName('ItemName').OldValue =
adotHours.FieldByName('ItemName').Value then
Showmessage(Please enter another value');
But as for me, it would be better to put this code in event adotHours.BeforePost and call the abort procedure if values are the same - in other case, when user changes value and clicks on other row on grid - it cause Post in dataset, and you may miss your check in button click.

Resources