开发者

Item Height in Tree View does not change despite of sending message with Perform

I have a Tree View as a main menu. After program start, I add new sub items.

Then I do TreeView1.Perform(TVM_SETITEMHEIGHT, 28, 0);

At designtime I set the Form's Position property to poDesigned. Why does TreeView1.Perform not work if I do Position := poScreenCenter; in runtime?

This is my code:

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;


NGLN's answer gives the explanation for the problem.

If you need only to set the Position (or any other property of the form that will cause the window (in the technical sense) to be recreated) at the same time you set the height of the tree-view items, you can do

procedure TForm1.FormCreate(Sender: TObject);
begin
  Position := poDesigned;
  TreeView1.HandleNeeded;
  TreeView1.Perform(TVM_SETITEMHEIGHT, 28, 0);
end;


The issue is that setting the Position property of the Form results in call to RecreateWnd. RecreateWnd means destroying the windows screen object and building it up from scratch. It seems this is needed (or te most easiest way) to fully implement all the effects of changing this property. Recreation of window handles is not uncommon: e.g. changing the BorderStyle of a form, or even that of an Edit control results in a call to RecreateWnd.

RecreateWnd cascades into recreating all child windowed controls, also your TreeView. Normally, a component knows from its internals (properties, private data) how to recreate itself. For instance: a TreeView saves its nodes to a temporary memory stream prior to handle destruction, and loads it back in after handle recreation.

So, who is to blame: the Position property of the Form, the TreeView, or neither? In the absence of a ItemHeight property for the TreeView, you are forced to manually send a WinAPI message. And that is a modification to the control which is not registered by the VCL. So far the explanation as why this happens.

The best solution is to be sure your customization is done every time the TreeView is recreated. Unfortunately there is no event available for that. You would need to override CreateWnd (see update below). But when you leave the Ctl3D and BorderStyle properties intact, it is also possible to control this at the parent's level. I came down to overriding CM_ShowingChanged, because the TreeView unfortunately isn't yet completely rebuild after 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;

Update:

As requested in below comment, here is the solution with overriden 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.

And if you don't like to subclass TTreeView, then override CreateWnd of the form, but in that case you need to call HandleNeeded as answered by 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.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜