??xml version="1.0" encoding="utf-8" standalone="yes"?>
我们在用CuteFtp或者NetAnt{工L(fng)时候,一般都?x)被其复杂的界面所吸引Q在q些界面中窗口被分割q的区域Q真正做C(jin)H口的Q意分剌Ӏ?那么我们自己如何创徏cM的界面,也实现窗口的L的分割呢 Q在VC6.0中这需要用到CSplitterWndcRCSplitterWnd看上d是一U特D的框架H口Q每个窗口都被相同的或者不同的视图所填充。当H口被切分后用户可以使用鼠标Ud切分条来调整H口的相对尺寸。虽然VC6.0支持从AppWizard中创建分割窗口,但是自动加入的分割条L不能让我们满意,因此我们q是通过手工增加代码来熟(zhn)这个类?
CSplitterWnd的构造函C要包括下面三个?
BOOL Create(CWnd* pParentWnd,int nMaxRows,int nMaxCols,SIZE sizeMin,CCreateContext* pContext,DWORD dwStyle,UINT nID);功能描述Q该函数用来创徏动态切分窗口?参数含义QpParentWnd 切分H口的父框架H口?nMaxRows,nMaxCols是创建的最大的列数和行数?sizeMin是窗格的现实大小?pContext 大多数情况下传给父窗口?nID是字H口的ID?
BOOL CreateStatic(CWnd* pParentWnd,int nRows,int nCols,DWORD dwStyle,UINT nID)功能描述Q用来创建切分窗口?参数含义同上?
BOOL CreateView (int row,int col,CruntimeClass* pViewClass,SIZE sizeinit,CcreateContext* pContext);功能描述Qؓ(f)?rn)态切分的H口的网格填充视图。在视图于切分H口联系在一L(fng)时候必 d切分窗口创建好?
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
m_wndSplitter.Create(this,2,2,CSize(100,100),pContext);
CCuteFTPView | |
CView2 | CView3 |
CView4 | |
创徏步骤Q?
?在创Z前我们必d用AppWizard生成单文档CuteFTPQ生成的视类?CCuteFTPView.同时在增加三个视cL者从视类l承而来的派生类CView2,CView3 CView4.
?增加成员Q?/b>
在Cmainfrm.h中我们将增加下面的代码:(x)
CSplitterWnd wndSplitter1;
CSplitterWnd wndSplitter2;?重蝲CMainFrame::OnCreateClient()函数Q?/b>BOOL CMainFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/, CCreateContext* pContext)
{ //创徏一个静(rn)态分栏窗口,分ؓ(f)三行一?
if(m_wndSplitter1.CreateStatic(this,3,1)==NULL)
return FALSE;
//CCuteFTPViewq接??列窗g
m_wndSplitter1.CreateView(0,0,RUNTIME_CLASS(CCuteFTPView),CSize(100,100), pContext);
m_wndSplitter1.CreateView(2,0,RUNTIME_CLASS(CView4),CSize(100,100),pContext);
//CView4q接???
if(m_wndSplitter2.CreateStatic(&m_wndSplitter,1,2,WS_CHILD|WS_VISIBLE,
m_wndSplitter.IdFromRowCol(1, 0))==NULL)
return FALSE; //第1?列再分开1??
//CView2c连接到W二个分栏对象的0??
m_wndSplitter2.CreateView(0,0,RUNTIME_CLASS(CView2),CSize(400,300),pContext);
//CView3c连接到W二个分栏对象的0??
m_wndSplitter2.CreateView(0,1,RUNTIME_CLASS(CView3),CSize(400,300),pContext);
return TRUE;
} 2.3实现各个分割区域的通信AddDocTemplate (new CMultiDocTemplate(IDR_VIEW2TYPE,
RUNTIME_CLASS(CMainDoc),
RUNTIME_CLASS(CMDIChildWnd),
RUNTIME_CLASS(CView2))); 我们现在来实现CCuteFTPView与CView2之间的通信。由于跟文cȝq的视图c?是不能安全的与除文cM外的其余的视囄通信的。因此我们只能让他们都与文档 c通信。在文中我们设|相应的指针以用来获的各个视图。我们重?CCuteFTPView::OnOpenDocument()函数Q?CCuteFTPView* pCuteFTPView;q样我们在文类中就L(fng)?jin)跟它相q的所有的视图的指针?br />如果需要在 CCuteFTPView中调用CView2中的一个方法DoIt()则代码如下:(x)
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; }
CCuteFTPDoc* pDoc=GetDocument();
CView2* pView2=pDoc->pView3;
pView3.DoIt();
CMainFrame* MainFrame=(CMainFrame*)this->GetParent()->GetParent();
CCuteFTPDoc* Doc=(CCuteFTPDoc*)MainFrame->GetActiveDocument();
if(Doc!=NULL) Doc->DoIt();
CCuteFTPDoc中的相应的处理函数DoIt()代码如下Q?
CView2* pView2;
POSITION pos;
CView* pView;
while(pos!=NULL)
{
pView=GetNextView(pos);
if(pView->IsKindOf(RUNTIME_CLASS(CView2))==NULL)
pView2=(CView2*)pView;
}
pView2->DoIt(); ?b>无文关联视图之间的通信CMainFrame* MainFrame=(CMainFrame*)this->GetParent()->GetParent();
CView4* View4=(CView4*)MainFrame->m_wndSplitter1.GetPane(2,0);
View4->DoIt(); 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 );具体实现如下Q实C我将l出原有代码的主要部分以?qing)修改后的代码以作对比?br />在cpp文g中加入下面的枚Dcd?
enum HitTestValue
{
noHit = 0,//表示没有选中M对象
vSplitterBox = 1,
hSplitterBox = 2,
bothSplitterBox = 3,
vSplitterBar1 = 101,//代表各个方向的水q_割条
vSplitterBar15 = 115,
hSplitterBar1 = 201,//代表垂直方向的各个分割条
hSplitterBar15 = 215,
splitterIntersection1 = 301,//代表各个交叉?
splitterIntersection225 = 525
};
CWnd* CxSplitterWnd::GetActivePane(int* pRow, int* pCol)
{
ASSERT_VALID(this);
//获得当前的获得焦点的H口
//下面注释_体的是原有的代码的主要部分?
// 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;
//下面加注释粗体的是原有代码的主要部分?
//FrameWnd* pFrameWnd = GetParentFrame();
//ASSERT_VALID(pFrameWnd);
//pFrameWnd->SetActiveView((CView*)pPane);
pPane->SetFocus();//修改后的语句
}
void CxSplitterWnd::StartTracking(int ht)
{
ASSERT_VALID(this);
if (ht == noHit)
return;
// GetHitRect will 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);
}
//下面加注释的从E序中删厅R?
//CView* pView = (CView*)GetActivePane();
//if (pView != NULL && pView->IsKindOf(RUNTIME_CLASS(CView)))
//{
// ASSERT_VALID(pView);
// CFrameWnd* pFrameWnd = GetParentFrame();
//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;
//下面_体的是原程序的语句
//return GetParentFrame()->SendMessage(WM_COMMAND, wParam, lParam);
return GetParent()->SendMessage(WM_COMMAND, wParam, lParam);
}
BOOL CxSplitterWnd::OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult )
{
if (CWnd::OnNotify(wParam, lParam, pResult))
return TRUE;
//下面_体的是源程序的语句
//*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 is necessary if using CxSplitterWnd
in a regular dll
// AFX_MANAGE_STATE(AfxGetStaticModuleState());
return CWnd::OnWndMsg(message, wParam, lParam, pResult);
} q样我们可以在对话框中使用CxSplitterWndcM(jin)?void CXXSplitterWnd::OnLButtonDown(UINT nFlags,CPoint point)其余的处理方法类伹{?
{ CWnd::OnLButtonDown(nFlags,point); }
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:
//重画分割H口边界,使之为红?
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:
//重画分割条,使之为绿?
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);
} (tng) (tng) (tng) (tng) ASSERT()被测试它的参敎ͼ若参Cؓ(f)0Q则中断执行q打CD说明消息。在 Release 版本的程序中它不起Q何作用。ASSERT()使用的时候必M证参数表辑ּ中不能有函数调用Q译者注QASSERT()宏在 Release 版本中不对表辑ּ求|(j)Q因此对于Q何有函数调用的参数表辑ּQ应该用宏 VERIFY()Q以保证表达式中的函数调用在 Release 版本中会(x)被正求倹{?/span>
断言(assertion)用带断言信息(E序, 模块, assertion?的对话框执行. 对话框有3个按? "Break", "Repeat" ("Debug"), and "Continue" ("Ignore"). "Break" l束E序, "Continue" 忽略断言, 最有用的是"Repeat"按钮. 按下它在断言的地Ҏ(gu)开源代码编辑器. 在这里你可以试所有的变量值ƈ明白哪里Z(jin)问题?/span>
例如QASSERT(pPointer);ASSERT(n>0 && n<100);ASSERT(0);
ASSERT在执行简单验证时很有用,但对?span>C++对象Q特别是由CObjectz的对象,则有更好的方法ASSERT_VALID来实现类似操作。作Z般规则,我们应在开始用每一个对象之前检查数据讹?
(tng)
ASSERT_VALID宏得对CObject的派生类实现该操作非常简单?/font>
例如QASSERT_VALID(this);ASSERT_VALID(pView);
(tng)
VERIFY()
(tng) (tng) (tng) VERIFY()?span> ASSERT()很相|区别在于?Release 版本中它仍然有效Q译者注Q原作者在q里没有讲清楚,VERIFY()不会(x)打印说明Q只是会(x)对参数表辑ּ求|(j)?
(tng)
TRACE()
(tng) (tng) (tng) (tng) (tng)TRACE()基本上就是函?printf()的一个复制品Q唯一的区别是它把l果输出到调试窗口。在 Release 版本中,它也是无效的。一般是用TRACE0(),TRACE1(),TRACE2()…而不用TRACE()?/font>
?tng)这三个宏?span> Release 版本中都不会(x)产生M实质性的影响Q它们是否v作用取决于是否定义了(jin)预定义了(jin)?_DEBUG。这是对 Microsoft Visual C++ 而言Q在其它的编译器中可能其它不同的宏?/span>
(tng)
常用用法Q?/font>
(tng)
Z(jin)控制传进的指?
void SomeFun(SomeType* pPointer)
{
(tng) (tng)ASSERT(pPointer);
(tng)//some instructions.
}
(tng)
你可以在"switch" ?"if"操作中捕获奇怪的?br />例如:
switch(nRGBColors){
(tng) case nRed: (tng) (tng) {//some instructions.} break;
(tng) case nGreen: {//some instructions.} break;
(tng) case nBlue: (tng) {//some instructions.} break;
(tng) default: ASSERT(0); (tng) (tng) (tng) // we should have never come here!
}
if(nWaterTemp >=0 && nWaterTemp < 50){
(tng) //some instructions.
}
else if(nWaterTemp >= 50 && nWaterTemp <= 100){
(tng) //some instructions.
}
else{
(tng) ASSERT(0); (tng) (tng) (tng) // we should have never come here!
}
(tng)
对值的断言:
ASSERT(nSomeValue >= MinValue and nSomeValue <= MaxValue);
ASSERT(nOtherValue != 0);
(tng)
可爱?ASSERT 错误
ASSERT( m_MyWnd.Create() );
? q是一个可怕的错误! E序在调试版中正常工? 在发行版中不工作. C: q是一个在发行版中被U除的宏. 以这U方法你的窗口将永远不会(x)被创? 如果你用 MFC, q样?
VERIFY( m_MyWnd.Create() );
它在调试版中?/span> ASSERT 一样ƈ且在发行版中执行 m_MyWnd.Create() ?/span>