为 Delphi/C++ Builder 增添 IP 控件
西安交通大学
刘明华
在进行网络设置时,我们常常碰到需要填写 IP 地址的情况,为此 Windows 引入了一个专门用来接受 IP 地址输入的控件(如图所示)。 Windows IP 地址编辑控件( IP Address edit control )是随着微软的 Internet Explorer 4.0 的推出而引入 Windows 操作系统的( Windows 98 和 Windows 2000 中都已经包含了此控件)。但是令人遗憾的是在 Inprise 公司最新版本的 Delphi 5 和 C++ Builder 5 中, Borland 可视化控件库( Visual Component Library )中居然没有提供 IP 地址编辑控件。
为了让 Delphi 和 C++ Builder 开发人员更方便地使用 IP 控件,本文介绍如何将它包装成一个可以在 Delphi 和 C++ Builder 的 IDE 环境中使用的可视化 Delphi 控件。同时本文也为读者学习 Delphi 控件编写提供了一个比较好的示例;它涉及了 Delphi 控件编写的以下方面的知识:将 Windows 窗口类封装成 Delphi 控件,将 Windows 消息映射成事件属性,多个属性共享 read 和 write 方法,以及属性编辑器( Property Editor )的构造。
IP 控件的属性、方法及事件
本文将为 Delphi/C++ Builder 的 IP 控件实现下面的属性、方法和事件。它们的简单介绍如下:
IP 属性 : IP 控件中的 IP 地址值 (32 位整数 LongWord) 。为了获取 IP 控件的 IP 值,我们只需要读取此属性即可;通过对 IP 属性赋值,我们就能够改变 IP 控件的内容。
Field0..Field3 属性 : Field0 到 Field3 分别为 IP 控件的 4 个部分的值。这四个属性也是为了读取和改变 IP 控件的 IP 值而设置的。
Field0Range..Field3Range 属性 : Field0Range 到 Field3Range 限制 IP 控件各部分的取值的范围。
Blank 属性 :判断 IP 控件的 IP 串是否为空,此属性为只读属性。
About 属性 :在 Object Inspector 中点击它可以显示控件的版本信息,添加此属性是为了提供一个编写属性编辑器的简单例子。
以下属性继承自 TWinControl 类: Font 、 TabOrder 、 TabStop 、 ParentShowHint 、 ShowHint 、 Hint 、及 Visible 。
TIPEdit 的事件属性有: OnFieldChange 、 OnChange 、 OnEnter 以及 OnExit 。
Clear 方法 :清空 IP 控件中的 IP 串。
SetFieldFocus 方法 :设置 IP 控件的输入焦点。
IP 控件的实现
// 文件名: IPEdit.PAS
{ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
// IP address edit control for Borland Delphi 5
// Implemented by Simon Liu (simon_liu@263.net)
{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
unit IPEdit;
interface
uses Windows, Messages, SysUtils, Classes,
Forms, Controls, ComCtrls, CommCtrl, DsgnIntf;
type
TFieldRange = record
LowRange: Byte;
HighRange: Byte;
end;
TFieldChangeEvent = procedure(Sender: TObject; OldField, Value: Byte) of Object;
// 定义 TAboutProperty 属性编辑器
TAboutProperty = class(TPropertyEditor)
public
procedure Edit; override;
function GetAttributes: TPropertyAttributes; override;
function GetValue: string; override;
end;
TIPEdit = class(TWinControl)
private
fAbout:TAboutProperty;
FIP: LongWord;
FFields:array[0..3] of Byte;
FFieldRanges: array [0..3] of TFieldRange;
FCreating: Boolean;
FOnChange: TNotifyEvent;
FOnEnter: TNotifyEvent;
FOnExit: TNotifyEvent;
FOnFieldChange: TFieldChangeEvent;
procedure SetIP(Value: LongWord);
function GetIP: LongWord;
function GetField(Index:Integer):Byte;
procedure SetField(Index:Integer; B:Byte);
function GetFieldRange(Field: Integer): TFieldRange;
procedure SetFieldRange(Field:integer; Value: TFieldRange);
procedure SetIPAddress;
function GetBlank: Boolean;
procedure WMNotifyFormat(var Message: TMessage); message WM_NOTIFYFORMAT;
// 处理 IP 控件的通知消息 IPN_FIELDCHANGED
procedure CNNotify(var Message: TWMNotify); message CN_NOTIFY;
procedure CNCommand(var Message: TWMCommand); message CN_COMMAND;
procedure WMSetFont(var Message: TWMSetFont); message WM_SETFONT;
protected
procedure CreateParams(var Params: TCreateParams); override;
procedure CreateWnd; override;
procedure DestroyWnd; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
// 清除 IP 控件中的 IP 串
procedure Clear;
// 设置 IP 控件的输入焦点( field 的有效取值为 0..3 )
procedure SetFieldFocus(Field:Byte);
published
// 判断 IP 控件的 IP 串是否为空
property Blank: Boolean read GetBlank;
//Field0 到 Field3 分别为 IP 控件的 4 个部分的值
property Field0: Byte index 0 read GetField write SetField;
property Field1: Byte index 1 read GetField write SetField;
property Field2: Byte index 2 read GetField write SetField;
property Field3: Byte index 3 read GetField write SetField;
//Field0Range 到 Field3Range 限制 IP 控件各部分的取值的范围
property Field0Range: TFieldRange index 0 read GetFieldRange write SetFieldRange;
property Field1Range: TFieldRange index 1 read GetFieldRange write SetFieldRange;
property Field2Range: TFieldRange index 2 read GetFieldRange write SetFieldRange;
property Field3Range: TFieldRange index 3 read GetFieldRange write SetFieldRange;
//IP 地址值 (32 位整数 LongWord)
property IP: LongWord read GetIP write SetIP;
//The About box
property About:TAboutProperty read fAbout;
// 事件属性
property OnChange: TNotifyEvent read FOnChange write FOnChange;
property OnEnter: TNotifyEvent read FOnEnter write FOnEnter;
property OnExit: TNotifyEvent read FOnExit write FOnExit;
property OnFieldChange: TFieldChangeEvent read FOnFieldChange write FOnFieldChange;
// 以下属性继承自 TWinControl 控件
property Enabled;
property TabOrder;
property TabStop;
property ParentShowHint;
property ShowHint;
property Hint;
property Visible;
end;
procedure Register;
implementation
{~~~~~~~~~~~~~~~~~TAboutProperty~~~~~~~~~~~~~~~~~~~~~}
procedure TAboutProperty.Edit;
begin
Application.MessageBox(
'TIPEdit for Delphi 5'#13
+'(C) 2000 by Simon Liu'#13
+'Email:simon_liu@263.net',
'About TIPEdit',MB_ICONINFORMATION);
end;
function TAboutProperty.GetAttributes: TPropertyAttributes;
begin
Result := [paDialog, paReadOnly];
end;
function TAboutProperty.GetValue: string;
begin
Result := '(Simon)';
end;
{~~~~~~~~~~~~~~~~~~~~~~TIPEdit~~~~~~~~~~~~~~~~~~~~~~~~}
constructor TIPEdit.Create(AOwner: TComponent);
var
i: integer;
begin
// 初始化 ICC_INTERNET_CLASSES 类控件
CheckCommonControl(ICC_INTERNET_CLASSES);
inherited Create(AOwner);
for i:= 0 to 3 do
begin
FFieldRanges[i].LowRange:= 0;
FFieldRanges[i].HighRange:= 255;
FFields[i]:=0;
end;
FIP:=0;
Height:= 25;
Width:= 152;
TabSTop:= True;
end;
procedure TIPEdit.DestroyWnd;
begin
inherited DestroyWnd
end;
destructor TIPEdit.Destroy;
begin
inherited Destroy;
end;
procedure TIPEdit.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
CreateSubClass(Params, WC_IPADDRESS);
with Params do
begin
end;
end;
procedure TIPEdit.CreateWnd;
begin
FCreating := True;
try
inherited CreateWnd;
SetIPAddress;
finally
FCreating := False;
end;
end;
function TIPEdit.GetBlank: Boolean;
begin
Result:= Boolean(SendMessage(Handle, IPM_ISBLANK, 0, 0));
end;
procedure TIPEdit.Clear;
begin
SendMessage(Handle, IPM_CLEARADDRESS, 0, 0);
end;
procedure TIPEdit.SetFieldFocus(Field:Byte);
begin
SendMessage(Handle, IPM_SETFOCUS, Field, 0);
end;
function TIPEdit.GetFieldRange(Field: Integer): TFieldRange;
begin
Result:= FFieldRanges[Field];
end;
procedure TIPEdit.SetFieldRange(Field: Integer; Value: TFieldRange);
begin
if Value.LowRange > Value.HighRange then exit;
if (FFieldRanges[Field].LowRange <> Value.LowRange) or
(FFieldRanges[Field].HighRange <> Value.HighRange) then
begin
FFieldRanges[Field]:= Value;
SendMessage(Handle, IPM_SETRANGE, Field,
MakeIPRange(Value.LowRange, Value.HighRange));
end;
end;
function TIPEdit.GetField(Index: Integer): Byte;
begin
if (Index>=0)and(Index<=3) then Result:=FFields[Index]
else Result:=0;
end;
procedure TIPEdit.SetField(Index:Integer; B:Byte);
begin
if (FFields[Index] <> B)then
begin
FFields[Index]:=B;
SetIPAddress;
end;
end;
procedure TIPEdit.SetIPAddress;
var
i:LongWord;
begin
i:=MAKEIPADDRESS(FFields[0], FFields[1], FFields[2], FFields[3]);
SendMessage(Handle, IPM_SETADDRESS, 0, i);
FIP:=i;
end;
procedure TIPEdit.SetIP(Value: LongWord);
begin
if (FIP <> Value) then
begin
FFields[0]:= First_IPAddress(Value);
FFields[1]:= Second_IPAddress(Value);
FFields[2]:= Third_IPAddress(Value);
FFields[3]:= Fourth_IPAddress(Value);
SetIPAddress;
end;
end;
function TIPEdit.GetIP: LongWord;
begin
SendMessage(Handle, IPM_GETADDRESS, 0, Integer(@Result));
end;
procedure TIPEdit.WMSetFont(var Message: TWMSetFont);
begin
// 不可以调用父控件的此方法
// 否则,控件不能正常工作
end;
procedure TIPEdit.WMNotifyFormat(var Message: TMessage);
begin
with Message do
Result := DefWindowProc(Handle, Msg, WParam, LParam);
end;
procedure TIPEdit.CNNotify(var Message: TWMNotify);
var
pNM: PNMIPAddress;
begin
with (Message.NMHdr)^ do
begin
case Code of
IPN_FIELDCHANGED:
begin
pNM:= PNMIPADDRESS(Message.NMHdr);
if (pNM^.iField>=0)and(pNM^.iField<=3) then
FFields[pNM^.iField]:=pNM^.iValue;
if Assigned(FOnFieldChange) then
FOnFieldChange(self, pNM^.iField, pNM^.iValue);
end;
end;
end;
end;
procedure TIPEdit.CNCommand(var Message: TWMCommand);
begin
case Message.NotifyCode of
EN_CHANGE:
begin
if not FCreating then
if Assigned(FOnChange) then FOnChange(self);
end;
EN_KILLFOCUS: if Assigned(FOnExit) then FOnExit(self);
EN_SETFOCUS: if Assigned(FOnEnter) then FOnEnter(self);
end;
end;
// 注册 TIPEdit 控件以及 TAboutProperty 属性编辑器
procedure Register;
begin
RegisterComponents('Simon', [TIPEdit]);
RegisterPropertyEditor(typeInfo(TAboutProperty),
TIPEdit, 'ABOUT', TAboutProperty);
end;
end.