Проблема заключается в том, что установка свойства Position формы приводит к вызову RecreateWnd. RecreateWnd означает уничтожение объекта экрана Windows и его создание с нуля. Похоже, что это необходимо (или самый простой способ), чтобы полностью реализовать все эффекты изменения этого свойства. Воссоздание оконных дескрипторов не редкость: например, изменение BorderStyle формы или даже элемента управления Edit приводит к вызову RecreateWnd.
RecreateWnd каскадно воссоздает все дочерние оконные элементы управления, в том числе и TreeView. Обычно компонент знает из своих внутренних компонентов (свойства, личные данные), как воссоздать себя. Например: TreeView сохраняет свои узлы во временном потоке памяти до уничтожения дескриптора и загружает его обратно после воссоздания дескриптора.
Итак, кто виноват: свойство Position формы, TreeView или ни то, ни другое? При отсутствии свойства ItemHeight для TreeView вы вынуждены вручную отправлять сообщение WinAPI. И это модификация элемента управления, который не зарегистрирован VCL. Пока объяснение, почему это происходит.
Лучшее решение - убедиться, что ваша настройка выполняется каждый раз, когда TreeView воссоздается. К сожалению, для этого нет событий. Вам нужно будет переопределить CreateWnd (см. Обновление ниже). Но если оставить свойства Ctl3D и BorderStyle без изменений, это также можно контролировать на уровне родителя. Я перешел к переопределению CM_ShowingChanged, потому что TreeView, к сожалению, еще не полностью перестроен после TForm1.CreateWnd:
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 формы, но в этом случае вам нужно вызвать HandleNeeded, как ответил Andreas Rejbrand :
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.