VC + + to achieve arbitrary window segmentation
Turn from: http://www.1shang.com/tech/tech1/techList7909.htm
First, about the CSplitterWnd class
When we use tools such as CuteFTP or netant, we are generally attracted by its complicated interface, in which the window is divided into several regions, and the window is arbitrarily segmented. So how do we create a similar interface of our own, and also implement arbitrary segmentation of the window? In VC6.0 This requires the use of the CSplitterWnd class. CSplitterWnd looks like a special frame window, with each window filled with the same or different views. When the window is split, the user can use the mouse to move the Shard bar to adjust the relative size of the window. Although VC6.0 supports creating split windows from AppWizard, the automatically added split bar is always not satisfying, so we are familiar with this class by adding code manually.
The CSplitterWnd constructor consists mainly of the following three.
BOOL Create (cwnd* pparentwnd,int nmaxrows,int nmaxcols,size sizemin,ccreatecontext* Pcontext,dword);
Function Description: This function is used to create a dynamic segmentation window.
Parameter meaning: pParentWnd the parent frame window of the segmentation window.
Nmaxrows,nmaxcols is the maximum number of columns and rows created.
Sizemin is the actual size of the pane.
PContext is passed to the parent window in most cases.
Nid is the ID number of the Word window.
BOOL createstatic (cwnd* pparentwnd,int nrows,int ncols,dword dwstyle,uint)
Feature Description: Used to create a segmentation window.
The parameter meaning is ditto.
BOOL createview (int row,int col,cruntimeclass* pviewclass,size sizeinit,ccreatecontext* pContext);
Function Description: Fills the view for the grid of the statically split window. You must first create the split window when you associate the view with the split window.
Parameter meaning: ditto.
You can see from the CSplitterWnd source that a protection function Createcommon is invoked in a function, whether by using dynamic create creation or by using static creation createstatic. From the key code in the Createcommon function below, you can see that the essence of creating CSplitterWnd is to create a series of MDI child windows.
DWORD Dwcreatestyle = dwstyle & ~ (ws_hscroll| Ws_vscroll);
if (AFXDATA.BWIN4)
Dwcreatestyle &= ~ws_border; Create with the same wnd-class as Mdi-frame (no erase Bkgnd)
if (! CreateEx (0, _afxwndmdiframe, NULL, Dwcreatestyle, 0, 0, 0, 0,pparentwnd->m_hwnd, (hmenu) NID, NULL)
return FALSE; Create invisible
Second, create nested split window
2.1 Creating a dynamic split window
The dynamic split window uses the Create method. The following code creates a 2x2 pane.
M_wndsplitter.create (This,2,2,csize (100,100), pContext);
However, the number of panes for a dynamically created split window cannot exceed 2x2, and for all panes, you must share the same view and have more restrictions, so we do not focus on dynamic creation. Our main focus is on the creation of a static split window.
2.2 Creating a static split window
Static-created code is much simpler than dynamic creation, and you can create up to 16x16 panes. Different panes we can use CreateView to populate different views.
Here we will create a cuteftp window split.
To create a step:
▲ before creating, we must first generate a single document CUTEFTP with AppWizard, and the generated view class is Ccuteftpview. A derived class cview2,cview3 CView4 that adds three visual classes or inherits from the view class.
▲ Add Members:
In Cmainfrm.h we will add the following code:
CSplitterWnd WndSplitter1;
CSplitterWnd WndSplitter2;
▲ overloaded cmainframe::oncreateclient () function:
BOOL cmainframe::oncreateclient (lpcreatestruct, ccreatecontext* pContext)
{//Create a static column window, divided into three rows
if (m_wndsplitter1.createstatic (this,3,1) ==null)
return FALSE;
Connect Ccuteftpview to 0 rows, 0 column panes
M_wndsplitter1.createview (0,0,runtime_class (Ccuteftpview), CSize (100,100), pContext);
M_wndsplitter1.createview (2,0,runtime_class (CVIEW4), CSize (100,100), pContext); Connect CView4 to 0 rows 2 columns
if (M_wndsplitter2.createstatic (&m_wndsplitter,1,2,ws_child| Ws_visible, M_wndsplitter.idfromrowcol (1, 0)) ==null)
return FALSE; Divide row 1th 0 into 1 rows and 2 columns
Connect the CView2 class to the 0 row 0 columns of the second column object
M_wndsplitter2.createview (0,0,runtime_class (CVIEW2), CSize (400,300), pContext); Connect the CVIEW3 class to the 0 row 1 columns of the second column object
M_wndsplitter2.createview (0,1,runtime_class (CVIEW3), CSize (400,300), pContext);
return TRUE;
}
2.3 Implement the communication of each partition area
Communication between a document-attached view
The Ccuteftpview generated by AppWizard is linked to the document, and we also have CVIEW2 connected to the document, so we need to modify the Ccuteftpapp InitInstance () function, we will add the following section.
AddDocTemplate (New CMultiDocTemplate (Idr_view2type,
Runtime_class (Cmaindoc),
Runtime_class (CMDIChildWnd),
Runtime_class (CVIEW2)));
We are now going to implement the communication between Ccuteftpview and CView2. Because a view class that is connected to a document class is not safe to communicate with the rest of the view class except for the document class. So we can only have them communicate with the document class. In the document we set the corresponding pointers for each view to be obtained. We overload the Ccuteftpview::onopendocument () function;
ccuteftpview* Pcuteftpview;
Cview2* PView2;
POSITION POS;
cview* PView;
while (Pos!=null)
{
Pview=getnextview (POS);
if (Pview->iskindof (Runtime_class (Ccuteftpview)) ==null)
pcuteftpview= (ccuteftpview*) PView;
Else (Pview->iskindof (Runtime_class (Ccuteftpview)) ==null)
Pview2= (cview2*) PView;
}
So we get a pointer to all the views that are connected to it in the document class.
If you need to call a method doit () in CView2 in Ccuteftpview, the code is as follows:
ccuteftpdoc* pdoc=getdocument ();
cview2* pview2=pdoc->pview3;
Pview3.doit ();
No communication between document view and document Associated View
CVIEW3 and CVIEW4 are not associated with the document. We now implement CVIEW3 and CVIEW2 communication. As mentioned earlier, CVIEW2 can only communicate securely with Ccuteftpdoc, so CVIEW3 must use the document classes if they need to communicate with CVIEW2. So the key to the program is how to get a pointer to a document in CVIEW3. There is no such class member in the view class that can be used to directly access the document class. But we know that in the main window class mainframe we can get pointers to any of the program's window classes. So as soon as we get the pointer to the main window of the program, we can solve the problem. The code implementation accesses the doit () method in CView2 in CVIEW3.
The code in CVIEW3 is as follows:
cmainframe* mainframe= (cmainframe*) this->getparent ()->getparent ();
ccuteftpdoc* doc= (ccuteftpdoc*) mainframe->getactivedocument ();
if (doc!=null) Doc->doit ();
The corresponding handler function in the Ccuteftpdoc doit () code is as follows:
Cview2* PView2;
POSITION POS;
cview* PView;
while (Pos!=null)
{
Pview=getnextview (POS);
if (Pview->iskindof (Runtime_class (CVIEW2)) ==null)
Pview2= (cview2*) PView;
}
Pview2->doit ();
No communication between document-associated views
CVIEW3 and CView4 are not connected to the document, how to achieve communication between them. As we said above, because we have access to any view in the main frame, our primary task is to get pointers to the main frame in the program. Access the method doit () in CView4 in CVIEW3.
cmainframe* mainframe= (cmainframe*) this->getparent ()->getparent ();
cview4* view4= (cview4*) Mainframe->m_wndsplitter1.getpane (2,0);
View4->doit ();
By now we have implemented the framework of the CuteFTP main window and the ability to communicate with each other. Similarly, we can implement some other popular interfaces such as Netants,foxmail segmentation.
Three, about the dialog box segmentation
Until now, only a document/view based program can use CSplitterWnd, whereas a dialog based application does not support CSplitterWnd, but if we overload some virtual methods in an inherited class, we can also make CSplitterWnd available in a dialog box program. From the MFC source program WinSplit.cpp can be seen, in order to obtain the parent window of the local program has called the virtual method GetParentFrame (), so if used in the dialog box, we must change it to GetParent (); So we'll csplitterwnd the following several methods.
virtual void starttracking (int ht);
Virtual cwnd* Getactivepane (int* prow = null, int* pcol = null);
virtual void Setactivepane (int row, int col, cwnd* pwnd = NULL);
Virtual BOOL OnCommand (WPARAM WPARAM, LPARAM LPARAM);
Virtual BOOL onnotify (WPARAM WPARAM, LPARAM LPARAM, lresult* pResult);
Virtual BOOL onwndmsg (UINT message, WPARAM WPARAM, LPARAM LPARAM, lresult* pResult);
The implementation is as follows, I will give the main part of the original code and the modified code for comparison.
Add the following enumeration type to the CPP file.
Enum Hittestvalue
{
Nohit = 0,//indicates that no object is selected
Vsplitterbox = 1,
Hsplitterbox = 2,
Bothsplitterbox = 3,
VSPLITTERBAR1 = 101,//Horizontal split bar representing all directions
VSPLITTERBAR15 = 115,
HSplitterBar1 = 201,//representing the vertical direction of each split bar
HSplitterBar15 = 215,
SplitterIntersection1 = 301,//represents each intersection point
splitterIntersection225 = 525
};
cwnd* Cxsplitterwnd::getactivepane (int* prow, int* pcol)
{
Assert_valid (this);
Get the current window that gets focus
The following comment Bold is the main part of the original code.
cwnd* PView = NULL;
cframewnd* Pframewnd = GetParentFrame ();
Assert_valid (Pframewnd);
PView = Pframewnd->getactiveview ();
if (PView = NULL)
PView = GetFocus ();
cwnd* PView = GetFocus ();
if (PView!= NULL &&!) Ischildpane (PView, prow, Pcol))
PView = NULL;
return pView;
}
void Cxsplitterwnd::setactivepane (int row, int col, cwnd* pwnd)
{
cwnd* Ppane = pwnd = NULL? GetPane (Row, col): pwnd;
The following comments bold are the main parts of the original code.
framewnd* Pframewnd = GetParentFrame ();
Assert_valid (Pframewnd);
Pframewnd->setactiveview ((cview*) ppane);
Ppane->setfocus ()//Modified statement
}
void cxsplitterwnd::starttracking (int ht)
{
Assert_valid (this);
if (ht = = Nohit)
Return
Gethitrect would restrict ' m_rectlimit ' as appropriate
Getinsiderect (M_rectlimit);
if (HT >= splitterIntersection1 && HT <= splitterIntersection225)
{
Split two directions (two tracking rectangles)
int row = (ht-splitterintersection1)/15;
int col = (ht-splitterintersection1)% 15;
Gethitrect (row + vSplitterBar1, m_recttracker);
int ytrackoffset = M_PTTRACKOFFSET.Y;
M_btracking2 = TRUE;
Gethitrect (col + hSplitterBar1, m_recttracker2);
M_pttrackoffset.y = Ytrackoffset;
}
else if (ht = = Bothsplitterbox)
{
Hit on splitter boxes (for keyboard)
Gethitrect (Vsplitterbox, M_recttracker);
int ytrackoffset = M_PTTRACKOFFSET.Y;
M_btracking2 = TRUE;
Gethitrect (Hsplitterbox, M_recttracker2);
M_pttrackoffset.y = Ytrackoffset; Center it
M_recttracker.offsetrect (0, M_rectlimit.height ()/2); M_recttracker2.offsetrect (M_rectlimit.width ()/2, 0);
}
Else
{
Only hit one bar
Gethitrect (HT, m_recttracker);
}
//The comments below will be deleted from the program. &NBSP
//cview* PView = (cview*) getactivepane ()
//if (PView!= NULL && pview->iskindof (RUNTIME _class (CView))
//{
//Assert_valid (PView);
//cframewnd* Pframewnd = GetParentFrame (); & nbsp
//assert_valid (Pframewnd);
//pview->onactivateframe (wa_inactive, Pframewnd);
//}
//Steal focus and capture
SetCapture ();
SetFocus ();
//Make sure no updates are pending
RedrawWindow (null, NULL, Rdw_allchildren | Rdw_updatenow);
//Set tracking state and appropriate cursor
m_btracking = TRUE;
Oninverttracker (M_recttracker);
if (m_btracking2)
Oninverttracker (m_ RECTTRACKER2);
M_httrack = ht;
Setsplitcursor (HT);
}
BOOL Cxsplitterwnd::oncommand (WPARAM WPARAM, LPARAM LPARAM)
{
if CWnd::OnCommand (WPARAM, LParam))
return true;
///below bold is the original program's statement
//return getparentframe ()-> SendMessage (Wm_command, WParam, LParam);
return GetParent ()->sendmessage (Wm_command, WParam, LParam); &NBSP
}
BOOL cxsplitterwnd::onnotify (WPARAM WPARAM, LPARAM LPARAM, lresult* presult )
{
if ( Cwnd::onnotify (WParam, LParam, PResult))
return true;
//The following bold is the source program's statement
//* PResult = GetParentFrame ()->sendmessage (wm_notify, WParam, LParam);
*presult = GetParent ()->sendmessage (wm_notify, WParam, LParam);
return TRUE;
}
BOOL cxsplitterwnd::onwndmsg (UINT message, WPARAM WPARAM, LPARAM LPARAM, lresult* pResult)
{
The code line below are necessary if using Cxsplitterwnd in a regular DLL
Afx_manage_state (AfxGetStaticModuleState ());
return cwnd::onwndmsg (Message, WParam, LParam, PResult);
}
So we can use the Cxsplitterwnd class in the dialog box.
Iv. expansion of the CSplitterWnd
CSplitterWnd expansion is a lot of topics, and we can extend CSplitterWnd by overwriting the original method or by adding new methods. Let us cite only two examples here.
4.1 Lock Slitting Strips
When a user creates a good split window, it is sometimes not desirable to resize the window by dragging the Shard bar. Then you must lock the Shard bar. The easiest way to lock a shard is to not allow CSplitterWnd to process wm_lbuttondown,wm_mousemove,wm_setcursor messages, but to give them to the CWnd window for processing, shielding them from the message. Take the WM_LBUTTONDOWN process. Modified to read as follows:
void Cxxsplitterwnd::onlbuttondown (UINT nflags,cpoint point)
{
Cwnd::onlbuttondown (Nflags,point);
}
The rest of the processing methods are similar.
4.2 Customization of slitting strips
The Shard Bar generated by window is always fixed, without any changes, and we can use some software, such as ACDSee, to find that their shard is not the same as the automatic generation of the segmentation bar. So how to customize your own segmentation bar. Ondrawsplitter and Oninverttracker can be achieved by means of the virtual method of overloading CSplitterWnd. The following code generates the effect that the edge color of the split window is red and the color of the stripe is green. The code is as follows:
void Csplitterwndex::ondrawsplitter (CDC *pdc, Esplittype ntype, const CRect &rectarg)
{
if (pdc==null)
{
RedrawWindow (rectarg,null,rdw_invalidate| Rdw_nochildren);
Return
}
Assert_valid (PDC);
CRect Rc=rectarg;
Switch (ntype)
{
Case Splitborder:
Redraw the edge of the split window to make it red
Pdc->draw3drect (Rc,rgb (255,0,0), RGB (255,0,0));
Rc. Inflaterect (-cx_border,-cy_border);
Pdc->draw3drect (Rc,rgb (255,0,0), RGB (255,0,0));
Return
Case Splitbox:
Pdc->draw3drect (Rc,rgb (0,0,0), RGB (0,0,0));
Rc. Inflaterect (-cx_border,-cy_border);
Pdc->draw3drect (Rc,rgb (0,0,0), RGB (0,0,0));
Rc. Inflaterect (-cx_border,-cy_border);
Pdc->fillsolidrect (Rc,rgb (0,0,0));
Pdc->draw3drect (Rc,rgb (0,0,0), RGB (0,0,0));
Return
Case Splitbar:
Repaint the split bar to make it green
Pdc->fillsolidrect (Rc,rgb (255,255,255));
Rc. Inflaterect ( -5,-5);
Pdc->draw3drect (Rc,rgb (255,0,0), RGB (255,0,0));
Return
Default
ASSERT (FALSE);
}
Pdc->fillsolidrect (Rc,rgb (0,0,255));
}
void Csplitterwndex::oninverttracker (CRect &rect)
{
Assert_valid (this);
ASSERT (!rect. Isrectempty ());
ASSERT ((GetStyle () &ws_clipchildren) ==0);
CRect Rc=rect;
Rc. Inflaterect (2,2);
cdc* Pdc=getdc ();
cbrush* Pbrush=cdc::gethalftonebrush ();
Hbrush Holdbrush=null;
if (pbrush!=null) holdbrush= (hbrush) SelectObject (pdc->m_hdc,pbrush->m_hobject);
Pdc->patblt (RC.LEFT,RC.TOP,RC. Width (), RC. Height (), blackness);
if (holdbrush!=null)
SelectObject (Pdc->m_hdc,holdbrush);
ReleaseDC (PDC);
}
Similarly, we can generate a split window with our own personality as long as we inherit some of the other virtual methods in CSplitterWnd.