B
Bacchus
Unregistered / Unconfirmed
GUEST, unregistred user!
以前看过一些关于制作透明Form的议题,解决方案大概是在CreateParam中
设置WS_EX_TRANSPARENT,并在OnCreate中设置Brush.Style := bsClear。
试了一下,发现并不OK。于是按照这个思路写了一个完整的程序。如下:
-----Unit1.pas-----
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Buttons, StdCtrls, ExtCtrls, AppEvnts;
type
TForm1 = class(TForm)
SpeedButton1: TSpeedButton;
Shape1: TShape;
ApplicationEvents1: TApplicationEvents;
procedure FormCreate(Sender: TObject);
procedure SpeedButton1Click(Sender: TObject);
procedure ApplicationEvents1Deactivate(Sender: TObject);
private
FPaintFlag: Boolean;
procedure WmPaint(var Msg: TMessage); message WM_PAINT;
procedure WmMove(var Msg: TMessage); message WM_MOVE;
procedure WmNcHitTest(var Msg: TWmNcHitTest); message WM_NCHITTEST;
procedure RepaintDesktop;
protected
procedure CreateParams(var Params: TCreateParams); override;
public
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
{ TForm1 }
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.ExStyle := Params.ExStyle or WS_EX_TRANSPARENT;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Brush.Style := bsClear;
end;
procedure TForm1.RepaintDesktop;
var R: TRect;
begin
R := Rect(Left, Top, Left+Width, Top+Height);
RedrawWindow(HWND_DESKTOP, @R, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_ALLCHILDREN);
Sleep(300);
FPaintFlag := True;
end;
procedure TForm1.WmPaint(var Msg: TMessage);
begin
if not FPaintFlag then
RepaintDesktop()
else
begin
FPaintFlag := False;
PaintHandler(TWmPaint(Msg));
end;
end;
procedure TForm1.WmMove(var Msg: TMessage);
begin
inherited;
RepaintDesktop();
end;
procedure TForm1.WmNcHitTest(var Msg: TWmNcHitTest);
var Ctrl: TControl;
P: TPoint;
begin
P.x := Msg.XPos;
P.y := Msg.YPos;
P := ScreenToClient(P);
Ctrl := ControlAtPos(P, False);
if Ctrl = SpeedButton1 then
Msg.Result := HTCLIENT
else if Ctrl = Shape1 then
Msg.Result := HTCAPTION
else
Msg.Result := HTNOWHERE;
end;
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
Close();
end;
procedure TForm1.ApplicationEvents1Deactivate(Sender: TObject);
begin
Invalidate();
end;
end.
-----Unit1.dfm-----
object Form1: TForm1
Left = 192
Top = 107
BorderStyle = bsNone
Caption = 'Form1'
ClientHeight = 157
ClientWidth = 301
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object Shape1: TShape
Left = 24
Top = 8
Width = 145
Height = 129
Brush.Color = clYellow
Pen.Color = clRed
Pen.Width = 3
Shape = stCircle
end
object SpeedButton1: TSpeedButton
Left = 136
Top = 8
Width = 25
Height = 25
Caption = 'X'
OnClick = SpeedButton1Click
end
object ApplicationEvents1: TApplicationEvents
OnDeactivate = ApplicationEvents1Deactivate
Left = 96
Top = 64
end
end
这个程序基本上已经OK,主要还有两个缺陷:
1、在重画背景时,ReDrawWindow不等重画完就返回,不得不Sleep一下。
但无论Sleep多久都难以保证背景已经重画好。
2、窗口的不可见部分仍然遮挡其他窗口。
后来发现了SetWindowRgn函数,问题一下子变得简单。如下:
-----t1.pas-----
unit t1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Buttons, ExtCtrls;
type
TForm1 = class(TForm)
Shape1: TShape;
SpeedButton1: TSpeedButton;
procedure FormCreate(Sender: TObject);
procedure SpeedButton1Click(Sender: TObject);
private
{ Private declarations }
procedure WmNcHitTest(var Msg: TWmNcHitTest); message WM_NCHITTEST;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
var Rgn1, Rgn2: HRGN;
begin
with SpeedButton1 do
Rgn1 := CreateRectRgn(Left, Top, Left+Width, Top+Height);
with Shape1 do
Rgn2 := CreateEllipticRgn(Left, Top, Left+Width+1, Top+Height+1);
CombineRgn(Rgn1, Rgn1, Rgn2, RGN_OR);
DeleteObject(Rgn2);
SetWindowRgn(Handle, Rgn1, True);
end;
procedure TForm1.WmNcHitTest(var Msg: TWmNcHitTest);
var Ctrl: TControl;
P: TPoint;
begin
P.x := Msg.XPos;
P.y := Msg.YPos;
P := ScreenToClient(P);
Ctrl := ControlAtPos(P, False);
if Ctrl = SpeedButton1 then
Msg.Result := HTCLIENT
else if Ctrl = Shape1 then
Msg.Result := HTCAPTION
else
Msg.Result := HTNOWHERE;
end;
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
Close();
end;
end.
-----t1.dfm-----
object Form1: TForm1
Left = 192
Top = 107
BorderStyle = bsNone
Caption = 'Form1'
ClientHeight = 212
ClientWidth = 333
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object Shape1: TShape
Left = 48
Top = 32
Width = 161
Height = 121
Brush.Color = clYellow
Pen.Color = clRed
Pen.Width = 3
Shape = stEllipse
end
object SpeedButton1: TSpeedButton
Left = 184
Top = 40
Width = 33
Height = 33
Caption = 'X'
OnClick = SpeedButton1Click
end
end
这是制作不规则形状Form的最好方法。但不可用于让背景半透过的情况。
可以观察金山词霸2000的透明提示条,当背景改变时(例如让它遮住时钟),
画面就不同步了。可以说半透明是没有完善解决方案的。
望各路大侠在此不吝赐教,提出不同的见解。
设置WS_EX_TRANSPARENT,并在OnCreate中设置Brush.Style := bsClear。
试了一下,发现并不OK。于是按照这个思路写了一个完整的程序。如下:
-----Unit1.pas-----
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Buttons, StdCtrls, ExtCtrls, AppEvnts;
type
TForm1 = class(TForm)
SpeedButton1: TSpeedButton;
Shape1: TShape;
ApplicationEvents1: TApplicationEvents;
procedure FormCreate(Sender: TObject);
procedure SpeedButton1Click(Sender: TObject);
procedure ApplicationEvents1Deactivate(Sender: TObject);
private
FPaintFlag: Boolean;
procedure WmPaint(var Msg: TMessage); message WM_PAINT;
procedure WmMove(var Msg: TMessage); message WM_MOVE;
procedure WmNcHitTest(var Msg: TWmNcHitTest); message WM_NCHITTEST;
procedure RepaintDesktop;
protected
procedure CreateParams(var Params: TCreateParams); override;
public
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
{ TForm1 }
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.ExStyle := Params.ExStyle or WS_EX_TRANSPARENT;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Brush.Style := bsClear;
end;
procedure TForm1.RepaintDesktop;
var R: TRect;
begin
R := Rect(Left, Top, Left+Width, Top+Height);
RedrawWindow(HWND_DESKTOP, @R, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_ALLCHILDREN);
Sleep(300);
FPaintFlag := True;
end;
procedure TForm1.WmPaint(var Msg: TMessage);
begin
if not FPaintFlag then
RepaintDesktop()
else
begin
FPaintFlag := False;
PaintHandler(TWmPaint(Msg));
end;
end;
procedure TForm1.WmMove(var Msg: TMessage);
begin
inherited;
RepaintDesktop();
end;
procedure TForm1.WmNcHitTest(var Msg: TWmNcHitTest);
var Ctrl: TControl;
P: TPoint;
begin
P.x := Msg.XPos;
P.y := Msg.YPos;
P := ScreenToClient(P);
Ctrl := ControlAtPos(P, False);
if Ctrl = SpeedButton1 then
Msg.Result := HTCLIENT
else if Ctrl = Shape1 then
Msg.Result := HTCAPTION
else
Msg.Result := HTNOWHERE;
end;
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
Close();
end;
procedure TForm1.ApplicationEvents1Deactivate(Sender: TObject);
begin
Invalidate();
end;
end.
-----Unit1.dfm-----
object Form1: TForm1
Left = 192
Top = 107
BorderStyle = bsNone
Caption = 'Form1'
ClientHeight = 157
ClientWidth = 301
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object Shape1: TShape
Left = 24
Top = 8
Width = 145
Height = 129
Brush.Color = clYellow
Pen.Color = clRed
Pen.Width = 3
Shape = stCircle
end
object SpeedButton1: TSpeedButton
Left = 136
Top = 8
Width = 25
Height = 25
Caption = 'X'
OnClick = SpeedButton1Click
end
object ApplicationEvents1: TApplicationEvents
OnDeactivate = ApplicationEvents1Deactivate
Left = 96
Top = 64
end
end
这个程序基本上已经OK,主要还有两个缺陷:
1、在重画背景时,ReDrawWindow不等重画完就返回,不得不Sleep一下。
但无论Sleep多久都难以保证背景已经重画好。
2、窗口的不可见部分仍然遮挡其他窗口。
后来发现了SetWindowRgn函数,问题一下子变得简单。如下:
-----t1.pas-----
unit t1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Buttons, ExtCtrls;
type
TForm1 = class(TForm)
Shape1: TShape;
SpeedButton1: TSpeedButton;
procedure FormCreate(Sender: TObject);
procedure SpeedButton1Click(Sender: TObject);
private
{ Private declarations }
procedure WmNcHitTest(var Msg: TWmNcHitTest); message WM_NCHITTEST;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
var Rgn1, Rgn2: HRGN;
begin
with SpeedButton1 do
Rgn1 := CreateRectRgn(Left, Top, Left+Width, Top+Height);
with Shape1 do
Rgn2 := CreateEllipticRgn(Left, Top, Left+Width+1, Top+Height+1);
CombineRgn(Rgn1, Rgn1, Rgn2, RGN_OR);
DeleteObject(Rgn2);
SetWindowRgn(Handle, Rgn1, True);
end;
procedure TForm1.WmNcHitTest(var Msg: TWmNcHitTest);
var Ctrl: TControl;
P: TPoint;
begin
P.x := Msg.XPos;
P.y := Msg.YPos;
P := ScreenToClient(P);
Ctrl := ControlAtPos(P, False);
if Ctrl = SpeedButton1 then
Msg.Result := HTCLIENT
else if Ctrl = Shape1 then
Msg.Result := HTCAPTION
else
Msg.Result := HTNOWHERE;
end;
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
Close();
end;
end.
-----t1.dfm-----
object Form1: TForm1
Left = 192
Top = 107
BorderStyle = bsNone
Caption = 'Form1'
ClientHeight = 212
ClientWidth = 333
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object Shape1: TShape
Left = 48
Top = 32
Width = 161
Height = 121
Brush.Color = clYellow
Pen.Color = clRed
Pen.Width = 3
Shape = stEllipse
end
object SpeedButton1: TSpeedButton
Left = 184
Top = 40
Width = 33
Height = 33
Caption = 'X'
OnClick = SpeedButton1Click
end
end
这是制作不规则形状Form的最好方法。但不可用于让背景半透过的情况。
可以观察金山词霸2000的透明提示条,当背景改变时(例如让它遮住时钟),
画面就不同步了。可以说半透明是没有完善解决方案的。
望各路大侠在此不吝赐教,提出不同的见解。