导读:
akTop in FAnchors then Top := 0;
if akRight in FAnchors then
Left := Screen.Width - Width;
if akBottom in FAnchors then
Top := Screen.Height - Height;
end else
begin
if akLeft in FAnchors then Left := -Width + cOffset;
if akTop in FAnchors then Top := -Height + cOffset;
if akRight in FAnchors then
Left := Screen.Width - cOffset;
if akBottom in FAnchors then
Top := Screen.Height - cOffset;
end;
end; 在这里,我们首先定义一个常量cOffset去表示窗体隐藏后显露部分的大小。然后我们利用WindowFromPoint这个Windows API函数检测鼠标是否位于窗体之上。接下来的判断就是处理在显示和隐藏状态下窗体Left 和Top 属性值的设置。注意,针对Fanchors中存在不同值的情况,窗体Left和Top的设置是各不相同的,但是这些设置只有顺序上的差异而并没有优先级别的差异。(为什么要提到这一点呢?) 最后需要注意的是:在本事件中Top、Left、Width和Height都是窗体Form1的属性值。 好了,有关窗体隐藏的核心代码已经介绍完毕了,不过要达到预期效果,窗体Form1在创建时还必须做一些准备工作,代码如下: procedure TForm1.FormCreate(Sender: TObject);
begin
Timer1.Enabled := False;
Timer1.Interval := 200;
FormStyle := fsStayOnTop;
end; 这里的代码相对简单,不过值得指出的是对Form1的FormStyle 属性的设置。FormStyle 为fsStayOnTop时可保证Form1始终位于最前显示。从效果角度看,当系统工具栏为“总在最前显示”时是最为明显的,因为若窗体移动到系统工具栏上时也不会被其所遮盖。 四、进一步完善 上面的代码已经基本实现了窗体的自动隐藏效果,但是我在介绍代码的时候有两个问题是被提出但没有被解答的。 首先是为什么触发隐藏时Fanchors中将至少有一个值而不多于两个值呢?注意代码中对Fanchors的赋值是通过四个判断进行的, 那么如果触发隐藏的话,Fanchors中将毫无疑问会有一个值存在,但这种情况是针对隐藏发生在屏幕的四边而言。当窗体被推入到屏幕的四角时,那么Fanchors中便将会有两个值存在。那此时窗体会隐藏到什么地方呢? 实际的效果告诉我们,窗体会被隐藏到屏幕的四角上。此时若我们试图让窗体重新显示,你便会发现窗体在不断的闪烁。为什么呢?这就是第二个问题提出的原因了。因为对窗体显示或隐藏的处理是根据Fanchors中的值作出的。当Fanchors中有两个值的时候,就将会引发对窗体属性的两次设置。而因为设置语句只有顺序差异而没有优先级差异,那么OnTimer事件中每次都会对窗体进行两次的属性值设置,从而导致我们看到闪烁的显示效果。 怎么去解决这个问题呢?我们再观察一下QQ的处理。在2003 II版的QQ里面,窗体的隐藏效果作了一定的调整:当窗体在屏幕左右两边隐藏时,它会自动充满屏幕的左右两边且高度不可改变;当窗体脱离屏幕两边的隐藏区域后,窗体的大小会恢复为隐藏前的大小,如图五所示。(注意:窗体并非是完全充满屏幕的两边。QQ在处理这个效果时可能只注意了系统工具栏总在最前显示且位于屏幕下方的情况,所以其充满的区域也只是屏幕顶端到系统工具栏上方的一段空间,如图六所示。)这样的处理可以令窗体即使被推入到屏幕四角,也可以保证只会对其中的一个隐藏方向进行处理,从而避免了前面出现的闪烁现象。
图五 Q Q 窗体自动充满屏幕两边
图六 Q Q 窗体自动充满屏幕两边的漏洞 结合前面的分析,要实现如上的效果还是从拦截WM_MOVING消息入手。重写后的WMMOVING过程如下: procedure TForm1.WMMOVING(var Msg: TMessage);
begin
inherited;
with PRect(Msg.LParam)^ do
begin
if (akLeft in FAnchors) or (akRight in FAnchors) then
begin
if (Left >0) and (Right
begin
if rec_Position then
begin
Bottom := top + Lst_Height;
Right := Left + Lst_Width;
Height := Lst_Height;
Width := Lst_Width;
end;
end else
begin
SetBarHeight;
Top := Cur_Top;
Bottom := Cur_Bottom;
exit;
end;
end;
Left := Min(Max(0, Left), Screen.Width - Width);
..
if not Rec_Position then
begin
Lst_Height := form1.Height;
Lst_Width := form1.width;
end;
FAnchors := [];
..
if (akLeft in FAnchors) or (akRight in FAnchors) then
begin
Rec_Position := True;
SetBarHeight;
Top := Cur_Top;
Bottom := Cur_Bottom;
end else
Rec_Position := False;
Timer1.Enabled := FAnchors <>[];
end;
end; 在新的代码中,我们首先使用了三个新定义的全局变量,分别是: Lst_Height : Integer; //记录窗体隐藏前的高度
Lst_Width : Integer; //记录窗体隐藏前的宽度
Rec_Position : Boolean; //是否启动窗体宽高记录标志 然后加入了三个判断代码块。 在第一个判断中首先判定窗体在移动前是否位于屏幕左右两边的隐藏区域。若为真,则判断窗体是否从隐藏区域向屏幕中央移动(注意,存在此判断的原因是因为我们还可能将窗体往屏幕两边推动)。若再为真,则恢复窗体隐藏前的大小;反之,强制设置矩形的Top和Bottom值并退出消息的处理。 第二个判断在于记录窗体的宽高值。Rec_Position 是记录窗体宽高的标志,它的值在第三个判断中进行设置。若窗体在移动前位于屏幕两边的隐藏区域,则Rec_Position为True,此时窗体的高度已经固定,记录已经无意义。所以只在Rec_Position为False时才需要记录窗体的宽高。 第三个判断位于Fanchors值设置之后。它根据窗体的位置对矩形的显示效果进行判断处理。判断也是基于窗体是否位于屏幕两边进行,为True则设置矩形的高度并设置Rec_Position的值为True。 在第三个判断中使用了一个新定义的过程SetBarHeight,其代码如下: procedure TForm1.SetBarHeight;
var
AppBarData : TAPPBARDATA;
begin
AppBarData.cbSize := SIZEOF(AppBarData);
If SHAppBarMessage(ABM_GETSTATE,AppBarData) AND ABS_AUTOHIDE) <>0 then
begin
Cur_Top := 1;
Cur_Bottom := Screen.Height - 1;
end else
begin
SHAppBarMessage(ABM_GETTASKBARPOS,AppBar
本文转自
http://www.cnsoft.cn/Exploiture/Programme/Delphi/200507/4335_2.html
akTop in FAnchors then Top := 0;
if akRight in FAnchors then
Left := Screen.Width - Width;
if akBottom in FAnchors then
Top := Screen.Height - Height;
end else
begin
if akLeft in FAnchors then Left := -Width + cOffset;
if akTop in FAnchors then Top := -Height + cOffset;
if akRight in FAnchors then
Left := Screen.Width - cOffset;
if akBottom in FAnchors then
Top := Screen.Height - cOffset;
end;
end; 在这里,我们首先定义一个常量cOffset去表示窗体隐藏后显露部分的大小。然后我们利用WindowFromPoint这个Windows API函数检测鼠标是否位于窗体之上。接下来的判断就是处理在显示和隐藏状态下窗体Left 和Top 属性值的设置。注意,针对Fanchors中存在不同值的情况,窗体Left和Top的设置是各不相同的,但是这些设置只有顺序上的差异而并没有优先级别的差异。(为什么要提到这一点呢?) 最后需要注意的是:在本事件中Top、Left、Width和Height都是窗体Form1的属性值。 好了,有关窗体隐藏的核心代码已经介绍完毕了,不过要达到预期效果,窗体Form1在创建时还必须做一些准备工作,代码如下: procedure TForm1.FormCreate(Sender: TObject);
begin
Timer1.Enabled := False;
Timer1.Interval := 200;
FormStyle := fsStayOnTop;
end; 这里的代码相对简单,不过值得指出的是对Form1的FormStyle 属性的设置。FormStyle 为fsStayOnTop时可保证Form1始终位于最前显示。从效果角度看,当系统工具栏为“总在最前显示”时是最为明显的,因为若窗体移动到系统工具栏上时也不会被其所遮盖。 四、进一步完善 上面的代码已经基本实现了窗体的自动隐藏效果,但是我在介绍代码的时候有两个问题是被提出但没有被解答的。 首先是为什么触发隐藏时Fanchors中将至少有一个值而不多于两个值呢?注意代码中对Fanchors的赋值是通过四个判断进行的, 那么如果触发隐藏的话,Fanchors中将毫无疑问会有一个值存在,但这种情况是针对隐藏发生在屏幕的四边而言。当窗体被推入到屏幕的四角时,那么Fanchors中便将会有两个值存在。那此时窗体会隐藏到什么地方呢? 实际的效果告诉我们,窗体会被隐藏到屏幕的四角上。此时若我们试图让窗体重新显示,你便会发现窗体在不断的闪烁。为什么呢?这就是第二个问题提出的原因了。因为对窗体显示或隐藏的处理是根据Fanchors中的值作出的。当Fanchors中有两个值的时候,就将会引发对窗体属性的两次设置。而因为设置语句只有顺序差异而没有优先级差异,那么OnTimer事件中每次都会对窗体进行两次的属性值设置,从而导致我们看到闪烁的显示效果。 怎么去解决这个问题呢?我们再观察一下QQ的处理。在2003 II版的QQ里面,窗体的隐藏效果作了一定的调整:当窗体在屏幕左右两边隐藏时,它会自动充满屏幕的左右两边且高度不可改变;当窗体脱离屏幕两边的隐藏区域后,窗体的大小会恢复为隐藏前的大小,如图五所示。(注意:窗体并非是完全充满屏幕的两边。QQ在处理这个效果时可能只注意了系统工具栏总在最前显示且位于屏幕下方的情况,所以其充满的区域也只是屏幕顶端到系统工具栏上方的一段空间,如图六所示。)这样的处理可以令窗体即使被推入到屏幕四角,也可以保证只会对其中的一个隐藏方向进行处理,从而避免了前面出现的闪烁现象。
图五 Q Q 窗体自动充满屏幕两边
图六 Q Q 窗体自动充满屏幕两边的漏洞 结合前面的分析,要实现如上的效果还是从拦截WM_MOVING消息入手。重写后的WMMOVING过程如下: procedure TForm1.WMMOVING(var Msg: TMessage);
begin
inherited;
with PRect(Msg.LParam)^ do
begin
if (akLeft in FAnchors) or (akRight in FAnchors) then
begin
if (Left >0) and (Right
begin
if rec_Position then
begin
Bottom := top + Lst_Height;
Right := Left + Lst_Width;
Height := Lst_Height;
Width := Lst_Width;
end;
end else
begin
SetBarHeight;
Top := Cur_Top;
Bottom := Cur_Bottom;
exit;
end;
end;
Left := Min(Max(0, Left), Screen.Width - Width);
..
if not Rec_Position then
begin
Lst_Height := form1.Height;
Lst_Width := form1.width;
end;
FAnchors := [];
..
if (akLeft in FAnchors) or (akRight in FAnchors) then
begin
Rec_Position := True;
SetBarHeight;
Top := Cur_Top;
Bottom := Cur_Bottom;
end else
Rec_Position := False;
Timer1.Enabled := FAnchors <>[];
end;
end; 在新的代码中,我们首先使用了三个新定义的全局变量,分别是: Lst_Height : Integer; //记录窗体隐藏前的高度
Lst_Width : Integer; //记录窗体隐藏前的宽度
Rec_Position : Boolean; //是否启动窗体宽高记录标志 然后加入了三个判断代码块。 在第一个判断中首先判定窗体在移动前是否位于屏幕左右两边的隐藏区域。若为真,则判断窗体是否从隐藏区域向屏幕中央移动(注意,存在此判断的原因是因为我们还可能将窗体往屏幕两边推动)。若再为真,则恢复窗体隐藏前的大小;反之,强制设置矩形的Top和Bottom值并退出消息的处理。 第二个判断在于记录窗体的宽高值。Rec_Position 是记录窗体宽高的标志,它的值在第三个判断中进行设置。若窗体在移动前位于屏幕两边的隐藏区域,则Rec_Position为True,此时窗体的高度已经固定,记录已经无意义。所以只在Rec_Position为False时才需要记录窗体的宽高。 第三个判断位于Fanchors值设置之后。它根据窗体的位置对矩形的显示效果进行判断处理。判断也是基于窗体是否位于屏幕两边进行,为True则设置矩形的高度并设置Rec_Position的值为True。 在第三个判断中使用了一个新定义的过程SetBarHeight,其代码如下: procedure TForm1.SetBarHeight;
var
AppBarData : TAPPBARDATA;
begin
AppBarData.cbSize := SIZEOF(AppBarData);
If SHAppBarMessage(ABM_GETSTATE,AppBarData) AND ABS_AUTOHIDE) <>0 then
begin
Cur_Top := 1;
Cur_Bottom := Screen.Height - 1;
end else
begin
SHAppBarMessage(ABM_GETTASKBARPOS,AppBar
本文转自
http://www.cnsoft.cn/Exploiture/Programme/Delphi/200507/4335_2.html