原創 VisualFC
本文假設讀者熟悉WTL,並且已經瞭解和使用過WTL的分隔視窗。WTL的分隔視窗簡單易用,並且可以用在對話方塊視窗程序中,但我們也發現使用WTL分隔視窗的一些問題,即WTL分隔視窗中的面板必須為其子視窗,這樣就無法接受主對話方塊視窗的TAB控制,在對話方塊應用程式中必須將對話方塊控制項的父視窗設定為WTL分隔視窗,然後後再添加到到WTL分隔視窗中,代碼如下:
CWindow ed1 = GetDlgItem(IDC_EDIT1);
CWindow ed2 = GetDlgItem(IDC_EDIT2);
ed1.SetParent(m_wndHorSplit);
ed2.SetParent(m_wndHorSplit);
m_wndHorSplit.SetSplitterPanes( ed1,ed2 );
可以看到,必須將控制項的父視窗設定為WTL分隔視窗,我們可以通過修改WTL的分隔視窗原始碼來修正這一點,修改後,控制項的父視窗仍然是主對話方塊視窗,如此編程就簡單了。代碼如下:
m_wndHorSplit.SetSplitterPanes( GetDlgItem(IDC_EDIT1), GetDlgItem(IDC_EDIT2) );
我們來修改WTL分隔視窗源檔案atlsplit.h,修改位置在類CSplitterImpl的void UpdateSplitterLayout()函數中,修改如下:
將下列的原始代碼
if(m_hWndPane[nPane] != NULL)
...{
::SetWindowPos(m_hWndPane[nPane], NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
}
更改為下列修改後的代碼
if(m_hWndPane[nPane] != NULL)
...{
//fix: 2007-12-06
HWND hWndParent = ::GetParent(m_hWndPane[nPane]);
if (hWndParent != pT->m_hWnd)
...{
pT->ClientToScreen(&rect);
CWindow(hWndParent).ScreenToClient(&rect);
}
::SetWindowPos(m_hWndPane[nPane], NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
}
同理將下面的原始代碼
if(m_hWndPane[m_nSinglePane] != NULL)
...{
::SetWindowPos(m_hWndPane[m_nSinglePane], NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
}
更改為下面修改後的代碼
if(m_hWndPane[m_nSinglePane] != NULL)
...{
//fix: 2007-12-06
HWND hWndParent = ::GetParent(m_hWndPane[m_nSinglePane]);
if (hWndParent != pT->m_hWnd)
...{
pT->ClientToScreen(&rect);
CWindow(hWndParent).ScreenToClient(&rect);
}
::SetWindowPos(m_hWndPane[m_nSinglePane], NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
}
最後我們還得處理SW_SHOWWINDOW訊息以在WTL分隔視窗隱藏時能夠將面板視窗也隱藏起來,為了不對atlsplit作更多的改動,以及方便在對話方塊應用程式中使用,我們從修正後的WTL分隔視窗派生一個新的分隔視窗類別來實現,代碼如下:
namespace WTL
...{
template <bool t_bVertical = true>
class CSplitterWindowExT : public CSplitterWindowImpl<CSplitterWindowT<t_bVertical>, t_bVertical>
...{
public:
DECLARE_WND_CLASS_EX(_T("WTL_SplitterWindowEx"), CS_DBLCLKS, COLOR_WINDOW)
typedef CSplitterWindowImpl<CSplitterWindowT<t_bVertical>, t_bVertical> _baseClass;
HWND CreateFromID(HWND hWndParent, UINT nDlgItemID)
...{
return CreateFromWindow(::GetDlgItem(hWndParent,nDlgItemID));
}
HWND CreateFromWindow(HWND hWnd)
...{
CWindow wnd = hWnd;
CWindow parent = wnd.GetParent();
UINT nID = wnd.GetDlgCtrlID();
RECT rc;
wnd.GetWindowRect(&rc);
DWORD dwStyle = wnd.GetStyle();
dwStyle |= (WS_CHILD | WS_VISIBLE);
DWORD dwExStyle = wnd.GetExStyle();
parent.ScreenToClient(&rc);
HWND hThisWnd = Create(parent,rc,NULL,dwStyle,dwExStyle,nID,NULL);
if (hThisWnd)
...{
wnd.DestroyWindow();
}
return hThisWnd;
}
public:
BEGIN_MSG_MAP(CSplitterWindowExT)
MESSAGE_HANDLER(WM_SHOWWINDOW, OnShowWindow)
CHAIN_MSG_MAP(_baseClass)
END_MSG_MAP()
LRESULT OnShowWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
...{
if (m_nSinglePane == SPLIT_PANE_NONE)
...{
if ( m_hWndPane[SPLIT_PANE_LEFT] )
::ShowWindow( m_hWndPane[SPLIT_PANE_LEFT], (BOOL)wParam);
if ( m_hWndPane[SPLIT_PANE_RIGHT] )
::ShowWindow( m_hWndPane[SPLIT_PANE_RIGHT], (BOOL)wParam);
}
else
...{
if ( m_hWndPane[m_nSinglePane] )
::ShowWindow( m_hWndPane[m_nSinglePane], (BOOL)wParam);
}
bHandled = FALSE;
return 0;
}
};
typedef CSplitterWindowExT<true> CSplitterWindowEx;
typedef CSplitterWindowExT<false> CHorSplitterWindowEx;
}; // namespace WTL
我們看到CSplitterWindowEx包含一個新的函數CreateFromID和CreateFromWindow,這兩個函數的目的是可以從對話方塊視窗的一個佔位用靜態視窗來產生擴充的分隔視窗對象,並從其中繼承視窗屬性。這樣我們可以VS60資源設計器來進行分隔視窗的位置設計及視窗屬性設計。
資源編輯器:
我們使用IDC_PANE2_STATIC和IDC_PANE_STATIC這兩個Picture控制項來實現佔位操作。目的有三,一是方便從這兩個佔位控制項產生分隔視窗控制,二是通過資源編輯器更改Picure控制項的視窗屬性可以控制產生的分隔視窗的視窗屬性。三是可以使用CDialogResize直接控制分隔視窗的縮放布局。代碼如下:
CSplitterWindowEx m_wndSplit;
CHorSplitterWindowEx m_wndHorSplit;
初始化分隔視窗控制的代碼放在OnInitDialog函數中。
m_wndHorSplit.CreateFromWindow(GetDlgItem(IDC_PANEL2_STATIC));
m_wndHorSplit.SetSplitterPanes( GetDlgItem(IDC_EDIT1), GetDlgItem(IDC_EDIT2) );
m_wndHorSplit.SetSplitterPos();
m_wndSplit.CreateFromID(m_hWnd,IDC_PANEL_STATIC);
m_wndSplit.SetSplitterPanes(GetDlgItem(IDC_LIST1), m_wndHorSplit);
m_wndSplit.SetSplitterPos(100);
怎麼樣,比修改前的WTL分隔視窗用起來簡單多了吧。試一下運行效果,支援TAB順序切換。與中VS60資源編輯器設計相比,是不是所見即所得 (WYSIWYG)。:)
運行
對於CDialogResize布局控制我們可以通過VisualFC的WTL類嚮導來自動產生,產生後的代碼如下:
BEGIN_DLGRESIZE_MAP(CMainDlg)
DLGRESIZE_CONTROL(IDC_BUTTON1, DLSZ_MOVE_X)
DLGRESIZE_CONTROL(ID_APP_ABOUT, DLSZ_MOVE_X | DLSZ_MOVE_Y)
DLGRESIZE_CONTROL(IDOK, DLSZ_MOVE_X)
DLGRESIZE_CONTROL(IDCANCEL, DLSZ_MOVE_X)
DLGRESIZE_CONTROL(IDC_PANEL_STATIC, DLSZ_SIZE_X | DLSZ_SIZE_Y)
END_DLGRESIZE_MAP()
需要注意一點是不要將IDC_EDIT1、IDC_EDIT2和IDC_LIST1以及IDC_PANEL2_STATIC添加到DLGRESIZE中,因為這些控制項雖然仍屬於主視窗,但已經為WTL分隔視窗所管理了。
最後回顧一下,我們對WTL的atlsplit.h檔案進行修改,完善了WTL的分隔視窗類別,允許對話方塊程式中控制項的父視窗不更改也可添加到WTL的分隔視窗中,我們通過一個派生的分隔視窗類別atlsplitex.h實現了對話方塊分隔視窗的直接控制,包括使用VS60資源編輯器所見即所得 (WYSIWYG)的設計以及對縮放布局的支援。
本文代碼下載:wtlsplitex.zip
代碼中包括修改後的atlsplit.h和atlsplitex.h檔案,你可以將atlsplit.h檔案替換WTL80中的atlsplit.h檔案,它的表現與原來的atlsplit.h檔案完全一致。