鄙人最近要写个Hanoi塔游戏的课程设计,MFC做,因此在网上下了一个适合自己理解的汉诺塔程序源码,看着看着就不懂了,望大侠们给源码上加上注释,肯定是越详细越好。在下的MFC编程经验几乎为ZERO,函数调来调去的,完全晕了!我自己试着注释了一部分,感觉还是不对。 
此程序用MFC的对话框向导编写的框架。 
需要解释的我标的红色,我自己注释了些,望大家看下对不对。
完整代码在http://hi.csdn.net/link.php?url=http://topic.csdn.net%2Fu%2F20100108%2F23%2F31883321-52fa-4194-8e39-46935d86252a.html  由于前贴代码太乱了,所以新起一贴。望大家帮帮忙!
// towerDlg.cpp : implementation file#include "stdafx.h"
#include "tower.h"
#include "towerDlg.h"#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif/////////////////////////////////////////////////////////////////////////////
// CTowerDlg dialogCTowerDlg::CTowerDlg(CWnd* pParent /*=NULL*/)
: CDialog(CTowerDlg::IDD, pParent)
{
stepout=0;
//{{AFX_DATA_INIT(CTowerDlg)
//}}AFX_DATA_INIT
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); //m_hIcon指示着MFC_Icon
}void CTowerDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CTowerDlg)
DDX_Control(pDX, IDC_BUTTON4, btnbac);
DDX_Control(pDX, IDC_SPIN1, spin);
DDX_Control(pDX, IDC_EDIT1, edit);
DDX_Control(pDX, IDC_OUT, out);
DDX_Control(pDX, IDC_BUTTON3, btnc);
DDX_Control(pDX, IDC_BUTTON2, btnb);
DDX_Control(pDX, IDC_BUTTON1, btna);
//}}AFX_DATA_MAP
}BEGIN_MESSAGE_MAP(CTowerDlg, CDialog)
//{{AFX_MSG_MAP(CTowerDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, OnButtonA)
ON_BN_CLICKED(IDC_BUTTON2, OnButtonB)
ON_BN_CLICKED(IDC_BUTTON3, OnButtonC)
ON_BN_CLICKED(IDOK, OnNew)
ON_BN_CLICKED(IDC_BUTTON4, OnBac)
ON_WM_LBUTTONDOWN()
ON_WM_CTLCOLOR()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()/////////////////////////////////////////////////////////////////////////////
// CTowerDlg message handlersBOOL CTowerDlg::OnInitDialog()
{
CDialog::OnInitDialog();

SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon

// TODO: Add extra initialization here
SetDlgItemInt(IDC_EDIT1,3);//设置IDC_EDIT1中的String变成int
OnNew();
edit.SetLimitText(1);//设置edit的值限制为1byte
// CFont font;
// font.CreatePointFont(500,"System");
// edit.SetFont(&font,NULL);
spin.SetRange(3,7);//设置滑动条的控制范围为3-7
spin.SetPos(3);//设置滑动条的当前值为3

return TRUE;  // return TRUE  unless you set the focus to a control
}// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.
void CTowerDlg::PaintTower(CPaintDC &dc, int idc, int a[])
{//idc相当于IDC_A/B/C,a[]=一个盘中的塔数目
CRect rect;
this->GetDlgItem(idc)->GetWindowRect(rect); //GetDlgItem(idc) 指示到idc所代表的窗口或控件  
ScreenToClient(rect);
rect.top=rect.bottom-10;//定义底和高的差值是10
int fillnum=0;//已经画了的塔的个数
for(int i=floor;i>=1;i--)
{
if(a[i]==0) continue; //a[i]==0,第i层塔没有要移动的命令,从而查找下一个塔标志
CRect one=rect;
one.left-=i*10/2;
one.right+=i*10/2; //塔的左右长度变宽
rect.OffsetRect(0,-10);//塔向上移动10,向右移动0
dc.FillRect(one,&CBrush(RGB(119,119,119)));//用灰色画刷填充矩形
fillnum++;
if(fillnum==a[0]&&set==a)
dc.FillRect(one,&CBrush(RGB(0,0,0)));//用黑色画刷填充矩形
}
}

void CTowerDlg::OnPaint() 
{
CPaintDC dc(this);
PaintTower(dc,IDC_A,a);//画盘A及其中的塔
PaintTower(dc,IDC_B,b);
PaintTower(dc,IDC_C,c);

if (IsIconic()) //对话框最小化则==0,否则非0
{
CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); // Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2; // Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}HCURSOR CTowerDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}void CTowerDlg::SetFloor(int floor)//设置每层的标志,是否显示出来
{
setbefore=set=NULL;
this->floor=floor;
a[0]=floor;
b[0]=c[0]=0;
for(int i=1;i<=floor;i++)//a塔各层标志为1,表示该层要被显示出来
{
a[i]=1;
b[i]=c[i]=0;
}
}

void CTowerDlg::OnButtonA() 
{
OnButton(a);
}
void CTowerDlg::OnButtonB() 
{
OnButton(b);

}
void CTowerDlg::OnButtonC() 
{
OnButton(c);
}void CTowerDlg::OnButton(int *i)
{
set=i;
CRect top;
if(setbefore==NULL){
setbefore=set;
top=GetTopRect(set);
}
else if(setbefore==set) {
top=GetTopRect(set);
setbefore=set=NULL;
}
else{
Move(set,setbefore);//交换set和setbefore的位置
setbefore=set=NULL;
IsWin();//判断是否赢了
btnbac.EnableWindow(1);
return;
}
InvalidateRect(top);//引起top重绘
}void CTowerDlg::Move(int *set, int *setbefore)
{
setbac1=setbefore;setbac2=set;
int i=1;
while(!setbefore[i]==1) i++;
CRect befrect=GetTopRect(setbefore);
for(int j=1;j<=i;j++)
{
if(set[j]==1)
{
InvalidateRect(befrect);
return;
}
}
set[0]++;setbefore[0]--;
set[i]=1;setbefore[i]=0;
stepout++;
SetDlgItemInt(IDC_STEPNUM,stepout);
CRect rect=GetItemRect(IDC_STEPNUM);
InvalidateRect(rect);
InvalidateRect(befrect);
CRect setrect=GetTopRect(set);
InvalidateRect(setrect);}void CTowerDlg::IsWin()
{
if(c[0]!=floor) return;
btna.EnableWindow(0);
btnb.EnableWindow(0);
btnc.EnableWindow(0);
int lest=GetLestStep();
char out[200];
sprintf(out,"恭喜你,你赢了  你的步数是%d  最佳步数是%d",
stepout,lest);
this->out.SetWindowText(out);
}void CTowerDlg::OnNew() 
{
floor=GetDlgItemInt(IDC_EDIT1);
SetFloor(floor);
btna.EnableWindow(1);
btnb.EnableWindow(1);
btnc.EnableWindow(1);
out.SetWindowText("");
stepout=0;
SetDlgItemInt(IDC_STEPNUM,stepout);
btnbac.EnableWindow(0);
this->Invalidate();
}int CTowerDlg::GetLestStep()
{
int ret=0;
for(int i=0;i<floor;i++)
ret=ret*2+1;
return ret;
}
void CTowerDlg::OnBac() 
{
stepout-=(1+1);   // move中加了一
SetDlgItemInt(IDC_STEPNUM,stepout);
Move(setbac1,setbac2);

btnbac.EnableWindow(0);
}CRect CTowerDlg::GetTopRect(int *tower)
{
int idc;
if(tower==a)
idc=IDC_A;
else if(tower==b)
idc=IDC_B;
else if(tower==c)
idc=IDC_C;
CRect rect;
this->GetDlgItem(idc)->GetWindowRect(rect);   
ScreenToClient(rect);
rect.top=rect.bottom-10;
rect.OffsetRect(0,-10*(tower[0]-1));
int i=1;
while(tower[i]!=1) i++;
rect.left-=i*10/2;rect.right+=i*10/2; 
return rect;
}

void CTowerDlg::OnLButtonDown(UINT nFlags, CPoint point) 
{
// TODO: Add your message handler code here and/or call default
CRect a,b,c;
a=GetItemRect(IDC_AC);
b=GetItemRect(IDC_BC);
c=GetItemRect(IDC_CC);
if(a.PtInRect(point))
OnButtonA();
else if(b.PtInRect(point))
OnButtonB();
else if(c.PtInRect(point))
OnButtonC();
CDialog::OnLButtonDown(nFlags, point);
}CRect CTowerDlg::GetItemRect(int idc)
{
CRect rect;
this->GetDlgItem(idc)->GetWindowRect(rect);   
ScreenToClient(rect);
return rect;
}

HBRUSH CTowerDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) 
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

// TODO: Change any attributes of the DC here
if   ( nCtlColor==CTLCOLOR_STATIC)   
{   
pDC->SetBkMode(TRANSPARENT); 
return   (HBRUSH)::GetStockObject(HOLLOW_BRUSH);   
// TODO: Return a different brush if the default is not desired
return hbr;}

解决方案 »

  1.   

    算了,就是void CTowerDlg::OnPaint() 
    {
        CPaintDC dc(this);
        PaintTower(dc,IDC_A,a);//画盘A及其中的塔
        PaintTower(dc,IDC_B,b);
        PaintTower(dc,IDC_C,c);

        if (IsIconic()) //对话框最小化则==0,否则非0
        {
            CPaintDC dc(this); // device context for painting        SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);        // Center icon in client rectangle
            int cxIcon = GetSystemMetrics(SM_CXICON);
            int cyIcon = GetSystemMetrics(SM_CYICON);
            CRect rect;
            GetClientRect(&rect);
            int x = (rect.Width() - cxIcon + 1) / 2;
            int y = (rect.Height() - cyIcon + 1) / 2;        // Draw the icon
            dc.DrawIcon(x, y, m_hIcon);
        }
        else
        {
            CDialog::OnPaint();
        }
    }下面的所有,大家帮帮忙吧,我实在是没办法了。
      

  2.   

    说的没错啊,哪里不明白?OnPaint 就是响应系统发给程序的 WM_PAINT 消息.
    当程序界面被遮盖,需要更新,需要显示时就会发送WM_PAINT消息.
      

  3.   

    主要问题看不到运行后的界面,你讲的也不清楚,大致讲一下几个
    在OnPaint()函数中使用  PaintTower(dc,IDC_A,a);//画盘A及其中的塔
        PaintTower(dc,IDC_B,b);
        PaintTower(dc,IDC_C,c);
    在为了防止,已经画好的A,B,C3个塔(实际上是控件)在窗体刷新时消失。void CTowerDlg::OnLButtonDown(UINT nFlags, CPoint point) 
    这个函数就是判断当前鼠标单击后,所在的位置是否在3个塔的某一个中
    主要是通过PtInRect()函数来判断的,这里使用了GetItemRect()函数 
    这个是他自己写的通过id来获取某个控件的大小和位置并返回
    如果在某个塔上,就调用相应按钮的函数,也就相当于和按下按钮执行
    相同的操作
    HBRUSH CTowerDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) 
    这个函数主要是用来使静态控件透明显示,也就是背景和窗体相同。
    主要是通过 nCtlColor==CTLCOLOR_STATIC(代表所有的静态控件) 
    pDC->SetBkMode(TRANSPARENT); //设置透明模式
    return   (HBRUSH)::GetStockObject(HOLLOW_BRUSH); //返回空画刷,使背景与窗体融合
    使用GetStockObject(NULL_BRUSH); 效果一样
    OnButton(int *i)
    这3个函数OnButtonA,OnButtonB,OnButtonC主要处理操作的一样,所以都写在了OnButton(int *i)
    函数中,在这个函数调用到了GetTopRect(int *tower)
    这个函数用来获取某个柱子顶层塔的矩形区域,move()函数用来交换柱子之间的塔
    IsWin()判断是否赢,在这个IsWin()函数中又调用了GetLestStep()
    函数,在这个函数中判断最后所走的一步之后,通过层次做循环条件
    判断所有的塔是否到达目标位置,
    大致讲这么多吧
      

  4.   

    5楼讲到了几个关键的地方,很详细,感谢。
    能再详细讲下void CTowerDlg::OnNew() 
    {
    floor=GetDlgItemInt(IDC_EDIT1);
    SetFloor(floor);
    btna.EnableWindow(1);
    btnb.EnableWindow(1);
    btnc.EnableWindow(1);
    out.SetWindowText("");
    stepout=0;
    SetDlgItemInt(IDC_STEPNUM,stepout);
    btnbac.EnableWindow(0);
    this->Invalidate();
    }
      

  5.   


    void CTowerDlg::OnNew() 
    {
        floor=GetDlgItemInt(IDC_EDIT1); //获取IDC_EDIT1中的整型值赋给
        SetFloor(floor);  //调用SetFloor()函数
        btna.EnableWindow(1); //激活A按钮
        btnb.EnableWindow(1); //
        btnc.EnableWindow(1); //
        out.SetWindowText(""); //将Static控件IDC_OUT的值清空
        stepout=0; 
        SetDlgItemInt(IDC_STEPNUM,stepout); //设置当前所操作的次数
        btnbac.EnableWindow(0); //将IDC_BUTTON4按钮灰显,不响应任何消息
        this->Invalidate(); //刷新窗体
    }
      

  6.   

    哦,现在基本上都清楚了,最后一个问题:
    就这个程序而言,能讲下这些函数的调用过程吗,系统调用简单说说,这些自定义的函数重点说下哈!
    要是能写个像main()的调用过程就好了,可以多写点汉字,不一定要代码。
      

  7.   

    这个是MFC程序,main()函数已经封装起来了,不像一般的c++程序有main()函数
    或者win32有WinMain()函数,都在程序中明确的写出来。MFC的有个CXXXApp类继承于CWinApp类,作为应用程序的全局类,
    可以看作程序的入口点便是CXXXApp类的InitInstance()函数,主对话框
    也是在这里定义的,如下:
    CTowerDlg dlg; 
    m_pMainWnd = &dlg; 
    int nResponse = dlg.DoModal(); 在执行DoModal()函数后,显示主窗口。窗体的初始化工作是在其类中的OnInitDialog()
    函数中做的。至于响应某个函数,有些需要点击按钮,如你的几个button按钮
    一些绘图和绘制文本的操作都是在OnPaint()进行
    自定义的函数,像这里的GetItemRect(),GetTopRect()等都是,
    在ClassView下右键某个类,就可以给这个类添加这些函数了。像这里的OnButtonA等函数,是按钮的消息函数,双击按钮的时候就会
    产生相应的函数,函数名可以更改。MFC采用的是消息映射机制
    一般的就是产生消息,触发某个事件
    主要是你的看看MFC的运行机制等,最好将vc书好好看看
      

  8.   

    哦,明白了MFC框架定义好了控制和如何响应控件的事件的函数如CButton和ONButtonC(),而自己定义函数最终都是被这些响应控件的函数调用了。 就像ONLButtonDown(系统定义)调用ONButtonB(系统定义),而ONButtonB(系统定义)调用ONButton(自定义函数),而ONButton(自定义函数)又调用GetTopRect(自定义函数)一样。 哈哈,终于明白了,YES,That's very good,thank you! thank you very much!
      

  9.   

    最最后一个问题 int * setbac1,*setbac2,*set,*setbefore代表什么意思呢!
      

  10.   

    这是别人定义的指针变量,用来指向一个整型值,
    在OnButton(int *i),Move(int *set, int *setbefore) 
    等函数要用到的,作为参数传给OnButton()这些函数
    具体每一个指向什么变量,你要去看他的源码中如何设置的了
      

  11.   

    这下完全看明白了。
    int *set,*setbefore,*setbac1,*setbac2;指向数组的指针,代表指向当前塔数组,先前塔的数组,后退一步的塔的数组等。
    此贴我收获不小,也看到我的不少问题。我实在是太高兴了,哈哈!
    感谢mmilmf
    大家有没有什么要回复的,差不多就要结贴了。
    那就10点钟结贴了哦!mmilmf至少给你150。同时此贴和http://topic.csdn.net/u/20100108/23/31883321-52fa-4194-8e39-46935d86252a.html?seed=1902365805&r=62614903#r_62614903是相连的,在此贴出来,以后搜索比较方便,嘿嘿!