請問各位:Create與CreateWnd的區別?(100分)

  • 主题发起人 maple_guo
  • 开始时间
M

maple_guo

Unregistered / Unconfirmed
GUEST, unregistred user!
他們各自的功能,調用的順序有什麽不同?
然後是我在改寫TDBGrid的CreateWnd時出現了一個怪問題(應該是我對CreateWnd的用法不了解所至),請看我的code:
代码:
constructor TMyDBGrid.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FSortOnCreate := True;  //用於排序
end;

procedure TMyDBGrid.CreateWnd;
begin
  inherited;
  if FSortOnCreate then
    Columns[0].Title.Caption := Columns[0].Title.Caption + ' ▲';
end;
問題:當我第一次打開(Create)窗體時正常,但關閉窗體(Free)後隨即又打開同一窗體,Columns[0].Title.Caption就會出現兩個' ▲',再打開就是三個。

以上可能表達得不太清楚,請幫忙,謝謝!
 
应该是它释放得不完全吧!
我之前也碰过类似的问题。
我的解决方法是定义一个信号灯,比如:
constructor TMyDBGrid.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Fflag:=false;
FSortOnCreate := True; //用於排序
end;

procedure TMyDBGrid.CreateWnd;
begin
inherited;
if FSortOnCreate then
if not Fflag then
begin
Columns[0].Title.Caption := Columns[0].Title.Caption + ' ▲';
Fflag:=true;
end;
end;

 
CreateWnd可能会被反复调用,但三角形只能加一个,所以得在CreateWnd过程中判断
三角形加上去的代码是否已经执行过了。也就是只在CreateWnd中调用一次加三角形的代码。
 
在Delphi中,Create一般是用来构造对象用的,TMyDBGrid.Create构造一个TMyDBGrid的实例,
而CreateWnd而是用来创建一个实例对应的窗体,其实就是通知Windows给TMyDBGrid的实例分
配一个窗口句柄,比如下面一段代码:
procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Text:=IntToStr(Edit1.Handle);
Edit1.Ctl3D:=not Edit1.Ctl3D;
end;

整个窗体上放一个TEdit实例和一个TButton实例,当单击Button时会用Edit来显示它自身
的Handle属性,然后改变它的Ctl3D属性,实验发现每按一次,显示的内容都会变,可见一个
窗口控件在生存期中并不唯一使用同一个Handle,某些属性比如Ctl3D属性的改变会导致
Windows重新为其分配句柄,而分配句柄的过程就是由CreateWnd来完成的:

procedure TWinControl.CreateWnd;
var
Params: TCreateParams;
TempClass: TWndClass;
ClassRegistered: Boolean;
begin
CreateParams(Params);
with Params do
begin
if (WndParent = 0) and (Style and WS_CHILD <> 0) then
if (Owner <> nil) and (csReading in Owner.ComponentState) and
(Owner is TWinControl) then
WndParent := TWinControl(Owner).Handle
else
raise EInvalidOperation.CreateFmt(SParentRequired, [Name]);
FDefWndProc := WindowClass.lpfnWndProc;
ClassRegistered := GetClassInfo(WindowClass.hInstance, WinClassName, TempClass);
if not ClassRegistered or (TempClass.lpfnWndProc <> @InitWndProc) then
begin
if ClassRegistered then Windows.UnregisterClass(WinClassName,
WindowClass.hInstance);
WindowClass.lpfnWndProc := @InitWndProc;
WindowClass.lpszClassName := WinClassName;
if Windows.RegisterClass(WindowClass) = 0 then RaiseLastOSError;
end;
CreationControl := Self;
CreateWindowHandle(Params);
//这里根据调用的方法的名称就看得出来它是分配句柄用的.
if FHandle = 0 then RaiseLastOSError;
end;
StrDispose(FText);
FText := nil;
UpdateBounds;
Perform(WM_SETFONT, FFont.Handle, 1);
if AutoSize then AdjustSize;
end;


再看看CreateWindowHandle的源码:
procedure TWinControl.CreateWindowHandle(const Params: TCreateParams);
begin
with Params do
FHandle := CreateWindowEx(ExStyle, WinClassName, Caption, Style,
X, Y, Width, Height, WndParent, 0, WindowClass.hInstance, Param);
end;
只不过的重新调用CreateWindowEx这个API函数.

根据以上的说明,可知一个Delphi窗口对象,它在生存期Create只会执一次,但CreateWnd可
能执行多次,我来知道你为什么会在CreateWnd方法中改变标题(看得出来好象是要显示当
前是哪一例被排序)我觉得应该定义一个成员来保存当前排序的列号,在需要时把这个序号
赋成0或其他值,如果需要初始化这个成员则直接在Create构造函数中赋值.而在绘制表格时
才将排序列标题加上一'▲'号输出,这样并不改变列的标题值本身,可以保证无论排序到哪
列都不会出现显示几个'▲'的情况!
 
学习,今天正好看书看到这个。
 
To aizb:
谢谢你的回复,但有一问题,我的Form是在Free掉之后再用Create创建的,也就是说它的生存期已经结束了,但就是出现了上述的情况,怎么解释呢[?]
还有一个相似的问题:我用的是MDIChildForm,当同样包含该控件的几个不同的Form先后被创建后就出现了' ▲'不断增加的问题。实在是想不通,又不能调试控件里的代码,希望你能回复,谢谢!
 
一个TWinControl控件在生存期间就有可能多次调用CreateWnd方法来重建窗口,
改变窗口某项属性就有可能导置窗口被重建, 比如设置Parent属性...

你可以在你的CreateWnd中加上一个ShowMessage()来观察这种效果:)

如果你只是想要给你的对象赋初始值, 应该在构造函数Create中做, 一般情况下不
需要重载CreateWnd函数.
 
To skyweb:
问题是我需要访问控件的Columns,但在Create中却会导致错误,所以我才会在CreateWnd是处理,不过现在已经知道那样做并不合适,谢谢你!
但我想不明白为何Free掉之后和创建类似窗体还会出现相同的问题(就是我楼上的楼上所说的问题)。
 
在Windows中,当一个对象被释放(Free)后,它点用的内存被交还给系统,但是内存中的数据并不会清0,
如果后面的对象用到这段空间,而正好和种属性又是相同的,再加上这些属性又没有显式初始化,其结果就
可能导至这些控件的属性沿用了前面Free掉的控件的属性.如果你的程序真的是把Form Free掉了,
就有可能是这种情况.还有就你的TMyDBGrid的实例是否是你Free掉的那个Form窗体的成员,如果不是
则有可能你把Form Free了但是TMyDBGrid实例并没有Free.而导到你的问题.

这认为解决问题的办法就是不要在CreateWnd中处理而是象我上面的方法那样设置一个成员变量来处理.
 
明白了,感谢各位,特别是aizb兄的耐心解释,谢谢!给分了。
 
顶部