Author: Jonas Bilinkevicius
How to wallpaper the client area of a MDI parent form
Answer:
Solve 1:
Here are the basics of how it is done:
1 type
2 TForm1 = class(TForm)
3 Image1: TImage;
4 procedure FormCreate(Sender: TObject);
5 procedure FormDestroy(Sender: TObject);
6 private
7 { Private declarations }
8 FClientInstance,
9 FPrevClientProc: TFarProc;
10 procedure ClientWndProc(var message: TMessage);
11 public
12 end;
13
14 implementation
15
16 procedure TForm1.ClientWndProc(var message: TMessage);
17 var
18 MyDC: hDC;
19 Ro, Co: Word;
20 begin
21 with message do
22 case Msg of
23 WM_ERASEBKGND:
24 begin
25 MyDC := TWMEraseBkGnd(message).DC;
26 for Ro := 0 to ClientHeight div Image1.Picture.Height do
27 for Co := 0 to ClientWIDTH div Image1.Picture.Width do
28 BitBlt(MyDC, Co * Image1.Picture.Width, Ro * Image1.Picture.Height,
29 Image1.Picture.Width,
30 Image1.Picture.Height, Image1.Picture.Bitmap.Canvas.Handle, 0, 0,
31 SRCCOPY);
32 Result := 1;
33 end;
34 else
35 Result := CallWindowProc(FPrevClientProc, ClientHandle, Msg, wParam, lParam);
36 end;
37 end;
38
39 procedure TForm1.FormCreate(Sender: TObject);
40 begin
41 if FileExists(ExtractFilePath(Application.ExeName) + 'backgrnd.bmp') then
42 begin
43 Image1.Picture.Bitmap.LoadFromFile(ExtractFilePath(Application.ExeName) +
44 'backgrnd.bmp');
45 FClientInstance := MakeObjectInstance(ClientWndProc);
46 FPrevClientProc := Pointer(GetWindowLong(ClientHandle, GWL_WNDPROC));
47 SetWindowLong(ClientHandle, GWL_WNDPROC, LongInt(FClientInstance));
48 end;
49 end;
50
51 procedure TForm1.FormDestroy(Sender: TObject);
52 begin
53 if (FPrevClientProc <> nil) then
54 begin
55 FClientInstance := Pointer(GetWindowLong(ClientHandle, GWL_WNDPROC));
56 SetWindowLong(ClientHandle, GWL_WNDPROC, Longint(FPrevClientProc));
57 FreeObjectInstance(FClientInstance);
58 end;
59 end;
Solve 2:
You need to do some Windows API level stuff to hook the window proc of MDI client
window. This client window occupies the client area of an MDI main from - that's
why you can't see the results of your painting.
Here's an example of how you do that. It also illustrates how to create a temporary
canvas using a supplied Device Context to facilitate painting the image bitmap. The
code looks for the file argyle.bmp in the Windows directory. If you don't have that
bitmap, substitute another. Make sure you create an OnDestroy handler and copy the
code from FormDestroy here into that handler.
60 {Example of painting the background of an MDI form}
61
62 unit MDIPaint;
63
64 interface
65
66 uses
67 SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms,
68 Dialogs;
69
70 type
71 TForm1 = class(TForm)
72 procedure FormDestroy(Sender: TObject);
73 private
74 { Private declarations }
75 FClientInstance: pointer;
76 FOldClientProc: pointer;
77 FBackground: TBitmap;
78 procedure ClientProc(var message: TMessage);
79 public
80 { Public declarations }
81 procedure CreateWnd; override;
82 end;
83
84 var
85 Form1: TForm1;
86
87 implementation
88
89 {$R *.DFM}
90
91 procedure TForm1.ClientProc(var message: TMessage);
92 var
93 ARect: TRect;
94 x, y: integer;
95 SrcRect: TRect;
96 begin
97 {if the message is to erase background, tile with the background bitmap}
98 with message do
99 begin
100 if Msg = WM_ERASEBKGND then
101 begin
102 WinProcs.GetClientRect(ClientHandle, ARect);
103 with TCanvas.Create do
104 try
105 Handle := wParam;
106 SrcRect := Rect(0, 0, FBackground.Width, FBackground.Height);
107 y := 0;
108 while y < ARect.Bottom do
109 begin
110 x := 0;
111 while x < ARect.Right do
112 begin
113 CopyRect(Bounds(x, y, FBackground.Width, FBackground.Height),
114 FBackground.Canvas, SrcRect);
115 inc(x, FBackground.Width);
116 end;
117 inc(y, FBackground.Height);
118 end;
119 Result := 1;
120 finally
121 Handle := 0;
122 Free;
123 end;
124 end
125 else
126 {otherwise call the original window proc}
127 Result := CallWindowProc(FOldClientProc, ClientHandle, Msg, wParam, lParam);
128 end;
129 end;
130
131 procedure TForm1.CreateWnd;
132 begin
133 inherited CreateWnd;
134 if FormStyle = fsMDIForm then
135 begin
136 FBackground := TBitmap.Create;
137 FBackground.LoadFromFile('c:\windows\argyle.bmp');
138 FClientInstance := MakeObjectInstance(ClientProc);
139 FOldClientProc := pointer(SetWindowLong(ClientHandle, GWL_WNDPROC,
140 longint(FClientInstance)));
141 end;
142 end;
143
144 procedure TForm1.FormDestroy(Sender: TObject);
145 begin
146 {reset the original client proc, free the client instance and the bitmap}
147 SetWindowLong(ClientHandle, GWL_WNDPROC, longint(FOldClientProc));
148 FreeObjectInstance(FClientInstance);
149 FBackground.Free;
150 end;
151
152 end.
Solve 3:
Here are the steps to add a wallpaper to the client area of of a MDI parent form:
1. Create a new project
2. Set the form's FormStyle to fsMDIForm
3. Drop an image on the form and select a bitmap into it.
4. Find the { Private Declarations } comment in the form's definition and add these
lines right after it:
FClientInstance, FPrevClientProc: TFarProc;
procedure ClientWndProc(var Message: TMessage);
5. Find the "implementation" line and the {$R *.DFM} line that follows it. After
that line, enter this code:
153 procedure TForm1.ClientWndProc(var message: TMessage);
154 var
155 MyDC: hDC;
156 Ro, Co: Word;
157 begin
158 with message do
159 case Msg of
160 WM_ERASEBKGND:
161 begin
162 MyDC := TWMEraseBkGnd(message).DC;
163 for Ro := 0 to ClientHeight div Image1.Picture.Height do
164 for Co := 0 to ClientWIDTH div Image1.Picture.Width do
165 BitBlt(MyDC, Co * Image1.Picture.Width, Ro * Image1.Picture.Height,
166 Image1.Picture.Width,
167 Image1.Picture.Height, Image1.Picture.Bitmap.Canvas.Handle, 0, 0,
168 SRCCOPY);
169 Result := 1;
170 end
171 else
172 Result := CallWindowProc(FPrevClientProc, ClientHandle, Msg, wParam, lParam);
173 end;
174 end;
6. Start an OnCreate method for the form and put these lines in it:
175
176 FClientInstance := MakeObjectInstance(ClientWndProc);
177 FPrevClientProc := Pointer(GetWindowLong(ClientHandle, GWL_WNDPROC));
178 SetWindowLong(ClientHandle, GWL_WNDPROC, LongInt(FClientInstance));
7. Add a new form to your project and set its FormStyle to fsMDIChild.
Now you have a working MDI project with "wallpaper". The image component is not visible, but its bitmap is replicated to cover the MDI form's client area. There is still one problem; when you minimize the child window its icon will be drawn against a gray rectangle.
|