⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 wtl for mfc programmers, part vii.mht

📁 大家知道wtl是window UI库
💻 MHT
📖 第 1 页 / 共 5 页
字号:
as the=20
      view window:</P><PRE>LRESULT CMainFrame::OnCreate ( LPCREATESTRUCT =
lpcs )
{
<SPAN class=3Dcpp-comment>//...</SPAN>
    <SPAN class=3Dcpp-comment>// Create the splitter window</SPAN>
<SPAN class=3Dcpp-keyword>const</SPAN> DWORD dwSplitStyle =3D WS_CHILD | =
WS_VISIBLE |
                           WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
            dwSplitExStyle =3D WS_EX_CLIENTEDGE;
=20
    m_wndVertSplit.Create ( *<SPAN class=3Dcpp-keyword>this</SPAN>, =
rcDefault, NULL,
                            dwSplitStyle, dwSplitExStyle );
=20
    <SPAN class=3Dcpp-comment>// Set the splitter as the client area =
window, and resize</SPAN>
    <SPAN class=3Dcpp-comment>// the splitter to match the frame =
size.</SPAN>
    m_hWndClient =3D m_wndVertSplit;
    UpdateLayout();
=20
    <SPAN class=3Dcpp-comment>// Position the splitter bar.</SPAN>
    m_wndVertSplit.SetSplitterPos ( <SPAN class=3Dcpp-literal>200</SPAN> =
);
=20
    <SPAN class=3Dcpp-keyword>return</SPAN> <SPAN =
class=3Dcpp-literal>0</SPAN>;
}</PRE>
      <P>Note that you need to set <CODE>m_hWndClient</CODE> and call=20
      <CODE>CFrameWindowImpl::UpdateLayout()</CODE> before setting the =
splitter=20
      position. <CODE>UpdateLayout()</CODE> resizes the splitter window =
to its=20
      initial size. If you skip that step, the splitter's size isn't =
under your=20
      control and it might be smaller than 200 pixels wide. The end =
result would=20
      be that <CODE>SetSplitterPos()</CODE> wouldn't have the effect you =
wanted.=20
      An alternative to calling <CODE>UpdateLayout()</CODE> is to get =
the client=20
      <CODE>RECT</CODE> of the frame window, and use that =
<CODE>RECT</CODE> when=20
      creating the splitter, instead of <CODE>rcDefault</CODE>. This =
way, you=20
      create the splitter in its initial position, and all subsequent =
methods=20
      dealing with position (like <CODE>SetSplitterPos()</CODE>) will =
work=20
      correctly.</P>
      <P>If you run the app now, you'll see the splitter in action. Even =
without=20
      creating anything for the panes, the basic behavior is there. You =
can drag=20
      the bar, and double-clicking it moves the bar to the center.</P>
      <P><IMG height=3D255 alt=3D" [Empty splitter - 4K] "=20
      src=3D"http://www.codeproject.com/wtl/WTL4MFC7/emptysplit.png" =
width=3D329=20
      align=3Dbottom border=3D0></P>
      <P>To demonstrate different ways of managing the pane windows, =
I'll use=20
      one <CODE>CListViewCtrl</CODE>-derived class, and a plain=20
      <CODE>CRichEditCtrl</CODE>. Here's a snippet from the=20
      <CODE>CClipSpyListCtrl</CODE> class, which we'll use in the left =
pane:</P><PRE><SPAN class=3Dcpp-keyword>typedef</SPAN> =
CWinTraitsOR&lt;LVS_REPORT | LVS_SINGLESEL | LVS_NOSORTHEADER&gt;
          CListTraits;
=20
<SPAN class=3Dcpp-keyword>class</SPAN> CClipSpyListCtrl :
    <SPAN class=3Dcpp-keyword>public</SPAN> =
CWindowImpl&lt;CClipSpyListCtrl, CListViewCtrl, CListTraits&gt;,
    <SPAN class=3Dcpp-keyword>public</SPAN> =
CCustomDraw&lt;CClipSpyListCtrl&gt;
{
<SPAN class=3Dcpp-keyword>public</SPAN>:
    DECLARE_WND_SUPERCLASS(NULL, WC_LISTVIEW)
=20
    BEGIN_MSG_MAP(CClipSpyListCtrl)
        MSG_WM_CHANGECBCHAIN(OnChangeCBChain)
        MSG_WM_DRAWCLIPBOARD(OnDrawClipboard)
        MSG_WM_DESTROY(OnDestroy)
        CHAIN_MSG_MAP_ALT(CCustomDraw&lt;CClipSpyListCtrl&gt;, <SPAN =
class=3Dcpp-literal>1</SPAN>)
        DEFAULT_REFLECTION_HANDLER()
    END_MSG_MAP()
<SPAN class=3Dcpp-comment>//...</SPAN>
};</PRE>
      <P>If you've been following the previous articles, you should have =
no=20
      trouble reading this class. It handles =
<CODE>WM_CHANGECBCHAIN</CODE> to=20
      know when other clipboard viewers come and go, and=20
      <CODE>WM_DRAWCLIPBOARD</CODE> to know when the contents of the =
clipboard=20
      change.</P>
      <P>Since the pane windows will exist for the life of the app, we =
can use=20
      member variables in <CODE>CMainFrame</CODE> for them as =
well:</P><PRE><SPAN class=3Dcpp-keyword>class</SPAN> CMainFrame : <SPAN =
class=3Dcpp-keyword>public</SPAN> ...
{
<SPAN class=3Dcpp-comment>//...</SPAN>
<SPAN class=3Dcpp-keyword>protected</SPAN>:
    CSplitterWindow  m_wndVertSplit;
    <B>CClipSpyListCtrl m_wndFormatList;
    CRichEditCtrl    m_wndDataViewer;</B>
};</PRE>
      <H2><A name=3Dcreatingpanes></A>Creating windows in the panes</H2>
      <P>Now that we have member variables for the splitter and the =
panes,=20
      filling in the splitter is a simple matter. After creating the =
splitter=20
      window, we create both child windows, using the splitter as their=20
      parent:</P><PRE>LRESULT CMainFrame::OnCreate ( LPCREATESTRUCT lpcs =
)
{
<SPAN class=3Dcpp-comment>//...</SPAN>
    <SPAN class=3Dcpp-comment>// Create the splitter window</SPAN>
<SPAN class=3Dcpp-keyword>const</SPAN> DWORD dwSplitStyle =3D WS_CHILD | =
WS_VISIBLE |
                           WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
            dwSplitExStyle =3D WS_EX_CLIENTEDGE;
=20
    m_wndVertSplit.Create ( *<SPAN class=3Dcpp-keyword>this</SPAN>, =
rcDefault, NULL,
                            dwSplitStyle, dwSplitExStyle );
=20
    <B><SPAN class=3Dcpp-comment>// Create the left pane (list of clip =
formats)</SPAN>
    m_wndFormatList.Create ( m_wndVertSplit, rcDefault );
=20
    <SPAN class=3Dcpp-comment>// Create the right pane (rich edit =
ctrl)</SPAN>
<SPAN class=3Dcpp-keyword>const</SPAN> DWORD dwRichEditStyle =3D=20
              WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
              ES_READONLY | ES_AUTOHSCROLL | ES_AUTOVSCROLL | =
ES_MULTILINE;
=20
    m_wndDataViewer.Create ( m_wndVertSplit, rcDefault,=20
                             NULL, dwRichEditStyle );
    m_wndDataViewer.SetFont ( AtlGetStockFont(ANSI_FIXED_FONT) );</B>
=20
    <SPAN class=3Dcpp-comment>// Set the splitter as the client area =
window, and resize</SPAN>
    <SPAN class=3Dcpp-comment>// the splitter to match the frame =
size.</SPAN>
    m_hWndClient =3D m_wndVertSplit;
    UpdateLayout();
=20
    m_wndVertSplit.SetSplitterPos ( <SPAN class=3Dcpp-literal>200</SPAN> =
);
=20
    <SPAN class=3Dcpp-keyword>return</SPAN> <SPAN =
class=3Dcpp-literal>0</SPAN>;
}</PRE>
      <P>Notice that both <CODE>Create()</CODE> calls use=20
      <CODE>m_wndVertSplit</CODE> as the parent window. The =
<CODE>RECT</CODE>=20
      parameter is not important, since the splitter will resize both =
pane=20
      windows as necessary, so we can use =
<CODE>CWindow::rcDefault</CODE>.</P>
      <P>The last step is to pass the <CODE>HWND</CODE>s of the panes to =
the=20
      splitter. This also has to come before <CODE>UpdateLayout()</CODE> =
so all=20
      the windows end up the correct size.</P><PRE>LRESULT =
CMainFrame::OnCreate ( LPCREATESTRUCT lpcs )
{
<SPAN class=3Dcpp-comment>//...</SPAN>
    m_wndDataViewer.SetFont ( AtlGetStockFont(ANSI_FIXED_FONT) );
=20
    <B><SPAN class=3Dcpp-comment>// Set up the splitter panes</SPAN>
    m_wndVertSplit.SetSplitterPanes ( m_wndFormatList, m_wndDataViewer =
);</B>
=20
    <SPAN class=3Dcpp-comment>// Set the splitter as the client area =
window, and resize</SPAN>
    <SPAN class=3Dcpp-comment>// the splitter to match the frame =
size.</SPAN>
    m_hWndClient =3D m_wndVertSplit;
    UpdateLayout();
=20
    m_wndVertSplit.SetSplitterPos ( <SPAN class=3Dcpp-literal>200</SPAN> =
);
=20
    <SPAN class=3Dcpp-keyword>return</SPAN> <SPAN =
class=3Dcpp-literal>0</SPAN>;
}</PRE>
      <P>And here's what the result looks like, after the list control =
has had=20
      some columns added:</P>
      <P><IMG height=3D259 alt=3D" [Splitter w/panes - 4K] "=20
      src=3D"http://www.codeproject.com/wtl/WTL4MFC7/panes.png" =
width=3D320=20
      align=3Dbottom border=3D0></P>
      <P>Note that the splitter puts no restrictions on what windows can =
go in=20
      the panes, unlike MFC where you are supposed to use =
<CODE>CView</CODE>s.=20
      The pane windows should have at least the <CODE>WS_CHILD</CODE> =
style, but=20
      beyond that you're pretty much free to use anything.</P>
      <H2><A name=3Dmessagerouting></A>Message Routing</H2>
      <P>Since we now have another window sitting between the main frame =
and the=20
      pane windows, you might have wondered how notification messages =
work.=20
      Specifically, how can the main frame receive =
<CODE>NM_CUSTOMDRAW</CODE>=20
      notifications so it can reflect them to the list? The answer can =
be found=20
      in the <CODE>CSplitterWindowImpl</CODE> message map:</P><PRE>  =
BEGIN_MSG_MAP(thisClass)
    MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
    MESSAGE_HANDLER(WM_SIZE, OnSize)
    CHAIN_MSG_MAP(baseClass)
    FORWARD_NOTIFICATIONS()
  END_MSG_MAP()</PRE>
      <P>The <CODE>FORWARD_NOTIFICATIONS()</CODE> macro at the end is =
the=20
      important one. Recall from <A=20
      =
href=3D"http://www.codeproject.com/wtl/WTL4MFC4.htm#reflnotify">Part =
IV</A>=20
      that there are several notification messages which are always sent =
to the=20
      parent of a child window. What =
<CODE>FORWARD_NOTIFICATIONS()</CODE> does=20
      is resend the message to <I>the splitter's</I> parent window. So =
when the=20
      list sends a <CODE>WM_NOTIFY</CODE> message to the splitter (the =
list's=20
      parent), the splitter in turn sends the <CODE>WM_NOTIFY</CODE> to =
the main=20
      frame (the splitter's parent). When the main frame reflects the =
message,=20
      it is sent back to the window that generated the =
<CODE>WM_NOTIFY</CODE> in=20
      the first place, which is the list, so the splitter doesn't get =
involved=20
      in reflection.</P>
      <P>The result of all this is that notification messages sent =
between the=20
      main frame and the list don't get affected by the presence of the =
splitter=20
      window. This makes it rather easy to add or remove splitters, =
because the=20
      child window classes won't have to be changed at all for their =
message=20
      processing to continue working.</P>
      <H2><A name=3Dpanecont></A>Pane Containers</H2>
      <P>WTL also supports a widget like the one in the left pane of =
Explorer,=20
      called a <I>pane container</I>. This control provides a header =
area with=20
      text, and optionally a Close button:</P>
      <P><IMG height=3D150 alt=3D" [Explorer pane container - 3K] "=20
      src=3D"http://www.codeproject.com/wtl/WTL4MFC7/explorerpc.png" =
width=3D199=20
      align=3Dbottom border=3D0></P>
      <P>The pane container manages a child window, just as the splitter =
manages=20
      two pane windows. When the container is resized, the child is=20
      automatically resized to match the space inside the container.</P>
      <H3><A name=3Dpcclasses></A>Classes</H3>
      <P>There are two classes in the implementation of pane containers, =
both in=20
      atlctrlx.h: <CODE>CPaneContainerImpl</CODE> and=20
      <CODE>CPaneContainer</CODE>. <CODE>CPaneContainerImpl</CODE> is a=20
      <CODE>CWindowImpl</CODE>-derived class that contains the complete=20
      implementation; <CODE>CPaneContainer</CODE> provides just a window =
class=20
      name. Unless you want to override any methods to change how the =
container=20
      is drawn, you will always use <CODE>CPaneContainer</CODE>.</P>
      <H3><A name=3Dpcmethods></A>Basic methods</H3><PRE>HWND Create(
    HWND hWndParent, LPCTSTR lpstrTitle =3D NULL,
    DWORD dwStyle =3D WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | =
WS_CLIPCHILDREN,
    DWORD dwExStyle =3D <SPAN class=3Dcpp-literal>0</SPAN>, UINT nID =3D =
<SPAN class=3Dcpp-literal>0</SPAN>, LPVOID lpCreateParam =3D NULL)
HWND Create(
    HWND hWndParent, UINT uTitleID,
    DWORD dwStyle =3D WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | =
WS_CLIPCHILDREN,
    DWORD dwExStyle =3D <SPAN class=3Dcpp-literal>0</SPAN>, UINT nID =3D =
<SPAN class=3Dcpp-literal>0</SPAN>, LPVOID lpCreateParam =3D NULL)</PRE>
      <P>Creating a <CODE>CPaneContainer</CODE> is similar to creating =
other=20
      child windows. There are two <CODE>Create()</CODE> methods that =
differ in=20
      just the second parameter. In the first version, you pass a string =
that=20
      will be used for the title text drawn in the header. In the second =
method,=20
      you pass the ID of a string table entry. The defaults for the =
remaining=20
      parameters are usually sufficient.</P><PRE>DWORD =
SetPaneContainerExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask =3D =
<SPAN class=3Dcpp-literal>0</SPAN>)
DWORD GetPaneContainerExtendedStyle()</PRE>
      <P><CODE>CPaneContainer</CODE> has additional extended styles that =
control=20
      the close button and the layout of the container:</P>
      <UL>
        <LI><CODE>PANECNT_NOCLOSEBUTTON</CODE>: Set this style to remove =
the=20

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -