我有一个树状视图作为主菜单。程序启动后,我添加了新的子项。
然后我做TreeView1.Perform(TVM_SETITEMHEIGHT, 28, 0);
在设计时,我将Form的Position属性设置为poDesigned。如果在运行时执行Position := poScreenCenter;
,为什么TreeView1.Perform无法正常工作?
这是我的代码:
procedure TForm1.FormCreate(Sender: TObject);
begin
TreeView1.Items.AddChild(TreeView1.Items.Item[0],'Sub Menu1');
TreeView1.Items.AddChild(TreeView1.Items.Item[0],'Sub Menu2');
TreeView1.Items.AddChild(TreeView1.Items.Item[0],'Sub Menu3');
TreeView1.Items.AddChild(TreeView1.Items.Item[0],'Sub Menu4');
TreeView1.Perform(TVM_SETITEMHEIGHT, 28, 0);
Position:=poScreenCenter;
end;
问题是,设置窗体的Position属性会导致调用RecreateWnd。 RecreateWnd意味着销毁Windows屏幕对象并从头开始构建它。似乎需要(或最简单的方法)来完全实现更改此属性的所有效果。窗口句柄的创建并不少见:例如更改窗体的BorderStyle甚至是Edit控件的BorderStyle都会导致调用RecreateWnd。
RecreateWnd级联为重新创建所有子窗口控件,也就是重新创建TreeView。通常,组件从其内部(属性,私有数据)知道如何重新创建自身。例如:TreeView在句柄销毁之前将其节点保存到临时内存流,并在句柄重新创建后将其重新加载。
所以,应该归咎于谁:窗体的Position属性,TreeView或两者都不是?如果没有TreeView的ItemHeight属性,则必须手动发送WinAPI消息。这是对未由VCL注册的控件的修改。到目前为止,解释了为什么会发生这种情况。
最佳解决方案是确保每次重新创建TreeView时都完成了自定义。不幸的是,没有活动可用。您将需要覆盖CreateWnd(请参见下面的更新)。但是,当您保留Ctl3D和BorderStyle属性不变时,也可以在父级级别控制它。我不得不重写CM_ShowingChanged,因为不幸的是,在TForm1.CreateWnd之后TreeView尚未完全重建:
TForm1 = class(TForm)
TreeView1: TTreeView;
...
private
procedure CMShowingChanged(var Message: TMessage);
message CM_SHOWINGCHANGED;
end;
procedure TForm1.CMShowingChanged(var Message: TMessage);
begin
inherited;
TreeView1.Perform(TVM_SETITEMHEIGHT, 28, 0);
end;
按照下面的注释的要求,这是使用重写的TTreeView.CreateWnd的解决方案:
unit Unit1;
interface
uses
Windows, Classes, Controls, Forms, StdCtrls, ComCtrls, CommCtrl, XPMan;
type
TTreeView = class(ComCtrls.TTreeView)
protected
procedure CreateWnd; override;
end;
TForm1 = class(TForm)
TreeView1: TTreeView;
XPManifest1: TXPManifest;
procedure FormCreate(Sender: TObject);
end;
implementation
{$R *.dfm}
{ TTreeView }
procedure TTreeView.CreateWnd;
begin
inherited CreateWnd;
Perform(TVM_SETITEMHEIGHT, 38, 0);
end;
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
begin
TreeView1.Items.AddChild(TreeView1.Items.Item[0],'Sub Menu1');
TreeView1.Items.AddChild(TreeView1.Items.Item[0],'Sub Menu2');
TreeView1.Items.AddChild(TreeView1.Items.Item[0],'Sub Menu3');
TreeView1.Items.AddChild(TreeView1.Items.Item[0],'Sub Menu4');
Position := poScreenCenter;
end;
end.
并且,如果您不喜欢继承TTreeView,则重写该窗体的CreateWnd,但是在这种情况下,您需要按Andreas Rejbrand的回答调用HandleNeeded:
unit Unit1;
interface
uses
Windows, Classes, Controls, Forms, StdCtrls, ComCtrls, CommCtrl, XPMan;
type
TForm1 = class(TForm)
TreeView1: TTreeView;
XPManifest1: TXPManifest;
procedure FormCreate(Sender: TObject);
protected
procedure CreateWnd; override;
end;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
TreeView1.Items.AddChild(TreeView1.Items.Item[0],'Sub Menu1');
TreeView1.Items.AddChild(TreeView1.Items.Item[0],'Sub Menu2');
TreeView1.Items.AddChild(TreeView1.Items.Item[0],'Sub Menu3');
TreeView1.Items.AddChild(TreeView1.Items.Item[0],'Sub Menu4');
Position := poScreenCenter;
end;
procedure TForm1.CreateWnd;
begin
inherited CreateWnd;
TreeView1.HandleNeeded;
TreeView1.Perform(TVM_SETITEMHEIGHT, 38, 0);
end;
end.
NGLN的答案给出了问题的解释。
[如果您只需要设置树视图项目的高度,则同时设置Position
(或将导致重新创建窗口(从技术角度而言)的表单的任何其他属性),你可以做
procedure TForm1.FormCreate(Sender: TObject);
begin
Position := poDesigned;
TreeView1.HandleNeeded;
TreeView1.Perform(TVM_SETITEMHEIGHT, 28, 0);
end;