问题
In Delphi, sometimes we need to do this...
function TForm1.EDIT_Click(Sender: TObject);
begin
(Sender As TEdit).Text := '';
end;
...but sometimes we need to repeat the function with other object class like...
function TForm1.COMBOBOX_Click(Sender: TObject);
begin
(Sender As TComboBox).Text := '';
end;
...because the operator As
does not accept flexibility. It must know the class in order to allow the .Text
that come after the ()
.
Sometimes the code gets full of similar functions
and procedures
because we need to do the same thing with similar visual controls that we can't specify.
This is only an case of use example. Generally I use these codes on more complex codes to achieve a standard objective on many controls and other kind of objects.
Is there an alternative or trick to make these tasks more flexible?
回答1:
Use RTTI to perform common tasks on similarly-named properties of unrelated classes, eg:
Uses
..., TypInfo;
// Assigned to both TEdit and TComboBox
function TForm1.ControlClick(Sender: TObject);
var
PropInfo: PPropInfo;
begin
PropInfo := GetPropInfo(Sender, 'Text', []);
if Assigned(PropInfo) then
SetStrProp(Sender, PropInfo, '');
end;
In some cases, some controls use Text
and some use Caption
instead, eg;
function TForm1.ControlClick(Sender: TObject);
var
PropInfo: PPropInfo;
begin
PropInfo := GetPropInfo(Sender, 'Text', []);
if not Assigned(PropInfo) then
PropInfo := GetPropInfo(Sender, 'Caption', []);
if Assigned(PropInfo) then
SetStrProp(Sender, PropInfo, '');
end;
回答2:
you can use the is
operator, try this sample
if Sender is TEdit then
TEdit(Sender).Text:=''
else
if Sender is TComboBox then
TComboBox(Sender).Text:='';
回答3:
You can eliminate the messy type-casting by using the absolute keyword which allows you to declare variables of different types occupying the same memory location, in this case the same location as the event parameter.
You still need to perform the type checking using "is" but in other respects this approach is a bit cleaner but just as safe.
procedure TMyForm.ControlClick(Sender: TObject);
var
edit: TEdit absolute Sender;
combo: TComboBox absolute Sender;
:
begin
if Sender is TEdit then
edit.Text := ''
else if Sender is TComboBox then
combobox.Text := ''
else
:
end;
I wrote in more detail about using this language feature in my blog almost 3 years ago.
回答4:
I'm posting my comment as an answer because I don't see any answer here that mentions this. SetTextBuf is a public method of TControl. This method is utilized to populate the internal text data member via the SetText windows message. This is how the a TControl descendant updates both the Caption and Text properties. So all TControl descendants, such as TButton, TEdit, TComboBox will work using the following type of code. And you don't have to use RTTI.
function TForm1.EDIT_Click(Sender: TObject);
begin
(Sender as TControl).SetTextBuf('Text or Caption'); // will work for both the Caption and text property
end;
回答5:
I don't know if you are using the tag property for anything but it can be useful for these situations. Setting the tag of all Tedits to say 1 and the tag of all Tcomboboxes to 2 etc could let you do:
if Sender is TControl then
Case TControl(Sender).tag of
1: TEdit(sender).text := '';
2: Tcombobox(sender).text := '';
3....etc
end;
Just a thought and it looks neater and easier to read/debug:)
回答6:
Thanks to you people, specially @RemyLebeau, I could make this universal function that applies do any kind of Win Control or Data Base Control. It turns the control in Red (or whatever color you want) if it's Required but empty, if it has repeated information on the Data Base, or whatever other condition we want to check. It return numbers instead of true or false, so we can send only one message at the end of many checks and tell the user how many error did he/she made.
function CheckInput(Control: TWinControl; Condition: Boolean; EmptyState: Integer; Msg: String): Integer;
var
PropInfo: PPropInfo;
begin
{ os controles que precisam passar por condições para que seu conteúdo seja aceito }
Result := 0;
if EmptyState = ciNotEmpty then
begin
PropInfo := GetPropInfo(Control, 'Text', []);
if Assigned(PropInfo) then
begin
if GetStrProp(Control, PropInfo) = '' then
begin
Condition := False;
Msg := ciEmptyMsg;
end;
end;
end;
if not Condition then
begin
Result := 1;
PropInfo := GetPropInfo(Control, 'Color', []);
if Assigned(PropInfo) then SetPropValue(Control, PropInfo, ciErrorColor);
if Msg <> '' then ShowMessage(Msg);
end
else
begin
PropInfo := GetPropInfo(Control, 'Color', []);
if Assigned(PropInfo) then SetPropValue(Control, PropInfo, ciNormalColor);
end;
end;
回答7:
If you go all the way down, you'll notice that both TEdit and TCombobox descend from TControl. If you look which method they use to set their text then you'll see it's the method implemented by TControl. That's why you can do something ugly like:
if (sender is TEdit) or (sender is TComboBox) then
TEdit(sender).Text:='test';
You have to make sure that all objects you put in here use the same method internally or your application will break in mysterious ways.
来源:https://stackoverflow.com/questions/11335829/how-to-use-sender-parameter-with-as-operator-for-more-then-one-class-at-a-ti