Delphi实现运行时控件的拖动、改变大小等,并且做到与控件类型的解耦

方法1:采用自定控件,自己继承一个Delphi中已有的控件,例如Panel

然后重写它的MouseDown,MouseMove,MouseUp等事件

缺点:此方法只能针对某一种控件达到效果,依赖性较强,复用性较低

方法2:通过Delphi提供的IDesignHook接口,实现对现有控件切换到设计时的编辑。

Delphi中帮助信息:

IDesignerHook is an interface that allows component writers to interact with the form designer in the IDE.

Unit

Forms

Description

IDesignerHook provides access to the form currently under construction in the form designer, and influences the behavior of the designer by specifying whether the object under construction is a form (as opposed to a composite component), drawing the grid that assists in layout, and so on.

Component writers can access the current IDesignerHook interface using the Designer property of a form.

我们通过实现接口中的方法来达到在运行时设计控件的目的。

在此,我们必须先将运行时控件切换为设计时。这时,TComponent的protected方法中有一个SetDesigning,在Delphi帮助中的说明如下:

Ensures that components inserted at design time have their design-mode flag set.

Delphi syntax:

procedure SetDesigning(Value: Boolean; SetChildren: Boolean=True);

保护的方法?protected, Oh,No,那外部类不是不能访问?这时有一个巧妙的方法破解它。我们通过友元来破解它,Delphi中在同一个单元的类互为友元类,但总不能把自己的程序写到Delphi库里面去吧,那就继承他,在要调用它的单元中,继承它,写一个TCrackComponent=class(Tcomponent)end;

然后,对要调用此方法的对象,先将其强制转换为TCrackComponent,例如

TCrackComponent,(Form).SetDesigning(False, True);

切换到设计时了,但是我们会发现,这个其实只是改变一个标签而已,说明这个控件是设计时,表面上看不出什么变化。这时我们再回到刚才那个IDesignHook接口,这个接口中,我们要实现一个IsDesignMsg方法

帮助中提供的说明如下:

Determines when the designer should handle a Windows message.

Delphi syntax:

function IsDesignMsg(Sender: TControl; var Message: TMessage): Boolean;

Description

IsDesignMsg is called for each message sent to a component in the designer. This method returns true if the message is a design message, meaning one the designer should handle for the component.

所有的控件的消息都会被传入这个接口中的这个方法,并且通过返回值判断是否拦截,如果为True则拦截,为了更好理解,我们进入Control的单元文件,找到wndproc方法

clip_image001

这里它先获取控件所属的Form,如果控件属于某个Form,这时,就把消息传送给Form.Designer,Designer是一个实现IDesignHook接口的对象,这时如果IsDesignMsg返回true,消息就被拦截了。

通过上面这个说明,我们也知道了,这个Form.Designer如果没有赋值,那消息也不会被拦截,所以我们需要将我们要对控件操作的内容写到一个实现IDesignHook接口的类中,赋值给Designer。

然后在这个类中的IsDesignMsg对消息进行处理,例如将所有鼠标键盘事件拦截。

现在,如果要只对Form中的某个控件中的控件进行设计,那要怎么办呢?我们先来看一下Tcomponent.SetDesigning中的源代码

procedure TComponent.SetDesigning(Value, SetChildren: Boolean);

var

I: Integer;

begin

if Value then

Include(FComponentState, csDesigning) else

Exclude(FComponentState, csDesigning);

if SetChildren then

for I := 0 to ComponentCount - 1 do Components[I].SetDesigning(Value);

end;

它是利用Comonents将其管理的所有控件设置为True或False,也就是说只有子构建的Owner是你要进行操作的控件,你才能设置它为设计时。要怎么办呢?我们自己也模仿他的递归,自己把控件中的子控件遍历一遍就好啦。代码如下:

procedure SetDesigning(Root:TWinControl);

var i:integer;

begin

TCrackComponent(Root).SetDesigning(True,false);

for i:=0 to root.ControlCount-1 do

begin

SetDesigning(TWinControl(root.Controls[i]));

end;

end;

感谢wr960204(武稀松)的文章,使我受益匪浅,本文是在看完他关于本话题的文章时候,理解总结改进得出的。

 

作者:DJ尐舞

欢迎转载,转载请注明出处 http://djbone.cnblogs.com

posted @ 2009-04-11 16:28  DJ尐舞  阅读(3305)  评论(2编辑  收藏  举报