📄 wtl for mfc programmers, part ii.mht
字号:
client=20
area.</P><PRE><SPAN class=3Dcpp-keyword>class</SPAN> CMyWindow : =
<SPAN class=3Dcpp-keyword>public</SPAN> =
CFrameWindowImpl<CMyWindow>
{
<SPAN class=3Dcpp-keyword>public</SPAN>:
BEGIN_MSG_MAP_EX(CMyWindow)<FONT color=3Dred>
</FONT> MSG_WM_CREATE(OnCreate)
MSG_WM_DESTROY(OnDestroy)<FONT color=3Dred>
</FONT> MSG_WM_TIMER(OnTimer)<FONT color=3Dred>
MSG_WM_ERASEBKGND(OnEraseBkgnd)
</FONT> CHAIN_MSG_MAP(CFrameWindowImpl<CMyWindow>)
END_MSG_MAP()
=20
<FONT color=3Dred> LRESULT OnEraseBkgnd ( HDC hdc )
{
CDCHandle dc(hdc);
CRect rc;
SYSTEMTIME st;
CString sTime;
=20
<SPAN class=3Dcpp-comment>// Get our window's client =
area.</SPAN>
GetClientRect ( rc );
=20
<SPAN class=3Dcpp-comment>// Build the string to show in the =
window.</SPAN>
GetLocalTime ( &st );
sTime.Format ( _T(<SPAN class=3Dcpp-string>"The time is =
%d:%02d:%02d"</SPAN>),=20
st.wHour, st.wMinute, st.wSecond );
=20
<SPAN class=3Dcpp-comment>// Set up the DC and draw the =
text.</SPAN>
dc.SaveDC();
=20
dc.SetBkColor ( RGB(<SPAN class=3Dcpp-literal>255</SPAN>,<SPAN =
class=3Dcpp-literal>153</SPAN>,<SPAN class=3Dcpp-literal>0</SPAN>);
dc.SetTextColor ( RGB(<SPAN class=3Dcpp-literal>0</SPAN>,<SPAN =
class=3Dcpp-literal>0</SPAN>,<SPAN class=3Dcpp-literal>0</SPAN>) );
dc.ExtTextOut ( <SPAN class=3Dcpp-literal>0</SPAN>, <SPAN =
class=3Dcpp-literal>0</SPAN>, ETO_OPAQUE, rc, sTime,=20
sTime.GetLength(), NULL );
=20
<SPAN class=3Dcpp-comment>// Restore the DC.</SPAN>
dc.RestoreDC(-<SPAN class=3Dcpp-literal>1</SPAN>);
<SPAN class=3Dcpp-keyword>return</SPAN> <SPAN =
class=3Dcpp-literal>1</SPAN>; <SPAN class=3Dcpp-comment>// We erased =
the background (ExtTextOut did it)</SPAN>
}
</FONT>};</PRE>
<P>This handler demonstrates one of the GDI wrappers,=20
<CODE>CDCHandle</CODE>, as well as <CODE>CRect</CODE> and=20
<CODE>CString</CODE>. All I need to say about <CODE>CString</CODE> =
is,=20
it's identical to MFC's <CODE>CString</CODE>. I'll cover the =
wrapper=20
classes later on, but for now you can think of =
<CODE>CDCHandle</CODE> as a=20
simple wrapper around an <CODE>HDC</CODE>, similar to MFC's=20
<CODE>CDC</CODE>, although when the <CODE>CDCHandle</CODE> goes =
out of=20
scope, it doesn't destroy the underlying device context.</P>
<P>So after all that, here's what our window looks like:</P>
<P><IMG height=3D208 alt=3D" [clock window - 4K] "=20
src=3D"http://www.codeproject.com/wtl/WTL4MFC2/firstwin.png" =
width=3D283=20
align=3Dbottom border=3D0></P>
<P>The sample code also has <CODE>WM_COMMAND</CODE> handlers for =
the menu=20
items; I won't cover those here, but you can check out the sample =
project=20
and see the WTL macro <CODE>COMMAND_ID_HANDLER_EX</CODE> in =
action.</P>
<H2><A name=3Dappwizard></A>What You Get with the WTL =
AppWizard</H2>
<P>The WTL distribution comes with a very nice AppWizard, so let's =
see=20
what features it puts in an SDI app.</P>
<H3><A name=3Dthruwizard></A>Going through the wizard</H3>
<P>Click <I>File|New</I> in VC and select <I>ATL/WTL AppWizard</I> =
from=20
the list. We'll be rewriting the clock app, so enter =
<I>WTLClock</I> for=20
the project name:</P>
<P><IMG height=3D403 alt=3D" [AppWiz screen 1 - 14K] "=20
src=3D"http://www.codeproject.com/wtl/WTL4MFC2/appwiz1.png" =
width=3D561=20
align=3Dbottom border=3D0></P>
<P>The next page is where you select an SDI, MDI, or dialog-based =
app,=20
along with some other options. Select the options as shown here =
and click=20
<I>Next</I>:</P>
<P><IMG height=3D387 alt=3D" [AppWiz screen 2 - 22K] "=20
src=3D"http://www.codeproject.com/wtl/WTL4MFC2/appwiz2.png" =
width=3D477=20
align=3Dbottom border=3D0></P>
<P>The last page is where we can choose to have a toolbar, rebar, =
and=20
status bar. To keep this app simple, uncheck all those and click=20
<I>Finish</I>.</P>
<P><IMG height=3D387 alt=3D" [AppWiz screen 3 - 21K] "=20
src=3D"http://www.codeproject.com/wtl/WTL4MFC2/appwiz3.png" =
width=3D477=20
align=3Dbottom border=3D0></P>
<H3><A name=3Dexaminecode></A>Examining the generated code</H3>
<P>After finishing the wizard, you'll see three classes in the =
generated=20
code: <CODE>CMainFrame</CODE>, <CODE>CAboutDlg</CODE>, and=20
<CODE>CWTLClockView</CODE>. From the names, you can guess what the =
purpose=20
of each class is. While there is a "view" class, it is strictly a =
"plain"=20
window derived from <CODE>CWindowImpl</CODE>; there is no =
framework at all=20
like MFC's doc/view architecture.</P>
<P>There is also a <CODE>_tWinMain()</CODE>, which initializes =
COM, the=20
common controls, and <CODE>_Module</CODE>, and then calls a global =
<CODE>Run()</CODE> function. <CODE>Run()</CODE> handles creating =
the main=20
window and starting the message pump. It also uses a new class,=20
<CODE>CMessageLoop</CODE>. <CODE>Run()</CODE> calls=20
<CODE>CMessageLoop::Run()</CODE> which actually contains the =
message pump.=20
I'll cover <CODE>CMessageLoop</CODE> in more detail in the next=20
section.</P>
<P><CODE>CAboutDlg</CODE> is a simple =
<CODE>CDialogImpl</CODE>-derived=20
class that is associated with a dialog with ID =
<CODE>IDD_ABOUTBOX</CODE>.=20
I covered dialogs in Part I, so you should be able to understand =
the code=20
in <CODE>CAboutDlg</CODE>.</P>
<P><CODE>CWTLClockView</CODE> is our app's "view" class. This =
works like=20
an MFC view, in that it is a captionless window that occupies the =
client=20
area of the main frame. <CODE>CWTLClockView</CODE> has a=20
<CODE>PreTranslateMessage()</CODE> function, which works just like =
the MFC=20
function of the same name, and a <CODE>WM_PAINT</CODE> handler. =
Neither=20
function does anything significant yet, but we'll fill in the=20
<CODE>OnPaint()</CODE> method to show the time.</P>
<P>Finally, we have <CODE>CMainFrame</CODE>, which has lots of =
interesting=20
new stuff. Here is an abbreviated version of the class =
definition:</P><PRE><SPAN class=3Dcpp-keyword>class</SPAN> CMainFrame : =
<SPAN class=3Dcpp-keyword>public</SPAN> =
CFrameWindowImpl<CMainFrame>,
<SPAN class=3Dcpp-keyword>public</SPAN> =
CUpdateUI<CMainFrame>,
<SPAN class=3Dcpp-keyword>public</SPAN> =
CMessageFilter,
<SPAN class=3Dcpp-keyword>public</SPAN> CIdleHandler
{
<SPAN class=3Dcpp-keyword>public</SPAN>:
DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)
CWTLClockView m_view;
<SPAN class=3Dcpp-keyword>virtual</SPAN> BOOL =
PreTranslateMessage(MSG* pMsg);
<SPAN class=3Dcpp-keyword>virtual</SPAN> BOOL OnIdle();
BEGIN_UPDATE_UI_MAP(CMainFrame)
END_UPDATE_UI_MAP()
BEGIN_MSG_MAP(CMainFrame)
<SPAN class=3Dcpp-comment>// ...</SPAN>
CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
END_MSG_MAP()
};</PRE>
<P><CODE>CMessageFilter</CODE> is a mix-in class that provides=20
<CODE>PreTranslateMessage()</CODE>, and <CODE>CIdleHandler</CODE> =
is=20
another mix-in that provides <CODE>OnIdle()</CODE>.=20
<CODE>CMessageLoop</CODE>, <CODE>CIdleHandler</CODE>, and=20
<CODE>CUpdateUI</CODE> work together to provide UI updating that =
works=20
like <CODE>ON_UPDATE_COMMAND_UI</CODE> in MFC.</P>
<P><CODE>CMainFrame::OnCreate()</CODE> creates the view window and =
saves=20
its window handle, so that the view will be resized when the frame =
window=20
is resized. <CODE>OnCreate()</CODE> also adds the =
<CODE>CMainFrame</CODE>=20
object to lists of message filters and idle handlers kept by the=20
<CODE>CAppModule</CODE>; I will cover those later.</P><PRE>LRESULT =
CMainFrame::OnCreate(UINT <SPAN class=3Dcpp-comment>/*uMsg*/</SPAN>, =
WPARAM <SPAN class=3Dcpp-comment>/*wParam*/</SPAN>,=20
LPARAM <SPAN =
class=3Dcpp-comment>/*lParam*/</SPAN>, BOOL& <SPAN =
class=3Dcpp-comment>/*bHandled*/</SPAN>)
{
m_hWndClient =3D m_view.Create(m_hWnd, rcDefault, NULL, |
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS =
|
WS_CLIPCHILDREN, WS_EX_CLIENTEDGE);
=20
<SPAN class=3Dcpp-comment>// register object for message filtering =
and idle updates</SPAN>
CMessageLoop* pLoop =3D _Module.GetMessageLoop();
pLoop->AddMessageFilter(<SPAN class=3Dcpp-keyword>this</SPAN>);
pLoop->AddIdleHandler(<SPAN class=3Dcpp-keyword>this</SPAN>);
=20
<SPAN class=3Dcpp-keyword>return</SPAN> <SPAN =
class=3Dcpp-literal>0</SPAN>;
}</PRE>
<P><CODE>m_hWndClient</CODE> is a member of =
<CODE>CFrameWindowImpl</CODE>;=20
this is the window that will be resized when the frame is =
resized.</P>
<P>The generated <CODE>CMainFrame</CODE> also has handlers for=20
<I>File|New</I>, <I>File|Exit</I>, and <I>Help|About</I>. We won't =
need=20
most of the default menu items for our clock, but it won't do any =
harm to=20
leave them in for now. You can build and run the wizard-generated =
code=20
now, although the app won't be very useful yet. You might be =
interested in=20
stepping through the <CODE>CMainFrame::CreateEx()</CODE> call in =
the=20
global <CODE>Run()</CODE> to see exactly how the frame window and =
its=20
resources are loaded and created.</P>
<P>Our next stop on the WTL tour is <CODE>CMessageLoop</CODE>, =
which=20
handles the message pump and idle processing.</P>
<H2><A name=3Dmsgloopinternals></A>CMessageLoop Internals</H2>
<P><CODE>CMessageLoop</CODE> provides a message pump for our app. =
Aside=20
from a standard =
<CODE>DispatchMessage</CODE>/<CODE>TranslateMessage</CODE>=20
loop, it also provides message filtering via=20
<CODE>PreTranslateMessage()</CODE> and idle processing via=20
<CODE>OnIdle()</CODE>. Here is pseudo code for the logic in=20
<CODE>Run()</CODE>:</P><PRE><SPAN class=3Dcpp-keyword>int</SPAN> =
Run()
{
MSG msg;
=20
<SPAN class=3Dcpp-keyword>for</SPAN>(;;)
{
<SPAN class=3Dcpp-keyword>while</SPAN> ( !PeekMessage(&msg) =
)
DoIdleProcessing();
=20
<SPAN class=3Dcpp-keyword>if</SPAN> ( <SPAN =
class=3Dcpp-literal>0</SPAN> =3D=3D GetMessage(&msg) )
<SPAN class=3Dcpp-keyword>break</SPAN>; <SPAN =
class=3Dcpp-comment>// WM_QUIT retrieved from the queue</SPAN>
=20
<SPAN class=3Dcpp-keyword>if</SPAN> ( =
!PreTranslateMessage(&msg) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
=20
<SPAN class=3Dcpp-keyword>return</SPAN> msg.wParam;
}</PRE>
<P><CODE>CMessageLoop</CODE> knows which=20
<CODE>PreTranslateMessage()</CODE> functions to call because =
classes that=20
need to filter messages call=20
<CODE>CMessageLoop::AddMessageFilter()</CODE>, just like=20
<CODE>CMainFrame::OnCreate()</CODE> does. And similarly, classes =
that need=20
to do idle processing call=20
<CODE>CMessageLoop::AddIdleHandler()</CODE>.</P>
<P>Notice that there are no calls to =
<CODE>TranslateAccelerator()</CODE>=20
or <CODE>IsDialogMessage()</CODE> in the message loop.=20
<CODE>CFrameWindowImpl</CODE> handles the former, but if you add =
any=20
modeless dialogs to your app, you'll need to add a call to=20
<CODE>IsDialogMessage()</CODE> in=20
<CODE>CMainFrame::PreTranslateMessage()</CODE>.</P>
<H2><A name=3Dframeinternals></A>CFrameWindowImpl Internals</H2>
<P><CODE>CFrameWindowImpl</CODE> and its base class=20
<CODE>CFrameWindowImplBase</CODE> provide a lot of the features =
you're=20
used to having in MFC's <CODE>CFrameWnd</CODE>: toolbars, rebars, =
status=20
bars, tooltips for toolbar buttons, and flyby help for menu items. =
I'll be=20
covering all those features gradually, since discussing the entire =
<CODE>CFrameWindowImpl</CODE> class could take up two whole =
articles! For=20
now, it will be sufficient to see how =
<CODE>CFrameWindowImpl</CODE>=20
handles <CODE>WM_SIZE</CODE> and its client area. For this =
discussion,=20
remember that <CODE>m_hWndClient</CODE> is a member of=20
<CODE>CFrameWindowImplBase</CODE> that holds the <CODE>HWND</CODE> =
of the=20
"view" window inside the frame.</P>
<P><CODE>CFrameWindowImpl</CODE> has a handler for=20
<CODE>WM_SIZE</CODE>:</P><PRE>LRESULT OnSize(UINT <SPAN =
class=3Dcpp-comment>/*uMsg*/</SPAN>, WPARAM wParam, LPARAM <SPAN =
class=3Dcpp-comment>/*lParam*/</SPAN>, BOOL& bHandled)
{
<SPAN class=3Dcpp-keyword>if</SPAN>(wParam !=3D SIZE_MINIMIZED)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -