当前位置: 首页 > news >正文

Splitter Control for Dialog

原文链接地址: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 or SPS_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) or height(SPS_HORIZONTAL) of the splitter depend on the width of rect when you call Create 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 }

 

转载于:https://www.cnblogs.com/wuyuan2011woaini/p/10477638.html

相关文章:

  • Windows消息大全
  • 开启 J2EE(一)—‘全明星队伍’
  • Python之位移操作符所带来的困惑
  • 第六章 设计基于锁的并发数据结构
  • Silverlight 3.0 中的 Local Connection
  • Java 获取目录以及子目录下的sql文件
  • IE中iframe标签显示在DIV之上的问题解决方案
  • SharePoint : 使用SPQuery对象时要注意的事项
  • Linux 上安装JDK
  • JUnit-4.13使用报java.lang.NoClassDefFoundError: org/hamcrest/SelfDescribing错误
  • vi和vim的基本介绍
  • 流程理解篇-测试篇
  • 简单贪心题(看最多的电视节目)
  • TestDriven.NET 2.0单元测试
  • Spring Boot 2.x (十二):Swagger2的正确玩儿法
  • Github访问慢解决办法
  • JavaScript设计模式系列一:工厂模式
  • JAVA多线程机制解析-volatilesynchronized
  • laravel 用artisan创建自己的模板
  • maya建模与骨骼动画快速实现人工鱼
  • Odoo domain写法及运用
  • python大佬养成计划----difflib模块
  • SwizzleMethod 黑魔法
  • 前端相关框架总和
  • 浅谈web中前端模板引擎的使用
  • 线性表及其算法(java实现)
  • 【干货分享】dos命令大全
  • puppet连载22:define用法
  • Python 之网络式编程
  • 新年再起“裁员潮”,“钢铁侠”马斯克要一举裁掉SpaceX 600余名员工 ...
  • # Swust 12th acm 邀请赛# [ A ] A+B problem [题解]
  • #includecmath
  • $.ajax()方法详解
  • (4)事件处理——(7)简单事件(Simple events)
  • (day 2)JavaScript学习笔记(基础之变量、常量和注释)
  • (非本人原创)我们工作到底是为了什么?​——HP大中华区总裁孙振耀退休感言(r4笔记第60天)...
  • (十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置
  • (转)Google的Objective-C编码规范
  • (转)四层和七层负载均衡的区别
  • (最全解法)输入一个整数,输出该数二进制表示中1的个数。
  • .md即markdown文件的基本常用编写语法
  • .NET 表达式计算:Expression Evaluator
  • .NET/C# 在代码中测量代码执行耗时的建议(比较系统性能计数器和系统时间)
  • .net6 webapi log4net完整配置使用流程
  • .NetCore Flurl.Http 升级到4.0后 https 无法建立SSL连接
  • .skip() 和 .only() 的使用
  • .vimrc php,修改home目录下的.vimrc文件,vim配置php高亮显示
  • /var/log/cvslog 太大
  • @javax.ws.rs Webservice注解
  • [ 数据结构 - C++] AVL树原理及实现
  • [1204 寻找子串位置] 解题报告
  • [2013AAA]On a fractional nonlinear hyperbolic equation arising from relative theory
  • [Contest20180313]灵大会议
  • [FROM COM张]如何解决Nios II SBTE中出现的undefined reference to `xxx'警告
  • [Git 1]基本操作与协同开发