How can I change the TEdit default error message (NumbersOnly mode)? - delphi

How can I change the TEdit's default error message when I use it in NumbersOnly mode. I mean this error:
Unacceptable character You can only type a number here
Is it possible to change this message ?

I don't know a direct way to change the value of that message (which is handled by Windows) but you can show your own message and then avoid to show the original windows hint ballon, using the Abort procedure in the OnKeyPress Event.
Check this sample
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if not (CharInSet(Key,['0'..'9',#8,#9])) then
begin
ShowHintMessage('Only numbers please');//you must write this function
Abort;//this will prevent which the original windows hint was shown
end;
end;
You must we aware which this code will be prevent the execution of the clipboard operations over the control.
Update
I Update the code to allow the Tab(#9) and Back space(#8) chars.

Looking at the VCL source, it looks like that message is generated by windows, rather than by Delphi. That is, the VCL is only wrapping the functionality that exists in windows. So it doesn't appear that it would be easy to modify the message.

Related

Can Delphi TField.OnValidate restore the original value without raising exception?

I usually implement validation logic as:
procedure TMyDM.IBQueryAMOUNTValidate(
Sender: TField);
begin
inherited;
if Sender.AsFloat>100
then raise Exception.Create('Amount is too large!');
end;
The question is - is there chance not to raise Exception in the OnValidate (which stops further processing), but to restore silently original value in the OnValidate and proceed with OnChange, CheckBrowseMode and all the GUI updates that are called by the CheckBrowseMode/Post?
Of course, I know that I can always replace OnValidate logic with OnChange logic that handles OldValue and NewValue but it seems to me that code would be cleaner it I stick with OnValidate.
Don't use OnValidate to do anything apart from raise an exception to reject the
Sender's value.
To see why, set up a simple test app consisting of a TClientDataSet with fields ID (Integer) and Name (String(20)), TDataSource,
TDBNavigator, TDBGrid and TDBEdit for the Name field. Add the following code:
procedure TForm1.ClientDataSet1NameValidate(Sender: TField);
begin
if Sender.AsString = 'x' then
Sender.DataSet.Cancel;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ClientDataSet1.CreateDataSet;
ClientDataSet1.InsertRecord([1, 'a']);
ClientDataSet1.InsertRecord([2, 'b']);
ClientDataSet1.InsertRecord([3, 'c']);
end;
Compile, run, and enter 'x' (without the quotes) into the DBEdit. Then click Save on the DBNavigator.
Notice that the edit is cancelled but the 'x' remains in the DBEdit. This is
in Delphi 10.2.3, btw. Back in D7 days, it was even worse - the wrong row in the
DB grid would show the 'x'!
The other thing is that OnValidate is never actually called in TDataSet's methods,
only descendants, e.g. TClientDataSet. So there is no guarantee that in general
OnValidate will be called at all or at the right time - it's up to the dataset-type's author to get it right.
So I think the answer to your q is "No", leave the OnValidate to raise exceptions but no more.
It seems to me, the only purpose of OnValidate event is raising exceptions. From Delphi help (http://docwiki.embarcadero.com/Libraries/Berlin/en/Data.DB.TField.OnValidate):
To reject the current value of the field from the OnValidate event
handler, raise an exception
...
If writing the data does not raise an
exception, the OnChange event handler is called to allow a response to
the change.
For tasks of validation I use OnSetText event, which gives me possibility to silent restore original value if new one is not acceptable.

DELPHI ERROR : Multiple-step operation generater error. Check each status value

I have a procedure when click a bitbutton, it open a dialog box to load some files, and add it into AdoQuery (AQSource1). When I add some files, this error appear :
"Multiple-step operation generater error. Check each status value."
Only when I add multiple files selected. But if I selected a file by a file there is no error at all... But sometimes if I select multiple files this error did not show up either.... Kind a confusing for me...
How to fix this ? in simple way...
PS:
I use Windows 7 Pro SP1 64bit, Embarcadero Delphi 2010
procedure TFMain1.btImgLoad1Click(Sender: TObject);
var i : integer;
strFilename : string;
begin
if OpenDialog1.Execute then
begin
// Add selected file to DBase and show it on DBGrid
for i := 0 to openDialog1.Files.Count-1 do
begin
// ShowMessage(openDialog1.Files[i]);
strfilename := openDialog1.Files[i];
AQSource1.Append;
AQSource1source_fileurl.Value := strFilename;
AQSource1source_filename.Value := ExtractFileName(strfilename);
AQSource1source_dateadd.Value := date();
AQSource1source_timeadd.Value := Time();
AQSource1.Post;
AQSource1.Close;
AQSource1.Open;
end;
end;
end;
Ah... Finally I found what the cause of it. It lies on the "Field size" in Access and AdoQuery in Delphi. The field size for both is 50. When I change them to 255, whola.... the error is gone....
So based on my conclusion, the error for "Multiple-step operation generater error. Check each status value." for my case was caused by the FIELD SIZE... Thanks ^^
this error usually occur when there is a values change on the server side and the changes are not being reflected on the client.for example when on/before insert trigger that change field value .
so all you need that is to change Adotable1.CursorLocation to the option clUseServer . no thing else.
good luck
I have similar experience, In one instance, when I have synthesized the required SQL text, and did not care about alias name & the ADO makes that alias automatically (which is a long name) this error occurs. the solution is to give an alias directly in the statement to by pass this short coming.

DELPHI Edit.OnExit by TAB, show window result on focus bug

I'm having trouble with the following scenario:
2 Edit's
Type something in Edit1 and press TAB, focus goes to Edit2
Edit1.OnExit -> show a Form with a message "Processing..." (makes a lengthy validation)
After the form closes, the focus on Edit2 seems to be "crashed"...
- the hole TEXT in Edit2 isn't selected
- the carret isn't flashing
Example:
Create a new form
Put 2 edits
Set this as OnExit event in Edit1:
procedure TForm1.Edit1Exit(Sender: TObject);
begin
with TForm.CreateNew(self) do
try
Width := 100;
Height := 50;
Position := poMainFormCenter;
show;
sleep(200);
finally
Free;
end;
end;
Run the application
Set focus in the Edit1 and press TAB
I'm using:
Delphi 7 Enterprise
Windows 7 x64
This is a known problem. Windows has problems when you change focus before it's completed the last focus change (eg., focus starts changing from Edit1 to Edit2, but Edit1.OnExit does something to change focus to another control or form.
This happens, for instance, when apps try to do validations in an OnExit event and then try to return focus to the original control when the validation fails.
The easiest solution is to post a message to your form handle in the OnExit instead, and handle the focus change need there. It will fire once the target control gets the input focus, and Windows doesn't get confused.
const
UM_EDIT1_EXITED = WM_USER + 1;
type
TForm1=class(TForm)
...
private
procedure UMEdit1Exited(var Msg: TMessage); message UM_EDIT1_EXITED;
end;
implementation
procedure TForm1.Edit1Exit(Sender: TObject);
begin
PostMessage(Handle, UM_EDIT1_EXITED, 0, 0);
end;
procedure TForm1.UMEdit1Exited(var Msg: TMessage);
begin
// Show your other form here
end;
From an old Borland NG post by Dr. Peter Below of TeamB:
here is my general sermon on the "show dialog from OnExit" problem:
If an OnExit handler is triggered (which happens in response to the
Windows
message WM_KILLFOCUS) Windows is in the midst of a focus change. If you do
something in the handler that causes another focus change (like popping up
a message box or doing a SetFocus call) Windows gets terribly confused.
The
missing cursor is a symptom of that.
If you have to display a message to your user from an OnExit handler, do
it
this way:
Define a constant for a user message somewhere in the INterface
section
of your unit, above the type declaration for your form
'Const
UM_VALIDATE = WM_USER + 200;'
Give your Form a handler for this message, best placed in the private
section of the class declaration:
Procedure UMValidate( Var Msg: TMessage ); message UM_VALIDATE;
Post a UM_VALIDATE message to the form from the OnExit handler if
the contents of the field are not ok. You can pass additional
information in the wparam and lparam parameters of the message, e.g.
an error number and the Sender object. In fact you could do the whole
validation in the UMValidate handler!
I'm not sure precisely what's going on here, but it looks like the order of processing of messages is a bit messed up. Instead of killing your other form with Free, use Release and the focus will behave as you desire.
Another option is to use ShowModal instead of Show. Normally you show a processing dialog modally because you don't want the user making modifications to the main form whilst you are processing. If you do that then you can carry on using Free.

Can NIL be used for a Sender object?

Recently I was asked to automate a little sub-routine that presented a series of data records and any two of four potential buttons for the user to select after seeing an analysis of the record. The boss said having the users see the analysis was wasting time since the users invariably selected the number one choice in the button list and he was prepared to live with my guesses for all but the best of his users. So, he wanted a NEW series of buttons added to offer Handle Automatically, Handle Manually and Handle Case by Case. The last button would just run the already existing code. The second button would essentially do nothing and just exit. The first button? Well, that was the rub.
What I decided to do was to use a couple of flags and then have the automatic path just simulate the click of whatever sub-button was best, based on the analysis. The issue was that calling Button1Click(Sender) wasn't possible because the procedure running the Analysis was called RunAnalysis and wasn't attached to a specific object to pass the TObject through. I eventually refactored the guts of the Button1Click method into Button1Pressed and then called THAT from Button1Click. Thus I was able to call Button1Pressed from within RunAnalysis.
The avoided path would have been to call Button1Click(Nil). I didn't try it since I had an easy solution (Thanks Modelmaker, by the way). But my question is, would the nil reference have worked or would it have caused a disaster. Could I have called a higher function (randomly?) that did have a sender, JUST to have a sender object in the procedure call? Just how important IS the Sender object, if I don't use anything that actually REFERENCES the Sender?
System details: Delphi 7 in Win 7 programming environment for use in Windows XP.
Thanks in advance for any wisdom, GM
I tend to put the event handler's code into another method when possible:
procedure TForm1.DoSomething(const Test: Boolean);
begin
// Do Something based on Test
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
DoSomething(False); // init here
end;
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
DoSomething(TCheckBox(Sender).Checked);
end;
So when I have a need to call CheckBox1Click(nil) it's a good sign for me to pull off the code from the event handler into a separate method.
A callback or "event" is no different than any other function. You can pass a NIL reference anywhere you want, as long as you either (a) wrote that code and know it's nil safe, or (b) you have read that code and everything it calls without checking for nil, and know it's nil safe.
Some programmers use NIL freely and some consider using NIL parameter values to be bad style. My style tends to be "don't assume Sender is assigned and don't assume that it is of a particular type", which leads to lots of check-code in my event handlers, but other code than mine, it varies widely and so the First Rule of coding comes in; "Don't make assumptions. Read the code.".
You can use nil as a parameter to a TNotify event (or any other expecting a Sender), as long as the code doesn't reference Sender:
procedure TForm1.Button1Click(Sender: TObject);
begin
DoSomeStuff(nil);
end;
procedure TForm1.DoSomeStuff(Sender: TObject);
begin
// Safe
DoSomeOtherStuff;
// Safe
// Do stuff with Sender
if Sender is TButton then
TButton(Sender).Caption := 'In DoSomeStuff'
// NOT safe!
with TButton(Sender) do
begin
Caption := 'In DoSomeStuff';
end;
end;
Short Answer - yes.
I use it to distinguish whether (say) a menu Event was clicked by a user or called directly by code.

Delphi: Convert keys to letters, ignoring shiftsate

I'm trying to make a search feature where the user can hold in the control key and type some text to search.
I'm using the OnKeyDown/OnKeyUp to trap the control key.
Is there a easy way to check if the key parameter given to the onKeyUp/Down event is a litteral?
Or is it possible to convert the char given to OnKeyPressed while holding down the control key to the char it would have been if the control key where not pressed?
Edit:
I need a solution that can handle letters beyond the simple a..z range, like æ, ø å.
It looks like Delphi 2009 got a couple of useful methods in the TCharachter-class; e.g. function IsLetterOrDigit(C: Char): Boolean;
I'm stuck with delphi 2007, though...
The OnKeyDown & OnKeyPress Events have a number of Limitations.
Rather use a TApplicationEvents Component.
In it's OnShortCut Event hander use the following code
procedure TFormA.ApplicationEvents1ShortCut(var Msg: TWMKey; var Handled: Boolean);
begin
if (Msg.CharCode = Ord('B')) and (HiWord(Msg.KeyData) and KF_ALTDOWN <> 0) then
begin
Handled := True;
// Do what needs to be done;
end;
end;
This will trap ALT-B
have a look at Delphi - Using the TApplicationEvents OnShortCut event to detect Alt+C key presses for more info
You can convert the Key(Char) parameter from OnKeyPress event to it's ordinal value using Ord(Key) however, in the OnKeyDown event you can do the following
if CharInSet(Char(Key), ['A'..'Z']) then
do something
you might also want to play with ShiftState to check if ALT, CTRL and/or SHIFT key is down...
Here you have all the info:
Virtual Key Codes
Understanding and Processing Keyboard events in Delphi

Resources