原文链接地址:https://www.codeproject.com/Articles/595602/Splitter-Control-for-Dialog
Introduction
Yes, that is another-another splitter control. This control based the control of this (Another splitter control for dialog). I have taken some changes of the origin code to make it much more easy to use. The new splitter-control can auto change the linked control's pos automatically, when the user changes the split-bar. That is it!
Background
I'm working for a GUI application which has many dialogs. But the developer before me makes all of the dialogs fixed size. Sometimes, that is ok. But the others will make the interface be hard to use. I have to move the scroll bar to see the part which be hidden by the fixed sized dialog. When I have a chance to modify it, I decide to make some change. From that time on, I look for a splitter control which can be used in the dialog.
There are many kinds of splitter-control in CodeProject. I like the one which is written by Hung Nguyen very much: Another splitter control for dialog. The commit dialog of the svn uses that one too. When I used this control in many of my application, I found that it has some small problem that I have to call ChangePos
function in many places. In other words, it cannot move the relation control automatically. So I make a new one to solve this problem.
Using the Code
Step 1: Add a Picture Control on Your Dialog at the Resource Editor.
Put a picture control on your dialog, give it id IDC_SPLITTER1
. Change the size of the control to make it look like a horizontal bar. Double click the control of IDC_SPLITTER1
, change the property as below. And then add a vertical one the same way, give it id IDC_SPLITTER2
.
Actually, all the operations above just want to make us not calc the splitter size. You can specify the size of the splitter by the CSplitterControl::Create
function.
Step 2: Add Splitters to the Dialog Class
Add SplitterControl.h and SplitterControl.cpp to your project. Insert #include "splittercontrol.h"
to the .h file of the dialog
class.
And then, add member variables:
1 private: 2 CSplitterControl m_wndSplitter1; 3 CSplitterControl m_wndSplitter2;
In the function of OnInitDialog
, create the splitters.
There are some notices here:
- Use
SPS_VERTICAL
orSPS_HORIZONTAL
to special the splitter style. - You can special a RGB color (the default id RGB(120, 120, 120)) for the splitter line.
- You can special the splitter line width (the default is 1).
- The
width(SPS_VERTICAL)
orheight(SPS_HORIZONTAL)
of the splitter depend on thewidth
ofrect
when you callCreate
function.
1 BOOL CSplitterControlDemoDlg::OnInitDialog() 2 { 3 CDialog::OnInitDialog(); 4 5 // Here, I ignore some code we not care about. 6 // You can reference the sample code for details. 7 8 CRect rc; 9 CWnd* pWnd; 10 11 pWnd = GetDlgItem(IDC_SPLITTER1); 12 pWnd->GetWindowRect(rc); 13 ScreenToClient(rc); 14 BOOL bRet = m_wndSplitter1.Create(WS_CHILD | WS_VISIBLE, rc, 15 this, IDC_SPLITTER1, SPS_VERTICAL|SPS_DELTA_NOTIFY);//, RGB(255, 0, 0)); 16 if (FALSE == bRet) 17 { 18 AfxMessageBox("m_wndSplitter1 create failed"); 19 } 20 21 pWnd = GetDlgItem(IDC_SPLITTER2); 22 pWnd->GetWindowRect(rc); 23 ScreenToClient(rc); 24 bRet = m_wndSplitter2.Create(WS_CHILD | WS_VISIBLE, rc, 25 this, IDC_SPLITTER2, SPS_HORIZONTAL, RGB(0, 0, 255)); 26 if (FALSE == bRet) 27 { 28 AfxMessageBox("m_wndSplitter2 create failed"); 29 }
Step 3: Add Linked Windows
The splitter can move the linked window pos automatically as the user changes the splitter's pos. So we should specify which window needs to change the pos.CSplitterControl::RegisterLinkedWindow
function take the work. Check the example below:
1 // register windows for splitter 2 this->m_wndSplitter1.RegisterLinkedWindow(SPLS_LINKED_LEFT, GetDlgItem(IDC_TREE)); 3 this->m_wndSplitter1.RegisterLinkedWindow(SPLS_LINKED_RIGHT, GetDlgItem(IDC_LIST)); 4 this->m_wndSplitter1.RegisterLinkedWindow(SPLS_LINKED_RIGHT, GetDlgItem(IDC_EDIT)); 5 this->m_wndSplitter1.RegisterLinkedWindow(SPLS_LINKED_RIGHT, &m_wndSplitter2); 6 7 this->m_wndSplitter2.RegisterLinkedWindow(SPLS_LINKED_UP, GetDlgItem(IDC_LIST)); 8 this->m_wndSplitter2.RegisterLinkedWindow(SPLS_LINKED_DOWN, GetDlgItem(IDC_EDIT)); 9 10 // relayout the splitter to make them good look 11 this->m_wndSplitter1.Relayout(); 12 this->m_wndSplitter2.Relayout();
Remember that SPLS_LINKED_LEFT
means the control is on the left side of the splitter. SPLS_LINKED_RIGHT
means the right. The two used for the splitter of vertical. If the control linked to a splitter of vertical with SPLS_LINKED_LEFT
, that means the right pos of the control will be changed by the splitter. SPLS_LINKED_RIGHT
is similar to SPLS_LINKED_LEFT
.
SPLS_LINKED_UP
and SPLS_LINKED_DOWN
as the name means the control will be at the up side of a horizontal splitter.
We have almost finished, after we have linked the controls to the splitter. In order to make the interface look better, we should call CSplitterControl::Relayout
function to make the initializing layout. You can call CSplitterControl::Relayout
function at any time you need.
Step 4: Splitter's Limit Pos
Usually, we need to set the splitter's moving range. That is not very important in the Document-View based application. But in the dialog based application, we have to process the window's edge ourselves. So, the limit pos of the splitter is very important for dialog based application.
In the sizabled
dialog, the limit pos of the splitter is not fixed. Once we change the dialog size, we have to change the new limit pos of the splitter. That is not very good. In my splitter control, they send a notify message to the parent window before you are ready to change the splitter pos every time. So if you want to use this, set the limit pos, just handle the notify message. The notify message is named SPN_MAXMINPOS
. There is some sample code here.
Add message handle function in the .h file of the dialog
class for the notify message (SPN_MAXMINPOS
):
1 afx_msg void OnMaxMinInfo(NMHDR* pNMHDR, LRESULT* pResult);
Map the message:
1 BEGIN_MESSAGE_MAP(CSplitterControlDemoDlg, CDialog) 2 //{{AFX_MSG_MAP(CSplitterControlDemoDlg) 3 // ... 4 ON_NOTIFY(SPN_MAXMINPOS, IDC_SPLITTER2, OnMaxMinInfo) 5 ON_NOTIFY(SPN_MAXMINPOS, IDC_SPLITTER1, OnMaxMinInfo) 6 // ... 7 //}}AFX_MSG_MAP 8 END_MESSAGE_MAP()
Implement the message handle function:
1 void CSplitterControlDemoDlg::OnMaxMinInfo(NMHDR* pNMHDR, LRESULT* pResult) 2 { 3 // Get current pos of the child controls 4 CRect rcTree; 5 CRect rcList; 6 CRect rcEdit; 7 CRect rcCancel; 8 m_wndType.GetWindowRect(rcTree); 9 m_lstItem.GetWindowRect(rcList); 10 m_txtContent.GetWindowRect(rcEdit); 11 m_btnCancel.GetWindowRect(rcCancel); 12 13 this->ScreenToClient(rcTree); 14 this->ScreenToClient(rcList); 15 this->ScreenToClient(rcEdit); 16 this->ScreenToClient(rcCancel); 17 18 // return the pos limit 19 SPC_NM_MAXMINPOS* pNewMaxMinPos = (SPC_NM_MAXMINPOS*)pNMHDR; 20 if (IDC_SPLITTER1 == pNMHDR->idFrom) 21 { 22 pNewMaxMinPos->lMin = rcTree.left + 50; 23 pNewMaxMinPos->lMax = rcCancel.left - STD_GAP; 24 } 25 else 26 { 27 pNewMaxMinPos->lMin = rcList.top + 50; 28 pNewMaxMinPos->lMax = rcEdit.bottom - 50; 29 } 30 }
Step 5: Some Special Used Method
Not everything will automatically be ok. The splitter needs us to register all of the linked controls to it when the dialog is initializing. So if a control is not created at that time, we cannot register it to the splitter. So the splitter provided another way to change the pos of these controls. The kernel function is CSplitterControl::ChangePos
. This function accepts a parameter dwLinkedSide
to specify the control is which side of the splitter. And the lDelta
usually is from the notify message SPN_DELTA
.
The SPN_DELTA
notify message is sent when the user releases the mouse. It is defined in the SplitterControl.h.
1 // Notify event : tell the parent to do some special things 2 // some times, the parent window can not register the child control for reason 3 // it does not created yet. 4 // so, SPN_DELTA event give the parent window a chance to change the child control's pos. 5 #define SPN_DELTA (WM_USER + 2) 6 struct SPC_NM_DELTA 7 { 8 NMHDR hdr; 9 LONG lDelta; 10 };
There is some sample code to show how to use this notify message:
Firstly, we need to use the SPS_DELTA_NOTIFY
style as we create the splitter.
1 BOOL bRet = m_wndSplitter1.Create(WS_CHILD | WS_VISIBLE, rc, 2 this, IDC_SPLITTER1, SPS_VERTICAL|SPS_DELTA_NOTIFY);//, RGB(255, 0, 0));
And then, add message maps:
1 BEGIN_MESSAGE_MAP(CSplitterControlDemoDlg, CDialog) 2 // ... 3 ON_NOTIFY(SPN_DELTA, IDC_SPLITTER1, OnSplitter1Delta) 4 // ... 5 //}}AFX_MSG_MAP 6 END_MESSAGE_MAP()
At last, implement it:
1 void CSplitterControlDemoDlg::OnSplitter1Delta(NMHDR* pNMHDR, LRESULT* pResult) 2 { 3 // this function just want to show you how to use the delta event 4 *pResult = 0; 5 6 SPC_NM_DELTA* pDelta = (SPC_NM_DELTA*)pNMHDR; 7 if (NULL == pDelta) 8 { 9 return; 10 } 11 12 m_wndSplitter1.ChangePos(&m_edHelp, SPLS_LINKED_LEFT, pDelta->lDelta); 13 }
完整代码
SplitterControlDemoDlg.h
1 // SplitterControlDemoDlg.h : header file 2 // 3 4 #if !defined(AFX_SPLITTERCONTROLDEMODLG_H__DF3D3AA3_8536_469C_B6A6_32FDC5549ABD__INCLUDED_) 5 #define AFX_SPLITTERCONTROLDEMODLG_H__DF3D3AA3_8536_469C_B6A6_32FDC5549ABD__INCLUDED_ 6 7 #if _MSC_VER > 1000 8 #pragma once 9 #endif // _MSC_VER > 1000 10 11 #include "SplitterControl.h" 12 13 14 15 #define STD_GAP 8 16 #define STD_BUTTON_WIDTH 90 17 #define STD_BUTTON_HEIGHT 25 18 19 20 ///////////////////////////////////////////////////////////////////////////// 21 // CSplitterControlDemoDlg dialog 22 class CSplitterControlDemoDlg : public CDialog 23 { 24 // Construction 25 public: 26 CSplitterControlDemoDlg(CWnd* pParent = NULL); // standard constructor 27 28 // Dialog Data 29 //{{AFX_DATA(CSplitterControlDemoDlg) 30 enum { IDD = IDD_SPLITTERCONTROLDEMO_DIALOG }; 31 CButton m_btnCancel; 32 CEdit m_edHelp; 33 CStatic m_stTitle; 34 CEdit m_txtContent; 35 CListCtrl m_lstItem; 36 CTreeCtrl m_wndType; 37 HICON m_hIcon; 38 CSplitterControl m_wndSplitter1; 39 CSplitterControl m_wndSplitter2; 40 //}}AFX_DATA 41 42 // ClassWizard generated virtual function overrides 43 //{{AFX_VIRTUAL(CSplitterControlDemoDlg) 44 protected: 45 virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support 46 //}}AFX_VIRTUAL 47 48 49 // Implementation 50 protected: 51 void InitSampleData(); 52 void Resize(); 53 void MoveDlgItem(int nD, const CRect& rcPos, BOOL bRepaint); 54 55 // Generated message map functions 56 //{{AFX_MSG(CSplitterControlDemoDlg) 57 virtual BOOL OnInitDialog(); 58 afx_msg void OnSysCommand(UINT nID, LPARAM lParam); 59 afx_msg void OnPaint(); 60 afx_msg HCURSOR OnQueryDragIcon(); 61 afx_msg void OnSelchangedTree(NMHDR* pNMHDR, LRESULT* pResult); 62 afx_msg void OnItemchangedList(NMHDR* pNMHDR, LRESULT* pResult); 63 afx_msg void OnMaxMinInfo(NMHDR* pNMHDR, LRESULT* pResult); 64 afx_msg void OnSplitter1Delta(NMHDR* pNMHDR, LRESULT* pResult); 65 afx_msg void OnSize(UINT nType, int cx, int cy); 66 //}}AFX_MSG 67 DECLARE_MESSAGE_MAP() 68 }; 69 70 //{{AFX_INSERT_LOCATION}} 71 // Microsoft Visual C++ will insert additional declarations immediately before the previous line. 72 73 #endif // !defined(AFX_SPLITTERCONTROLDEMODLG_H__DF3D3AA3_8536_469C_B6A6_32FDC5549ABD__INCLUDED_)
SplitterControlDemoDlg.cpp代码
1 // SplitterControlDemoDlg.cpp : implementation file 2 // 3 4 #include "stdafx.h" 5 #include "SplitterControlDemo.h" 6 #include "SplitterControlDemoDlg.h" 7 8 #ifdef _DEBUG 9 #define new DEBUG_NEW 10 #undef THIS_FILE 11 static char THIS_FILE[] = __FILE__; 12 #endif 13 14 ///////////////////////////////////////////////////////////////////////////// 15 // CAboutDlg dialog used for App About 16 17 class CAboutDlg : public CDialog 18 { 19 public: 20 CAboutDlg(); 21 22 // Dialog Data 23 //{{AFX_DATA(CAboutDlg) 24 enum { IDD = IDD_ABOUTBOX }; 25 //}}AFX_DATA 26 27 // ClassWizard generated virtual function overrides 28 //{{AFX_VIRTUAL(CAboutDlg) 29 protected: 30 virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support 31 //}}AFX_VIRTUAL 32 33 // Implementation 34 protected: 35 //{{AFX_MSG(CAboutDlg) 36 //}}AFX_MSG 37 DECLARE_MESSAGE_MAP() 38 }; 39 40 CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) 41 { 42 //{{AFX_DATA_INIT(CAboutDlg) 43 //}}AFX_DATA_INIT 44 } 45 46 void CAboutDlg::DoDataExchange(CDataExchange* pDX) 47 { 48 CDialog::DoDataExchange(pDX); 49 //{{AFX_DATA_MAP(CAboutDlg) 50 //}}AFX_DATA_MAP 51 } 52 53 BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) 54 //{{AFX_MSG_MAP(CAboutDlg) 55 // No message handlers 56 //}}AFX_MSG_MAP 57 END_MESSAGE_MAP() 58 59 ///////////////////////////////////////////////////////////////////////////// 60 // CSplitterControlDemoDlg dialog 61 62 CSplitterControlDemoDlg::CSplitterControlDemoDlg(CWnd* pParent /*=NULL*/) 63 : CDialog(CSplitterControlDemoDlg::IDD, pParent) 64 { 65 //{{AFX_DATA_INIT(CSplitterControlDemoDlg) 66 // NOTE: the ClassWizard will add member initialization here 67 //}}AFX_DATA_INIT 68 // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 69 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); 70 } 71 72 void CSplitterControlDemoDlg::DoDataExchange(CDataExchange* pDX) 73 { 74 CDialog::DoDataExchange(pDX); 75 //{{AFX_DATA_MAP(CSplitterControlDemoDlg) 76 DDX_Control(pDX, IDCANCEL, m_btnCancel); 77 DDX_Control(pDX, IDC_EDIT_HELP, m_edHelp); 78 DDX_Control(pDX, IDC_STATIC_TITLE, m_stTitle); 79 DDX_Control(pDX, IDC_EDIT, m_txtContent); 80 DDX_Control(pDX, IDC_LIST, m_lstItem); 81 DDX_Control(pDX, IDC_TREE, m_wndType); 82 //}}AFX_DATA_MAP 83 } 84 85 BEGIN_MESSAGE_MAP(CSplitterControlDemoDlg, CDialog) 86 //{{AFX_MSG_MAP(CSplitterControlDemoDlg) 87 ON_WM_SYSCOMMAND() 88 ON_WM_PAINT() 89 ON_WM_QUERYDRAGICON() 90 ON_NOTIFY(TVN_SELCHANGED, IDC_TREE, OnSelchangedTree) 91 ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST, OnItemchangedList) 92 ON_WM_SIZE() 93 ON_NOTIFY(SPN_MAXMINPOS, IDC_SPLITTER2, OnMaxMinInfo) 94 ON_NOTIFY(SPN_MAXMINPOS, IDC_SPLITTER1, OnMaxMinInfo) 95 ON_NOTIFY(SPN_DELTA, IDC_SPLITTER1, OnSplitter1Delta) 96 ON_WM_GETMINMAXINFO() 97 //}}AFX_MSG_MAP 98 END_MESSAGE_MAP() 99 100 ///////////////////////////////////////////////////////////////////////////// 101 // CSplitterControlDemoDlg message handlers 102 103 BOOL CSplitterControlDemoDlg::OnInitDialog() 104 { 105 CDialog::OnInitDialog(); 106 107 // Add "About..." menu item to system menu. 108 109 // IDM_ABOUTBOX must be in the system command range. 110 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); 111 ASSERT(IDM_ABOUTBOX < 0xF000); 112 113 CMenu* pSysMenu = GetSystemMenu(FALSE); 114 if (pSysMenu != NULL) 115 { 116 CString strAboutMenu; 117 strAboutMenu.LoadString(IDS_ABOUTBOX); 118 if (!strAboutMenu.IsEmpty()) 119 { 120 pSysMenu->AppendMenu(MF_SEPARATOR); 121 pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); 122 } 123 } 124 125 // Set the icon for this dialog. The framework does this automatically 126 // when the application's main window is not a dialog 127 SetIcon(m_hIcon, TRUE); // Set big icon 128 SetIcon(m_hIcon, FALSE); // Set small icon 129 130 // TODO: Add extra initialization here 131 CRect rc; 132 CWnd* pWnd; 133 134 pWnd = GetDlgItem(IDC_SPLITTER1); 135 pWnd->GetWindowRect(rc); 136 ScreenToClient(rc); 137 BOOL bRet = m_wndSplitter1.Create(WS_CHILD | WS_VISIBLE, rc, this, IDC_SPLITTER1, SPS_VERTICAL|SPS_DELTA_NOTIFY);//, RGB(255, 0, 0)); 138 if (FALSE == bRet) 139 { 140 AfxMessageBox("m_wndSplitter1 create failed"); 141 } 142 143 pWnd = GetDlgItem(IDC_SPLITTER2); 144 pWnd->GetWindowRect(rc); 145 ScreenToClient(rc); 146 bRet = m_wndSplitter2.Create(WS_CHILD | WS_VISIBLE, rc, this, IDC_SPLITTER2, SPS_HORIZONTAL, RGB(0, 0, 255)); 147 if (FALSE == bRet) 148 { 149 AfxMessageBox("m_wndSplitter2 create failed"); 150 } 151 152 InitSampleData(); 153 154 // register windows for splitter 155 this->m_wndSplitter1.RegisterLinkedWindow(SPLS_LINKED_LEFT, GetDlgItem(IDC_TREE)); 156 this->m_wndSplitter1.RegisterLinkedWindow(SPLS_LINKED_RIGHT, GetDlgItem(IDC_LIST)); 157 this->m_wndSplitter1.RegisterLinkedWindow(SPLS_LINKED_RIGHT, GetDlgItem(IDC_EDIT)); 158 this->m_wndSplitter1.RegisterLinkedWindow(SPLS_LINKED_RIGHT, &m_wndSplitter2); 159 160 this->m_wndSplitter2.RegisterLinkedWindow(SPLS_LINKED_UP, GetDlgItem(IDC_LIST)); 161 this->m_wndSplitter2.RegisterLinkedWindow(SPLS_LINKED_DOWN, GetDlgItem(IDC_EDIT)); 162 163 // relayout the splotter to make them good look 164 this->m_wndSplitter1.Relayout(); 165 this->m_wndSplitter2.Relayout(); 166 167 this->Resize(); 168 169 return TRUE; // return TRUE unless you set the focus to a control 170 } 171 172 void CSplitterControlDemoDlg::OnSysCommand(UINT nID, LPARAM lParam) 173 { 174 if ((nID & 0xFFF0) == IDM_ABOUTBOX) 175 { 176 CAboutDlg dlgAbout; 177 dlgAbout.DoModal(); 178 } 179 else 180 { 181 CDialog::OnSysCommand(nID, lParam); 182 } 183 } 184 185 // If you add a minimize button to your dialog, you will need the code below 186 // to draw the icon. For MFC applications using the document/view model, 187 // this is automatically done for you by the framework. 188 void CSplitterControlDemoDlg::OnPaint() 189 { 190 if (IsIconic()) 191 { 192 CPaintDC dc(this); // device context for painting 193 194 SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); 195 196 // Center icon in client rectangle 197 int cxIcon = GetSystemMetrics(SM_CXICON); 198 int cyIcon = GetSystemMetrics(SM_CYICON); 199 CRect rect; 200 GetClientRect(&rect); 201 int x = (rect.Width() - cxIcon + 1) / 2; 202 int y = (rect.Height() - cyIcon + 1) / 2; 203 204 // Draw the icon 205 dc.DrawIcon(x, y, m_hIcon); 206 } 207 else 208 { 209 CDialog::OnPaint(); 210 } 211 } 212 213 // The system calls this to obtain the cursor to display while the user drags 214 // the minimized window. 215 HCURSOR CSplitterControlDemoDlg::OnQueryDragIcon() 216 { 217 return (HCURSOR) m_hIcon; 218 } 219 220 221 void CSplitterControlDemoDlg::InitSampleData() 222 { 223 m_wndType.ModifyStyle(0, TVS_LINESATROOT | TVS_HASBUTTONS 224 | TVS_SHOWSELALWAYS | TVS_HASLINES ); 225 HTREEITEM hRoot = m_wndType.InsertItem("Local folder"); 226 HTREEITEM h1 = m_wndType.InsertItem("Inbox", hRoot); 227 HTREEITEM h2 = m_wndType.InsertItem("Outbox", hRoot); 228 HTREEITEM h3 = m_wndType.InsertItem("Send Items", hRoot); 229 m_wndType.SetItemData(hRoot, 0); 230 m_wndType.SetItemData(h1, 1); 231 m_wndType.SetItemData(h2, 2); 232 m_wndType.SetItemData(h3, 3); 233 234 m_lstItem.ModifyStyle(0, LVS_REPORT); 235 m_lstItem.InsertColumn(0, "From", LVCFMT_LEFT, 100); 236 m_lstItem.InsertColumn(1, "Subject", LVCFMT_LEFT, 100); 237 238 m_edHelp.SetWindowText("Yes, I'm the help edit."); 239 } 240 241 void CSplitterControlDemoDlg::OnSelchangedTree(NMHDR* pNMHDR, LRESULT* pResult) 242 { 243 NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; 244 // TODO: Add your control notification handler code here 245 246 *pResult = 0; 247 248 HTREEITEM h = m_wndType.GetSelectedItem(); 249 DWORD nID = m_wndType.GetItemData(h); 250 251 m_lstItem.DeleteAllItems(); 252 m_lstItem.DeleteColumn(1); 253 m_lstItem.DeleteColumn(0); 254 255 switch(nID) 256 { 257 case 0:break; 258 case 1: 259 m_lstItem.InsertColumn(0, "From", LVCFMT_LEFT, 100); 260 m_lstItem.InsertColumn(1, "Subject", LVCFMT_LEFT, 200); 261 m_lstItem.InsertItem(0, "Dad"); 262 m_lstItem.SetItemText(0, 1, "Dad's letter"); 263 m_lstItem.SetItemData(0, 0); 264 265 m_lstItem.InsertItem(1, "AnhPhong"); 266 m_lstItem.SetItemText(1, 1, "Hi, how are you ?"); 267 m_lstItem.SetItemData(1, 1); 268 269 m_lstItem.InsertItem(2, "TrungHau"); 270 m_lstItem.SetItemText(2, 1, "Reply to Hi"); 271 m_lstItem.SetItemData(2, 2); 272 break; 273 case 2: 274 m_lstItem.InsertColumn(0, "Subject", LVCFMT_LEFT, 200); 275 m_lstItem.InsertColumn(1, "Recipcent", LVCFMT_LEFT, 100); 276 m_lstItem.InsertItem(0, "Reply to Dad's letter"); 277 m_lstItem.SetItemData(0, 100); 278 279 m_lstItem.SetItemText(0, 1, "Dad"); 280 m_lstItem.InsertItem(1, "I'm fine, and you !"); 281 m_lstItem.SetItemText(1, 1, "AnhPhong"); 282 m_lstItem.SetItemData(1, 101); 283 break; 284 case 3: 285 m_lstItem.InsertColumn(0, "From", LVCFMT_LEFT, 100); 286 m_lstItem.InsertColumn(1, "Subject", LVCFMT_LEFT, 200); 287 m_lstItem.InsertItem(0, "TrungHau"); 288 m_lstItem.SetItemText(0, 1, "Hi"); 289 m_lstItem.SetItemData(0, 200); 290 break; 291 } 292 } 293 294 void CSplitterControlDemoDlg::OnItemchangedList(NMHDR* pNMHDR, LRESULT* pResult) 295 { 296 NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; 297 *pResult = 0; 298 299 CString strContent = ""; 300 POSITION pos = m_lstItem.GetFirstSelectedItemPosition(); 301 if (pos != NULL) 302 { 303 int nCurSel = m_lstItem.GetNextSelectedItem(pos); 304 int n = m_lstItem.GetItemData(nCurSel); 305 switch(n) 306 { 307 case 0: strContent = "content of Dad's letter"; 308 break; 309 case 1: strContent = "content of Hi, how are you ?"; 310 break; 311 case 2: strContent = "content of Reply to Hi "; 312 break; 313 314 case 100 : strContent = "content of Reply to Dad's letter"; 315 break; 316 case 101: strContent = "content of I'm fine, and you !"; 317 break; 318 319 case 200: strContent = "content of Hi"; 320 break; 321 322 } 323 } 324 m_txtContent.SetWindowText(strContent); 325 } 326 327 void CSplitterControlDemoDlg::OnSize(UINT nType, int cx, int cy) 328 { 329 this->Resize(); 330 } 331 332 void CSplitterControlDemoDlg::MoveDlgItem(int nID, const CRect& rcPos, BOOL bRepaint) 333 { 334 CWnd* pWnd = this->GetDlgItem(nID); 335 if (NULL == pWnd) 336 { 337 return; 338 } 339 340 pWnd->MoveWindow(rcPos, bRepaint); 341 342 CRect rcsp; 343 m_wndSplitter2.GetWindowRect(rcsp); 344 this->ScreenToClient(rcsp); 345 } 346 347 void CSplitterControlDemoDlg::Resize() 348 { 349 CRect rcDlg; 350 this->GetClientRect(rcDlg); 351 352 CRect rcTitle; 353 rcTitle.left = rcDlg.left + STD_GAP; 354 rcTitle.right = rcDlg.right - STD_GAP; 355 rcTitle.top = rcDlg.top + STD_GAP; 356 rcTitle.bottom = rcTitle.top + STD_BUTTON_HEIGHT; 357 this->MoveDlgItem(IDC_STATIC_TITLE, rcTitle, TRUE); 358 359 CRect rcOK; 360 rcOK.right = rcTitle.right; 361 rcOK.bottom = rcDlg.bottom - STD_GAP; 362 rcOK.top = rcOK.bottom - STD_BUTTON_HEIGHT; 363 rcOK.left = rcOK.right - STD_BUTTON_WIDTH; 364 this->MoveDlgItem(IDOK, rcOK, TRUE); 365 366 CRect rcCancel; 367 rcCancel.right = rcOK.left - STD_GAP; 368 rcCancel.left = rcCancel.right- STD_BUTTON_WIDTH; 369 rcCancel.top = rcOK.top; 370 rcCancel.bottom = rcOK.bottom; 371 this->MoveDlgItem(IDCANCEL, rcCancel, TRUE); 372 373 374 if (FALSE == IsWindow(m_wndSplitter1.GetSafeHwnd())) 375 { 376 return; 377 } 378 379 CRect rcSplit1; 380 m_wndSplitter1.GetWindowRect(rcSplit1); 381 this->ScreenToClient(rcSplit1); 382 rcSplit1.bottom = rcOK.top - STD_GAP; 383 this->m_wndSplitter1.MoveWindow(rcSplit1, TRUE); 384 385 CRect rcSplit2; 386 m_wndSplitter2.GetWindowRect(rcSplit2); 387 this->ScreenToClient(rcSplit2); 388 rcSplit2.right = rcOK.right; 389 this->m_wndSplitter2.MoveWindow(rcSplit2, TRUE); 390 391 CRect rcTree; 392 LONG lTreeWidth = rcTree.Width(); 393 rcTree.left = rcTitle.left; 394 rcTree.right = rcSplit1.left; 395 rcTree.top = rcTitle.bottom + STD_GAP; 396 rcTree.bottom = rcOK.top - STD_GAP; 397 this->MoveDlgItem(IDC_TREE, rcTree, TRUE); 398 399 CRect rcList; 400 rcList.top = rcTree.top; 401 rcList.bottom = rcSplit2.top; 402 rcList.left = rcSplit1.right; 403 rcList.right = rcOK.right; 404 this->MoveDlgItem(IDC_LIST, rcList, TRUE); 405 406 CRect rcEdit; 407 rcEdit.left = rcList.left; 408 rcEdit.right = rcList.right; 409 rcEdit.top = rcSplit2.bottom; 410 rcEdit.bottom = rcOK.top - STD_GAP; 411 this->MoveDlgItem(IDC_EDIT, rcEdit, TRUE); 412 413 CRect rcHelp; 414 rcHelp.left = rcTree.left; 415 rcHelp.right = rcTree.right; 416 rcHelp.top = rcOK.top; 417 rcHelp.bottom = rcOK.bottom; 418 this->MoveDlgItem(IDC_EDIT_HELP, rcHelp, TRUE); 419 } 420 421 void CSplitterControlDemoDlg::OnMaxMinInfo(NMHDR* pNMHDR, LRESULT* pResult) 422 { 423 // Get current pos of the child controls 424 CRect rcTree; 425 CRect rcList; 426 CRect rcEdit; 427 CRect rcCancel; 428 m_wndType.GetWindowRect(rcTree); 429 m_lstItem.GetWindowRect(rcList); 430 m_txtContent.GetWindowRect(rcEdit); 431 m_btnCancel.GetWindowRect(rcCancel); 432 433 this->ScreenToClient(rcTree); 434 this->ScreenToClient(rcList); 435 this->ScreenToClient(rcEdit); 436 this->ScreenToClient(rcCancel); 437 438 // return the pos limit 439 SPC_NM_MAXMINPOS* pNewMaxMinPos = (SPC_NM_MAXMINPOS*)pNMHDR; 440 if (IDC_SPLITTER1 == pNMHDR->idFrom) 441 { 442 pNewMaxMinPos->lMin = rcTree.left + 50; 443 pNewMaxMinPos->lMax = rcCancel.left - STD_GAP; 444 } 445 else 446 { 447 pNewMaxMinPos->lMin = rcList.top + 50; 448 pNewMaxMinPos->lMax = rcEdit.bottom - 50; 449 } 450 } 451 452 void CSplitterControlDemoDlg::OnSplitter1Delta(NMHDR* pNMHDR, LRESULT* pResult) 453 { 454 // this function just want to show you how to use the delta event 455 *pResult = 0; 456 457 SPC_NM_DELTA* pDelta = (SPC_NM_DELTA*)pNMHDR; 458 if (NULL == pDelta) 459 { 460 return; 461 } 462 463 m_wndSplitter1.ChangePos(&m_edHelp, SPLS_LINKED_LEFT, pDelta->lDelta); 464 }
SplitterControl.h
1 #ifndef __CSplitterControl_H_ 2 #define __CSplitterControl_H_ 3 4 5 #if _MSC_VER > 1000 6 #pragma once 7 #endif // _MSC_VER > 1000 8 // SplitterControl.h : header file 9 // 10 11 12 #include "AFXTEMPL.h" 13 14 15 ///////////////////////////////////////////////////////////////////////////// 16 // Splitter style 17 #define SPS_HORIZONTAL 0x00000000 // Horizontal look splitter 18 #define SPS_VERTICAL 0x00000001 // Vertical look splitter(this is the default style) 19 #define SPS_DELTA_NOTIFY 0x00000002 // if need notify the parent to handle delta message 20 21 22 // Linked side(used for Vertical style) 23 #define SPLS_LINKED_RIGHT 0x00000000 // linked window at the right of the splitter(right pos change automatic) 24 #define SPLS_LINKED_LEFT 0x00000001 // linked window at the left of the splitter(right pos change automatic) 25 26 27 // Linked side(used for Horizontal style) 28 #define SPLS_LINKED_UP SPLS_LINKED_LEFT // linked window at the top of the splitter(right pos change automatic) 29 #define SPLS_LINKED_DOWN SPLS_LINKED_RIGHT // linked window at the bottom of the splitter(right pos change automatic) 30 31 32 // Notify event : to get the max/min pos limit 33 // usualy, the max/min pos depend on the parent window size, so we'd better get it from parent. 34 #define SPN_MAXMINPOS (WM_USER + 1) 35 struct SPC_NM_MAXMINPOS 36 { 37 NMHDR hdr; 38 LONG lMax; 39 LONG lMin; 40 }; 41 42 43 // Notify event : tell the parent to do some special things 44 // some times, the parent window can not register the child control for reason it does not created yet. 45 // so, SPN_DELTA event give the parent window a chance to change the child control's pos. 46 #define SPN_DELTA (WM_USER + 2) 47 struct SPC_NM_DELTA 48 { 49 NMHDR hdr; 50 LONG lDelta; 51 }; 52 53 54 55 ///////////////////////////////////////////////////////////////////////////// 56 // CSplitterControl 57 // 58 class CSplitterControl : public CStatic 59 { 60 // Attributes 61 protected: 62 DWORD m_dwSplitterStyle; // Splitter Style 63 64 BOOL m_bMouseIsDown; // Record the mouse is down or up 65 CPoint m_ptCurrent; // Current positon 66 CPoint m_ptOrigin; // Positon when mouse down 67 HCURSOR m_hCursor; // Cursor when mouse move 68 69 COLORREF m_clrSplitterColor; // Color of splitter 70 LONG m_lSplitterWidth; // line width of splitter 71 72 int m_iMaxPos; // Max pos the splitter 73 int m_iMinPos; // Min pos the splitter 74 75 typedef CList<CWnd*, CWnd*> CLinkedWndList; 76 CLinkedWndList m_listLinkedWnds[2]; // Registered linked window 77 78 // Operations 79 public: 80 // Overrides 81 // ClassWizard generated virtual function overrides 82 //{{AFX_VIRTUAL(CSplitterControl) 83 //}}AFX_VIRTUAL 84 85 // Implementation 86 public: 87 // Constructor/Destructor 88 CSplitterControl(); 89 virtual ~CSplitterControl(); 90 91 // Create a splitter 92 BOOL Create(DWORD dwStaticStyle, const CRect& rect, CWnd* pParent, UINT nID, 93 DWORD dwSplitterControlStyle = SPS_VERTICAL, 94 COLORREF clrSplitterColor = RGB(120, 120, 120), 95 LONG lSplitterLineWidth = 1); 96 97 98 // Register linked window 99 BOOL RegisterLinkedWindow(DWORD dwLinkedSide, CWnd* pWnd); 100 101 102 // Usualy used in the SPN_DELTA notify event handle function 103 void ChangePos(CWnd* pWnd, DWORD dwLinkedSide, LONG lDelta); 104 105 // Relayout all linked windows 106 // you can call it in CDialog::OnInitDialog. 107 // if you have some special requirement, you can over write this function in sub class. 108 virtual void Relayout(); 109 110 111 // Generated message map functions 112 protected: 113 // Draw the splitter lines 114 // if the default implement is not suit for you, over write it. 115 virtual void DrawSplitterLine(CDC* pDC); 116 117 118 // To get the left most and the right most pos 119 // this function called before you change the splitter pos. 120 // it just send message to parent window, but there are some many other ways, 121 virtual void GetMaxMinPos(int& iMax, int& iMin); 122 123 124 // Move self to a new pos 125 void MoveSelfWindowToPoint(const CPoint& pt); 126 127 //{{AFX_MSG(CSplitterControl) 128 afx_msg void OnMouseMove(UINT nFlags, CPoint point); 129 afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message); 130 afx_msg void OnLButtonDown(UINT nFlags, CPoint point); 131 afx_msg void OnLButtonUp(UINT nFlags, CPoint point); 132 //}}AFX_MSG 133 134 DECLARE_MESSAGE_MAP() 135 }; 136 137 ///////////////////////////////////////////////////////////////////////////// 138 139 //{{AFX_INSERT_LOCATION}} 140 // Microsoft Visual C++ will insert additional declarations immediately before the previous line. 141 142 #endif//__CSplitterControl_H_
SplitterControl.cpp
1 // SplitterControl.cpp : implementation file 2 // 3 4 #include "stdafx.h" 5 #include "SplitterControl.h" 6 7 #ifdef _DEBUG 8 #define new DEBUG_NEW 9 #undef THIS_FILE 10 static char THIS_FILE[] = __FILE__; 11 #endif 12 13 14 ///////////////////////////////////////////////////////////////////////////// 15 // CSplitterControl 16 CSplitterControl::CSplitterControl() 17 { 18 m_dwSplitterStyle = SPS_VERTICAL; 19 m_bMouseIsDown = FALSE; 20 m_ptCurrent = CPoint(0, 0); 21 m_ptOrigin = CPoint(0, 0); 22 m_hCursor = NULL; 23 m_iMinPos = INT_MIN; 24 m_iMaxPos = INT_MAX; 25 m_clrSplitterColor = RGB(120, 120, 120); 26 m_lSplitterWidth = 1; 27 } 28 29 CSplitterControl::~CSplitterControl() 30 { 31 m_listLinkedWnds[SPLS_LINKED_LEFT].RemoveAll(); 32 m_listLinkedWnds[SPLS_LINKED_RIGHT].RemoveAll(); 33 } 34 35 36 BEGIN_MESSAGE_MAP(CSplitterControl, CStatic) 37 //{{AFX_MSG_MAP(CSplitterControl) 38 ON_WM_MOUSEMOVE() 39 ON_WM_SETCURSOR() 40 ON_WM_LBUTTONDOWN() 41 ON_WM_LBUTTONUP() 42 //}}AFX_MSG_MAP 43 END_MESSAGE_MAP() 44 45 ///////////////////////////////////////////////////////////////////////////// 46 // CSplitterControl message handlers 47 BOOL CSplitterControl::Create(DWORD dwStaticStyle, const CRect& rect, CWnd* pParent, UINT nID, 48 DWORD dwSplitterControlStyle, COLORREF clrSplitterColor, LONG lSplitterLineWidth) 49 { 50 // Save styles 51 m_dwSplitterStyle = dwSplitterControlStyle; 52 53 // Sace splitter color 54 m_clrSplitterColor = clrSplitterColor; 55 56 // load the cursor 57 m_hCursor = ::LoadCursor(NULL, m_dwSplitterStyle&SPS_VERTICAL?IDC_SIZEWE:IDC_SIZENS); 58 59 // just create the static(no title) 60 return (CStatic::Create("", (dwStaticStyle | SS_NOTIFY | WS_CHILD), rect, pParent, nID)); 61 } 62 63 void CSplitterControl::OnMouseMove(UINT nFlags, CPoint point) 64 { 65 if (m_bMouseIsDown) 66 { 67 // erase the old splitter 68 CWindowDC dc(NULL); 69 this->DrawSplitterLine(&dc); 70 71 // calc the new pos of the splitter 72 CPoint pt = point; 73 this->ClientToScreen(&pt); 74 75 CWnd* pParent = GetParent(); 76 ASSERT(pParent); 77 ASSERT(IsWindow(pParent->m_hWnd)); 78 pParent->ScreenToClient(&pt); 79 80 // split position limit 81 pt.x = (pt.x < m_iMinPos)?m_iMinPos:pt.x; 82 pt.y = (pt.y < m_iMinPos)?m_iMinPos:pt.y; 83 pt.x = (pt.x > m_iMaxPos)?m_iMaxPos:pt.x; 84 pt.y = (pt.y > m_iMaxPos)?m_iMaxPos:pt.y; 85 86 // save the current pos, this value will be used when the mouse up 87 pParent->ClientToScreen(&pt); 88 m_ptCurrent = pt; 89 90 // repaint the splitter 91 this->DrawSplitterLine(&dc); 92 } 93 94 CStatic::OnMouseMove(nFlags, point); 95 } 96 97 BOOL CSplitterControl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 98 { 99 // when the move is on the client of the splitter this event while be fired 100 // so just change the cursor 101 if (nHitTest == HTCLIENT) 102 { 103 (void)::SetCursor(m_hCursor); 104 return (TRUE); 105 } 106 107 return CStatic::OnSetCursor(pWnd, nHitTest, message); 108 } 109 110 void CSplitterControl::OnLButtonDown(UINT nFlags, CPoint point) 111 { 112 CStatic::OnLButtonDown(nFlags, point); 113 114 // get the max/min pos of the splitter first 115 this->GetMaxMinPos(this->m_iMaxPos, this->m_iMinPos); 116 117 // Record the mouse status 118 m_bMouseIsDown = TRUE; 119 120 SetCapture(); 121 122 123 // Get the move split start pos 124 CRect rcWnd; 125 GetWindowRect(rcWnd); 126 m_ptOrigin = m_ptCurrent = rcWnd.CenterPoint(); 127 128 // draw the splitter 129 CWindowDC dc(NULL); 130 this->DrawSplitterLine(&dc); 131 } 132 133 void CSplitterControl::OnLButtonUp(UINT nFlags, CPoint point) 134 { 135 if (m_bMouseIsDown) 136 { 137 // erase the old picture 138 this->ClientToScreen(&point); 139 CWindowDC dc(NULL); 140 this->DrawSplitterLine(&dc); 141 142 // move spliter control self to the new pos 143 m_bMouseIsDown = FALSE; 144 CWnd* pParent = GetParent(); 145 if (pParent && IsWindow(pParent->m_hWnd)) 146 { 147 CPoint pt = m_ptCurrent; 148 pParent->ScreenToClient(&pt); 149 this->MoveSelfWindowToPoint(pt); 150 151 } 152 153 // relayout all registerd windows 154 this->Relayout(); 155 156 // if need notify the parent 157 if (m_dwSplitterStyle & SPS_DELTA_NOTIFY) 158 { 159 if (pParent && IsWindow(pParent->m_hWnd)) 160 { 161 CPoint ptDelta = m_ptCurrent - m_ptOrigin; 162 SPC_NM_DELTA tNotify; 163 tNotify.hdr.hwndFrom = m_hWnd; 164 tNotify.hdr.idFrom = GetDlgCtrlID(); 165 tNotify.hdr.code = SPN_DELTA; 166 tNotify.lDelta = (m_dwSplitterStyle&SPS_VERTICAL)?ptDelta.x:ptDelta.y; 167 (void)pParent->SendMessage(WM_NOTIFY, tNotify.hdr.idFrom, (LPARAM)&tNotify); 168 } 169 } 170 } 171 172 CStatic::OnLButtonUp(nFlags, point); 173 ReleaseCapture(); 174 } 175 176 void CSplitterControl::DrawSplitterLine(CDC* pDC) 177 { 178 CPoint pt = this->m_ptCurrent; 179 COLORREF clrSplitter = this->m_clrSplitterColor; 180 181 182 int nRop = pDC->SetROP2(R2_NOTXORPEN); 183 184 CRect rcWnd; 185 GetWindowRect(rcWnd); 186 187 CPen pen(0, 1, clrSplitter); 188 CPen* pOldPen = pDC->SelectObject(&pen); 189 190 if (m_dwSplitterStyle&SPS_VERTICAL) 191 { 192 pDC->MoveTo(pt.x - m_lSplitterWidth, rcWnd.top); 193 pDC->LineTo(pt.x - m_lSplitterWidth, rcWnd.bottom); 194 195 pDC->MoveTo(pt.x + m_lSplitterWidth, rcWnd.top); 196 pDC->LineTo(pt.x + m_lSplitterWidth, rcWnd.bottom); 197 } 198 else // m_dwSplitterStyle == SPS_HORIZONTAL 199 { 200 pDC->MoveTo(rcWnd.left, pt.y - m_lSplitterWidth); 201 pDC->LineTo(rcWnd.right, pt.y - m_lSplitterWidth); 202 203 pDC->MoveTo(rcWnd.left, pt.y + m_lSplitterWidth); 204 pDC->LineTo(rcWnd.right, pt.y + m_lSplitterWidth); 205 } 206 207 pDC->SetROP2(nRop); 208 pDC->SelectObject(pOldPen); 209 } 210 211 void CSplitterControl::MoveSelfWindowToPoint(const CPoint& pt) 212 { 213 CWnd* pParent = GetParent(); 214 if (!pParent || !::IsWindow(pParent->m_hWnd)) 215 { 216 return; 217 } 218 219 CRect rc; 220 GetWindowRect(rc); 221 pParent->ScreenToClient(rc); 222 223 // calc the new rect 224 if (m_dwSplitterStyle&SPS_VERTICAL) 225 { 226 rc.OffsetRect((pt.x - (rc.left + rc.right) / 2), 0); 227 } 228 else 229 { 230 rc.OffsetRect(0, (pt.y - (rc.top + rc.bottom) / 2)); 231 } 232 233 MoveWindow(rc); 234 } 235 236 void CSplitterControl::GetMaxMinPos(int& iMax, int& iMin) 237 { 238 CWnd* pParent = GetParent(); 239 ASSERT(pParent); 240 ASSERT(IsWindow(pParent->m_hWnd)); 241 242 CRect rcParent; 243 pParent->GetClientRect(rcParent); 244 245 // try to get the max/min pos limit from parent window 246 SPC_NM_MAXMINPOS nmMinmax; 247 nmMinmax.hdr.hwndFrom = m_hWnd; 248 nmMinmax.hdr.idFrom = GetDlgCtrlID(); 249 nmMinmax.hdr.code = SPN_MAXMINPOS; 250 nmMinmax.lMax = (m_dwSplitterStyle&SPS_VERTICAL)?rcParent.right:rcParent.bottom; 251 nmMinmax.lMin = (m_dwSplitterStyle&SPS_VERTICAL)?rcParent.left:rcParent.top; 252 (void)pParent->SendMessage(WM_NOTIFY, nmMinmax.hdr.idFrom, (LPARAM)&nmMinmax); 253 254 // return 255 iMax = nmMinmax.lMax; 256 iMin = nmMinmax.lMin; 257 } 258 259 BOOL CSplitterControl::RegisterLinkedWindow(DWORD dwLinkSide, CWnd* pWnd) 260 { 261 // check parameters 262 if (NULL == pWnd) 263 { 264 return (FALSE); 265 } 266 267 // check parameters 268 if ((SPLS_LINKED_LEFT != dwLinkSide) && (SPLS_LINKED_RIGHT != dwLinkSide)) 269 { 270 return (FALSE); 271 } 272 273 // make sure the spliiter and pWnd have same parent 274 if (pWnd->GetParent()->m_hWnd != this->GetParent()->m_hWnd) 275 { 276 return (FALSE); 277 } 278 279 // save it 280 POSITION pos = m_listLinkedWnds[dwLinkSide].AddTail(pWnd); 281 if (NULL == pos) 282 { 283 return (FALSE); 284 } 285 286 return (TRUE); 287 } 288 289 void CSplitterControl::Relayout() 290 { 291 CWnd* pParent = GetParent(); 292 ASSERT(pParent); 293 ASSERT(IsWindow(pParent->m_hWnd)); 294 295 CRect rcSelf; 296 this->GetWindowRect(rcSelf); 297 298 // relayout all registered window 299 for (int i = 0; i < 2; i++) 300 { 301 for (POSITION pos = this->m_listLinkedWnds[i].GetHeadPosition(); NULL != pos;) 302 { 303 // get the window object 304 CWnd* pWnd = this->m_listLinkedWnds[i].GetNext(pos); 305 if (NULL == pWnd) 306 { 307 continue; 308 } 309 310 // calc the new pos(the code is not very good) 311 CRect rcLinked; 312 pWnd->GetWindowRect(rcLinked); 313 if (SPS_VERTICAL&m_dwSplitterStyle) 314 { 315 if (SPLS_LINKED_LEFT == i) 316 { 317 rcLinked.right = rcSelf.left; 318 } 319 else 320 { 321 rcLinked.left = rcSelf.right; 322 } 323 } 324 else 325 { 326 if (SPLS_LINKED_LEFT == i) 327 { 328 rcLinked.bottom = rcSelf.top; 329 } 330 else 331 { 332 rcLinked.top = rcSelf.bottom; 333 } 334 } 335 336 // move it to new pos and then update 337 pParent->ScreenToClient(rcLinked); 338 pWnd->MoveWindow(rcLinked, TRUE); 339 pWnd->Invalidate(); 340 } 341 } 342 } 343 344 void CSplitterControl::ChangePos(CWnd* pWnd, DWORD dwLinkedSide, LONG lDelta) 345 { 346 if (NULL == pWnd) 347 { 348 return; 349 } 350 351 if (FALSE == ::IsWindow(pWnd->GetSafeHwnd())) 352 { 353 return; 354 } 355 356 CWnd* pParent = pWnd->GetParent(); 357 if (NULL == pParent) 358 { 359 return; 360 } 361 362 if (FALSE == ::IsWindow(pParent->m_hWnd)) 363 { 364 return; 365 } 366 367 CRect rcWnd; 368 pWnd->GetWindowRect(rcWnd); 369 pParent->ScreenToClient(rcWnd); 370 371 if (SPS_VERTICAL & m_dwSplitterStyle) 372 { 373 if (SPLS_LINKED_LEFT & dwLinkedSide) // Same as SPLS_LINKED_UP 374 { 375 rcWnd.right += lDelta; 376 } 377 else 378 { 379 rcWnd.left += lDelta; 380 } 381 } 382 else 383 { 384 if (SPLS_LINKED_UP & dwLinkedSide) // Same as SPLS_LINKED_LEFT 385 { 386 rcWnd.bottom += lDelta; 387 } 388 else 389 { 390 rcWnd.top += lDelta; 391 } 392 } 393 394 pWnd->MoveWindow(rcWnd, TRUE); 395 }