• 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 }
  • 相关阅读:
    discuz X3.2 自定义系统广告详解
    windows平台myeclipse+PDT+apache+xdebug调试php
    南浮的IT民工
    linux实践——编译安装两个apache
    如何使maven+jetty运行时不锁定js和css[转]
    linux实践——ubuntu搭建 svn 服务
    测试代码插件(插入代码块)
    FTP 文件接口按天批处理脚本实例
    7月份工作小结
    报表开发过程
  • 原文地址:https://www.cnblogs.com/wuyuan2011woaini/p/10477638.html
Copyright © 2020-2023  润新知