10.1.4 为窗体添加属性
让我们来看一个使用属性进行封装的具体示例。这一次,我不打算创建一个自定义类,而是要修改IDE为你创建的可视窗体生成的窗体类;同时,我还将充分利用类完成功能。
当一个应用程序包含多个窗体时,通常希望能够从一个窗体中访问另一个窗体的信息。你可能会考虑添加一个公共字段,但这通常是一个不好的主意。每当你想要让某个窗体的信息对其他窗体可用时,最好使用属性。
只需在窗体类的声明中编写属性的名称和类型:
property Clicks: Integer;
然后按下Ctrl+Shift+C激活IDE的代码自动完成功能,你将看到以下效果:
typeTFormProp = class(TForm)privateFClicks: Integer;procedure SetClicks(const Value: Integer);publicproperty Clicks: Integer read FClicks write SetClicks;end;implementationprocedure TForm1.SetClicks(const Value: Integer);
beginFClicks := Value;
end;
毫无疑问,这样可以节省很多击键的工作。现在,当用户单击窗体时,你可以通过编写以下代码来增加点击计数,就像我在FormProperties示例的窗体的OnMouseDown事件中所做的那样:
Clicks := Clicks + 1;
你可能会想,直接增加FClicks怎么样?嗯,在这种特定情况下可能会起作用,但你也可以使用SetClicks方法来更新用户界面并实际显示当前值。如果绕过属性直接访问字段,用于更新用户界面的setter方法中的附加代码将不会执行,显示可能会不同步。
这种封装的另一个优势是,你可以从另一个窗体中以一种恰当的抽象方式引用点击次数。实际上,窗体类中的属性除了可以用于访问自定义值,还可以用于封装对窗体组件的访问。例如,如果你有一个窗体包含一个用于显示某些信息的标签,并且你希望从其他窗体修改这个标签的文本,你可能会倾向于编写:
Form1.StatusLabel.Text := 'new text';
这是一个常见的做法,但不是一个好的做法,因为它不提供对窗体结构或组件的任何封装。如果你在应用程序的许多地方都有类似的代码,然后稍后决定修改窗体的用户界面(也许你希望每次更新标签时都发生一些事情),那么你将不得不在许多地方修复代码。
另一个选择是使用方法,而更好的选择是使用属性来隐藏特定的控件,如下所示:
property StatusText: string read GetStatusText write SetStatusText;
如果你创建了上面的代码行,然后再次按下Ctrl+Shift+C组合键,编辑器将添加两个用于读取和写入属性的方法的定义:
function TFormProp.GetStatusText: string;
beginResult := LabelStatus.Text
end;procedure TFormProp.SetStatusText(const Value: string);
beginLabelStatus.Text := Value;
end;
请注意,在这种情况下,属性不是映射到类的本地字段,而是映射到窗体子对象的字段,即Label的字段(如果你使用自动代码生成,请记住要实际删除编辑器可能已经为你添加的FStatusText属性)。
在程序的其他窗体中,你可以简单地引用窗体的StatusText属性,如果用户界面发生变化,只有属性的Set和Get方法的实现会受到影响。即使在窗体的其他方法中,通常也应使用属性而不是访问底层实现:
procedure TFormProp.SetClicks(const Value: Integer);
beginFClicks := Value;StatusText := FClicks.ToString + ' clicks';
end;