📄 faq76.htm
字号:
<b>class</b> TForm1 <b>:</b> <b>public</b> TForm
<b>{</b>
<b>...</b>
<b>private</b><b>:</b>
<b>static</b> LRESULT CALLBACK ListViewProc<b>(</b>HWND hWnd<b>,</b>
UINT uMsg<b>,</b>
WPARAM wParam<b>,</b>
LPARAM lParam<b>)</b><b>;</b>
<b>}</b><b>;</b>
</pre>
<P>
When you use a static member function as your callback procedure, you cannot attempt to call non-static member functions
and you can't dereference non-static member variables of the class. This presents a problem in this example, because we
store the original window procedure in a private variable. We would not be able to dereference this variable from a
static callback function (unless we made the variable static too). The next section describes a VCL technique that
allows you to subclass using a non-static member function of a class.
</P>
<P>
<B>Note 3:</B> The original window proc for the listview is stored in a variable called <TT>FOriginalProc</TT>. This
variable serves two purposes. First, it allows us to pass on window messages when we don't want to alter the default
behavior of the control. Secondly, when the form's destructor executes, we set the window proc of the listview back
to the original handler. This is very important. The lifetime of the listview exceeds the lifetime of the form.
Actually, to be more precise, it exceeds the lifetime of the <TT>TForm1</TT> part of the form.</P>
<P>
The listview is deleted by the base classes of the form. When the listview gets destroyed, certain messages are sent.
Windows will send those messages to the <TT>ControlWndProc</TT> function if we don't restore the original handler.
The problem is that <TT>ControlWndProc</TT> calls a method of <TT>TForm1</TT>.
This is a bad scenario if the <TT>TForm1</TT> destructor has already executed. To prevent this from occuring, the code
restores the original window procedure of the listview in the destructor of the form.
</P>
<BR>
<H4>
Subclassing with the VCL MakeObjectInstance function
</H4>
<P>
The VCL provides a utility function called <TT>MakeObjectInstance</TT> that allows you to sublcass a window using
a member function of a class. When you subclass using <TT>MakeObjectInstance</TT>, the actual window proc is a method
inside the VCL called <TT>StdWndProc</TT>. <TT>StdWndProc</TT> dispatches windows messages to the method of your
class. <TT>StdWndProc</TT> handles the task of determining which object should get
the message. Put another way, <TT>StdWndProc</TT> knows the value of the hidden <TT>this</TT> pointer that must be
passed when your member function is called.
</P>
<P>
The code snippets below demonstrate how to sublcass the listview using <TT>MakeObjectInstance</TT>. Notice that the
stand alone function <TT>ControlWndProc</TT> is no longer needed.
</P>
<pre>
<font color="navy">//-------------------------------------------------------</font>
<font color="navy">// header file</font>
<b>class</b> TForm1 <b>:</b> <b>public</b> TForm
<b>{</b>
<b>__published</b><b>:</b> <font color="navy">// IDE-managed Components</font>
TListView <b>*</b>ListView1<b>;</b>
TImageList <b>*</b>ImageList1<b>;</b>
<b>void</b> <b>__fastcall</b> FormResize<b>(</b>TObject <b>*</b>Sender<b>)</b><b>;</b>
<b>private</b><b>:</b> <font color="navy">// User declarations</font>
WNDPROC FOriginalProc<b>;</b>
<b>void</b> <b>*</b> FObjectInstance<b>;</b>
<b>void</b> <b>__fastcall</b> PaintListView<b>(</b>HDC <b>&</b>dc<b>)</b><b>;</b>
<b>void</b> <b>__fastcall</b> ListViewProc<b>(</b>TMessage <b>&</b>msg<b>)</b><b>;</b>
<b>public</b><b>:</b> <font color="navy">// User declarations</font>
<b>__fastcall</b> TForm1<b>(</b>TComponent<b>*</b> Owner<b>)</b><b>;</b>
<b>__fastcall</b> <b>~</b>TForm1<b>(</b><b>)</b><b>;</b>
<b>}</b><b>;</b>
<font color="navy">//-------------------------------------------------------</font>
<font color="navy">// source file</font>
<font color="navy">//-------------------------------------------------------</font>
<font color="navy">//-------------------------------------------------------</font>
<b>__fastcall</b> TForm1<b>:</b><b>:</b>TForm1<b>(</b>TComponent<b>*</b> Owner<b>)</b>
<b>:</b> TForm<b>(</b>Owner<b>)</b>
<b>{</b>
<font color="navy">// Call GetWindowLong to retrieve the original window proc</font>
FOriginalProc <b>=</b> <b>(</b>WNDPROC<b>)</b> GetWindowLong<b>(</b>ListView1<b>-></b>Handle<b>,</b> GWL_WNDPROC<b>)</b><b>;</b>
<font color="navy">// Call MakeObjectInstance to setup a wndproc for the member function</font>
FObjectInstance <b>=</b> MakeObjectInstance<b>(</b>ListViewProc<b>)</b><b>;</b>
<font color="navy">// Pass the result of MakeObjectInstance to SetWindowLong</font>
<font color="navy">// to assign the new window proc for the listview</font>
SetWindowLong<b>(</b>ListView1<b>-></b>Handle<b>,</b> GWL_WNDPROC<b>,</b> <b>(</b>LONG<b>)</b>FObjectInstance<b>)</b><b>;</b>
<b>}</b>
<font color="navy">//-------------------------------------------------------</font>
<b>__fastcall</b> TForm1<b>:</b><b>:</b><b>~</b>TForm1<b>(</b><b>)</b>
<b>{</b>
<font color="navy">// Set the window proc back to the original function</font>
SetWindowLong<b>(</b>ListView1<b>-></b>Handle<b>,</b> GWL_WNDPROC<b>,</b> <b>(</b>LONG<b>)</b> FOriginalProc<b>)</b><b>;</b>
<font color="navy">// Call FreeObjectInstance to release memory consumed by MakeObjectInstance</font>
FreeObjectInstance<b>(</b>FObjectInstance<b>)</b><b>;</b>
<b>}</b>
<font color="navy">//-------------------------------------------------------</font>
<b>void</b> <b>__fastcall</b> TForm1<b>:</b><b>:</b>ListViewProc<b>(</b>TMessage <b>&</b>msg<b>)</b>
<b>{</b>
msg<b>.</b>Result <b>=</b> <font color="blue">0</font><b>;</b>
<b>switch</b><b>(</b>msg<b>.</b>Msg<b>)</b>
<b>{</b>
<b>case</b> WM_ERASEBKGND<b>:</b>
PaintListView<b>(</b><b>(</b>HDC<b>)</b>msg<b>.</b>WParam<b>)</b><b>;</b>
msg<b>.</b>Result <b>=</b> <font color="blue">0</font><b>;</b>
<b>break</b><b>;</b>
<b>case</b> WM_HSCROLL<b>:</b>
<b>case</b> WM_VSCROLL<b>:</b>
LockWindowUpdate<b>(</b>ListView1<b>-></b>Handle<b>)</b><b>;</b>
msg<b>.</b>Result <b>=</b> CallWindowProc <b>(</b><b>(</b>FARPROC<b>)</b>FOriginalProc<b>,</b>
ListView1<b>-></b>Handle<b>,</b>
msg<b>.</b>Msg<b>,</b>
msg<b>.</b>WParam<b>,</b>
msg<b>.</b>LParam<b>)</b><b>;</b>
InvalidateRect <b>(</b>ListView1<b>-></b>Handle<b>,</b> <font color="blue">0</font><b>,</b> <b>true</b><b>)</b><b>;</b>
LockWindowUpdate<b>(</b>NULL<b>)</b><b>;</b>
<b>break</b><b>;</b>
<b>default</b><b>:</b>
msg<b>.</b>Result <b>=</b> CallWindowProc <b>(</b><b>(</b>FARPROC<b>)</b>FOriginalProc<b>,</b>
ListView1<b>-></b>Handle<b>,</b>
msg<b>.</b>Msg<b>,</b>
msg<b>.</b>WParam<b>,</b>
msg<b>.</b>LParam<b>)</b><b>;</b>
<b>break</b><b>;</b>
<b>}</b>
<b>}</b>
<font color="navy">//-------------------------------------------------------</font>
<b>void</b> <b>__fastcall</b> TForm1<b>:</b><b>:</b>PaintListView<b>(</b>HDC <b>&</b>dc<b>)</b>
<b>{</b>
<font color="navy">//Same painting code as before</font>
<b>...</b>
<b>}</b>
<font color="navy">//-------------------------------------------------------</font>
<b>void</b> <b>__fastcall</b><b>/</b> TForm1<b>:</b><b>:</b>FormResize<b>(</b>TObject <b>*</b>Sender<b>)</b>
<b>{</b>
ListView1<b>-></b>Repaint<b>(</b><b>)</b><b>;</b>
<b>}</b>
<font color="navy">//-------------------------------------------------------</font>
</pre>
<P>
<B>Note 1:</B> <TT>MakeObjectInstance</TT> returns a <TT>void</TT> pointer. This <TT>void</TT> pointer should
be passed to the <TT>SetWindowLong</TT> API function. Do not pass the name of your member function to
<TT>SetWindowLong</TT>.
</P>
<P>
<B>Note 2:</B> Every call to <TT>MakeObjectInstance</TT> should have a corresponding call to
<TT>FreeObjectInstance</TT>. <TT>MakeObjectInstance</TT> allocates memory that is not freed until you call
<TT>FreeObjectInstance</TT>. Failing to free up this memory causes memory leaks.
</P>
<BR>
<H4>
Subclassing with the WindowProc property of a control</H4>
<P>
The <TT>TControl</TT> class from the VCL provides a public property that allows you to subclass a control without much
effort. The property is called <TT>WindowProc</TT>. You can intercept messages sent to a control by assigning a new
function to the <TT>WindowProc</TT> property of a control. The next code listing demonstrates how to subclass the
listview control through its <TT>WindowProc</TT> property.
</P>
<pre>
<font color="navy">//-------------------------------------------------------</font>
<font color="navy">// header file</font>
<b>class</b> TForm1 <b>:</b> <b>public</b> TForm
<b>{</b>
<b>__published</b><b>:</b> <font color="navy">// IDE-managed Components</font>
TListView <b>*</b>ListView1<b>;</b>
TImageList <b>*</b>ImageList1<b>;</b>
<b>void</b> <b>__fastcall</b> FormResize<b>(</b>TObject <b>*</b>Sender<b>)</b><b>;</b>
<b>private</b><b>:</b> <font color="navy">// User declarations</font>
TWndMethod FOriginalProc<b>;</b>
<b>void</b> <b>__fastcall</b> ListViewProc<b>(</b>TMessage <b>&</b>msg<b>)</b><b>;</b>
<b>void</b> <b>__fastcall</b> PaintListView<b>(</b>HDC <b>&</b>dc<b>)</b><b>;</b>
<b>public</b><b>:</b> <font color="navy">// User declarations</font>
<b>__fastcall</b> TForm1<b>(</b>TComponent<b>*</b> Owner<b>)</b><b>;</b>
<b>__fastcall</b> <b>~</b>TForm1<b>(</b><b>)</b><b>;</b>
<b>}</b><b>;</b>
<font color="navy">//-------------------------------------------------------</font>
<font color="navy">// source file</font>
<font color="navy">//-------------------------------------------------------</font>
<b>__fastcall</b> TForm1<b>:</b><b>:</b>TForm1<b>(</b>TComponent<b>*</b> Owner<b>)</b>
<b>:</b> TForm<b>(</b>Owner<b>)</b>
<b>{</b>
<font color="navy">// Store the original valud of WindowProc in the member variable</font>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -