开发者

Passing object in reference / one place to style objects

I got quite a large application which is currently being styled up. To save me changing all the buttons in the IDE/Object Inspector I am planning on just doing a few functions for the main objects like

procedure StyleButton(AButton : TButton)
begin
    AButton.Color := clGreen;
    AButton.Font.Style = [fsBold];
end;

etc etc and then add that to the forms onCreates as needed

StyleButton(Button1); whatever etc

There is no issue passing objects in params like this. It does just reference the first object right?

It works fine and I can't think of any issues, but because this is a large application which thousands of users I just want to be sure there will be no issues/memory leaks/resource consumpution issues.

Will also be doing similar things with TAdvStringGrid and TEdit/TMemo components.开发者_StackOverflow中文版

Then allows just 1 place to change these settings.

Or someone have a better idea?


This is an excellent idea. The function will modify whichever object you pass to it.

You are not passing by reference. You are passing by value. The value you are passing is a reference. "Passing by reference" means you'd use the var or out keywords, which are not appropriate in this situation.


Your idea is just fine, as the other answerers have already said. Just want to propose a solution that goes even further than David's and something you may want to consider in order to avoid having to add many statements like:

StyleButton(Button1);
StyleButton(Button2);

to each and every form for each and every control you would like to style;

What I would propose is to add a single method call to for example each form's OnShow event:

procedure TForm1.FormShow(Sender: TObject);
begin
  TStyler.StyleForm(Self);
end;

The TStyler could be implemented in a separate unit that looks like this:

interface

type
  TStyler = class;
  TStylerClass = class of TStyler;

  TStyler = class(TObject)
  public
    class procedure StyleForm(const aForm: TCustomForm);
    class procedure StyleControl(const aControl: TControl); virtual;
    class function GetStyler(const aControl: TControl): TStylerClass;
  end;

implementation

uses
  Contnrs;

type
  TButtonStyler = class(TStyler)
  public
    class procedure StyleControl(const aControl: TControl); override;
  end;

  TEditStyler = class(TStyler)
  public
    class procedure StyleControl(const aControl: TControl); override;
  end;

  TLabelStyler = class(TStyler)
  public
    class procedure StyleControl(const aControl: TControl); override;
  end;

var
  _Controls: TClassList;
  _Stylers: TClassList;

{ TStyler }

class function TStyler.GetStyler(const aControl: TControl): TStylerClass;
var
  idx: Integer;
begin
  Result := TStyler;
  idx := _Controls.IndexOf(aControl.ClassType);
  if idx > -1 then
    Result := TStylerClass(_Stylers[idx]);
end;

class procedure TStyler.StyleForm(const aForm: TCustomForm);

  procedure _StyleControl(const aControl: TControl);
  var
    i: Integer;
    StylerClass: TStylerClass;
  begin
    StylerClass := TStyler.GetStyler(aControl);
    StylerClass.StyleControl(aControl);
    if (aControl is TWinControl) then
      for i := 0 to TWinControl(aControl).ControlCount - 1 do
        _StyleControl(TWinControl(aControl).Controls[i]);
  end;

var
  i: Integer;
begin
  _StyleControl(aForm);
end;

class procedure TStyler.StyleControl(const aControl: TControl);
begin
// Do nothing. This is a catch all for all controls that do not need specific styling.
end;

{ TButtonStyler }

class procedure TButtonStyler.StyleControl(const aControl: TControl);
begin
  inherited;
  if aControl is TButton then
  begin
    TButton(aControl).Font.Color := clRed;
    TButton(aControl).Font.Style := [fsBold];
  end;
end;

{ TEditStyler }

class procedure TEditStyler.StyleControl(const aControl: TControl);
begin
  inherited;
  if aControl is TEdit then
  begin
    TEdit(aControl).Color := clGreen;
  end;
end;

{ TLabelStyler }

class procedure TLabelStyler.StyleControl(const aControl: TControl);
begin
  inherited;
  if aControl is TLabel then
  begin
    TLabel(aControl).Font.Color := clPurple;
    TLabel(aControl).Font.Style := [fsItalic];
  end;
end;

initialization
  _Controls := TClassList.Create;
  _Stylers := TClassList.Create;

  _Controls.Add(TButton);
  _Stylers.Add(TButtonStyler);

  _Controls.Add(TEdit);
  _Stylers.Add(TEditStyler);

  _Controls.Add(TLabel);
  _Stylers.Add(TLabelStyler);

finalization
  FreeAndNiL(_Controls);
  FreeAndNiL(_Stylers);
end.

This solution basically employs polymorphism and a registry that links control classes to styler classes. It also uses class procedures and functions to avoid having to instantiate anything.

Please note that the registry is implemented in this example as two lists that need to be kept in sync manually as the code assumes that finding a class at index X will find the styler at the same index in the other list. This can of course be improved upon very much, but is sufficient here to show the concept.


No, There is no issue (in your specific case) passing a object as parameter

procedure StyleButton(AButton : TButton)

when you do this you are passing a address memory (reference) and setting some properties of the referenced object, so there is not problem.


To add to what Rob and RRUZ have already said, you could consider an extra helper using open array parameters:

procedure StyleButtons(const Buttons: array of TButton);
var
  i: Integer;
begin
  for i := low(Buttons) to high(Buttons) do
    StyleButton(Buttons[i]);
end;

You can then call this as:

StyleButtons([btnOK, btnCancel, btnRelease64bitDelphi]);

which is, in my view, more readable at the call-site than:

StyleButton(btnOK);
StyleButton(btnCancel);
StyleButton(btnRelease64bitDelphi);

Note that I passed the open array as a const parameter because that is more efficient when dealing with arrays. Because each element of the array is itself a reference to the button, you are able to modify the actual button. The const just means that you cannot change the reference.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜